381 lines
16 KiB
HTML
381 lines
16 KiB
HTML
<section class="dashboard">
|
|
<header class="dashboard__hero">
|
|
<div class="hero__text">
|
|
<p class="eyebrow">ภาพรวมบัญชี</p>
|
|
<h1>ยินดีต้อนรับกลับ, {{ ownerName }}</h1>
|
|
<p class="hero__subtitle">
|
|
จดบันทึกรายรับรายจ่าย และดูสรุปต่อปี เดือน สัปดาห์ ได้ในหน้าเดียว
|
|
</p>
|
|
</div>
|
|
<div class="hero__actions">
|
|
<button class="btn btn--primary">สร้างรายงานด่วน</button>
|
|
<!-- <button class="btn btn--ghost">อัปโหลดใบเสร็จ</button> -->
|
|
</div>
|
|
</header>
|
|
|
|
<section class="dashboard__periods">
|
|
<article class="period-card" *ngFor="let summary of periodSummaries">
|
|
<header class="period-card__header">
|
|
<span class="period-card__badge" [ngClass]="'period-card__badge--' + summary.badge">
|
|
{{ summary.label }}
|
|
</span>
|
|
<p>{{ summary.note }}</p>
|
|
</header>
|
|
<div class="period-card__values">
|
|
<div>
|
|
<p class="muted">รายรับ</p>
|
|
<p class="income">{{ summary.income }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="muted">รายจ่าย</p>
|
|
<p class="expense">{{ summary.expense }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="muted">คงเหลือสุทธิ</p>
|
|
<p class="net">{{ summary.net }}</p>
|
|
</div>
|
|
</div>
|
|
<footer>
|
|
<span class="trend-chip">แนวโน้ม {{ summary.trend }}</span>
|
|
</footer>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="dashboard__stats">
|
|
<article class="stat-card" *ngFor="let card of kpiCards">
|
|
<div class="stat-card__icon" [ngClass]="'accent-' + card.accent"></div>
|
|
<div class="stat-card__body">
|
|
<p class="stat-card__label">{{ card.label }}</p>
|
|
<div class="stat-card__value">{{ card.value }}</div>
|
|
<p class="stat-card__trend">{{ card.trend }} · {{ card.context }}</p>
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="ledger-grid">
|
|
<article class="panel quick-log">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>บันทึกรายการแบบรวดเร็ว</h2>
|
|
<p>จดรายรับรายจ่ายภายในไม่กี่คลิก</p>
|
|
</div>
|
|
</div>
|
|
<form class="quick-log__form">
|
|
<label>
|
|
<span>ประเภท</span>
|
|
<div class="quick-log__toggle">
|
|
<button type="button" class="toggle-btn" [ngClass]="{ 'is-active': mode == 'i' }" (click)="mode = 'i'">รายรับ</button>
|
|
<button type="button" class="toggle-btn" [ngClass]="{ 'is-active': mode == 'e' }" (click)="mode = 'e'">รายจ่าย</button>
|
|
</div>
|
|
</label>
|
|
<label>
|
|
<span>วันที่</span>
|
|
<!-- <input type="text" disabled placeholder="10/04/2025 เวลา 12:00"/> -->
|
|
|
|
<input type="datetime-local"/>
|
|
</label>
|
|
<div class="quick-log__grid">
|
|
<label>
|
|
<span>หมวดหมู่</span>
|
|
@if(mode == 'i'){
|
|
<select class="w-full h-full px-4 py-2 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-100 focus:border-blue-50 transition-all bg-white">
|
|
<option value="">ไม่เลือก</option>
|
|
@for (item of myDropAct.income; track item.dtlcod) {
|
|
<option [value]="item.dtlcod">
|
|
{{ item.dtlnam }}
|
|
</option>
|
|
}
|
|
</select>
|
|
}@else if(mode == 'e'){
|
|
<select class="w-full h-full px-4 py-2 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-100 focus:border-blue-50 transition-all bg-white">
|
|
<option value="">ไม่เลือก</option>
|
|
@for (item of myDropAct.expense; track item.dtlcod) {
|
|
<option [value]="item.dtlcod">
|
|
{{ item.dtlnam }}
|
|
</option>
|
|
}
|
|
</select>
|
|
}
|
|
</label>
|
|
<label>
|
|
<span>ยอดเงิน (฿)</span>
|
|
<input type="number" placeholder="0.00" />
|
|
</label>
|
|
</div>
|
|
<label>
|
|
<span>บันทึกเพิ่มเติม</span>
|
|
<textarea rows="3" placeholder="รายละเอียดการรับ/จ่าย"></textarea>
|
|
</label>
|
|
<button type="button" class="btn btn--primary">บันทึกรายการ</button>
|
|
</form>
|
|
</article>
|
|
|
|
<article class="panel ledger-panel">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>สมุดบันทึกล่าสุด</h2>
|
|
<p>แยกสีระหว่างรายรับและรายจ่าย</p>
|
|
</div>
|
|
<button class="btn btn--ghost btn--compact">ดูทั้งหมด</button>
|
|
</div>
|
|
<div class="ledger-table" [class.is-scrollable]="myActData.length > 5">
|
|
<div class="ledger-row ledger-head">
|
|
<span>รายการ</span>
|
|
<span>หมวดหมู่</span>
|
|
<span>ยอดเงิน</span>
|
|
<span>บันทึก</span>
|
|
</div>
|
|
<!-- @for (idx of myActData; track i; let i = $index) {
|
|
<div class="ledger-row">
|
|
<div class="ledger-main">
|
|
<span class="pill" [ngClass]="idx.acttyp === 'i' ? 'pill--income' : 'pill--expense'">
|
|
{{ idx.type === 'i' ? 'รับ' : 'จ่าย' }}
|
|
</span>
|
|
|
|
<div>
|
|
<p class="ledger-title">{{ idx.title }}</p>
|
|
<p class="ledger-date">{{ idx.date }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<span class="ledger-category">{{ idx.category }}</span>
|
|
|
|
<span class="ledger-amount" [ngClass]="idx.type === 'i' ? 'is-credit' : 'is-debit'">
|
|
{{ idx.amount }}
|
|
</span>
|
|
|
|
<span class="ledger-note">{{ idx.note }}</span>
|
|
</div>
|
|
} -->
|
|
@for (idx of myActData; track idx.actseq; let i = $index) {
|
|
<div class="ledger-row">
|
|
|
|
<div class="ledger-main">
|
|
<span class="pill" [ngClass]="idx.acttyp === 'i' ? 'pill--income' : 'pill--expense'">
|
|
{{ idx.acttyp === 'i' ? 'รับ' : 'จ่าย' }}
|
|
</span>
|
|
|
|
<div>
|
|
<p class="ledger-title">{{ idx.acttypnam }}</p>
|
|
<p class="ledger-date">{{ idx.actacpdtm ?? '' | dtmtodatetime}}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<span class="ledger-category">{{ idx.actcatnam }}</span>
|
|
|
|
<span class="ledger-amount" [ngClass]="idx.acttyp === 'i' ? 'is-credit' : 'is-debit'">
|
|
{{ idx.actqty }}
|
|
</span>
|
|
|
|
<span class="ledger-note">{{ idx.actcmt }}</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="dashboard__grid">
|
|
<!-- <article class="panel panel--main">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>แนวโน้มรายรับ</h2>
|
|
<p>สรุป 6 เดือนล่าสุด</p>
|
|
</div>
|
|
<button class="btn btn--ghost btn--compact">ดาวน์โหลดข้อมูล</button>
|
|
</div>
|
|
<div class="trend-chart">
|
|
<div class="trend-chart__bar" *ngFor="let point of revenueTrend">
|
|
<span class="trend-chart__value" [style.height.%]="point.value"></span>
|
|
<span class="trend-chart__label">{{ point.label }}</span>
|
|
</div>
|
|
</div>
|
|
</article> -->
|
|
|
|
<article class="panel panel--main pie-panel">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>สัดส่วนค่าใช้จ่าย</h2>
|
|
<p>ดูหมวดไหนใช้เงินมากที่สุด</p>
|
|
</div>
|
|
<button class="btn btn--ghost btn--compact">จัดการหมวดหมู่</button>
|
|
</div>
|
|
<div class="pie-panel__content">
|
|
<div class="pie-chart" [style.background]="ActSumDataGradient">
|
|
<div class="pie-chart__center">
|
|
<p>รวมเดือนนี้</p>
|
|
<strong>{{myActSumData.summary.totalExpense}}</strong>
|
|
</div>
|
|
</div>
|
|
<ul class="pie-legend">
|
|
<li class="pie-legend__item" *ngFor="let idx of myActSumData.pie.expense">
|
|
<span class="swatch" [style.background]="idx.color"></span>
|
|
<div>
|
|
<p class="pie-legend__label">{{ idx.label }}</p>
|
|
<p class="pie-legend__value">{{ idx.percent }}%</p>
|
|
<p class="pie-legend__value">{{ idx.value }} บาท</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</article>
|
|
<!-- ตัวเลขซ้อนทับกัน -->
|
|
<article class="panel panel--side">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>สรุปสภาพคล่อง</h2>
|
|
<p>อัปเดตล่าสุด 5 นาทีที่แล้ว</p>
|
|
</div>
|
|
</div>
|
|
<div class="ratio-list">
|
|
<div class="ratio" *ngFor="let ratio of quickRatios" [ngClass]="'ratio--' + ratio.status">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;gap:0.5rem;">
|
|
<p style="margin:0;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
|
{{ ratio.label }}
|
|
</p>
|
|
<span style="margin-left:0.5rem;flex:0 0 auto">{{ ratio.value }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
<article class="panel alerts-panel">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>การแจ้งเตือนสำคัญ</h2>
|
|
<p>จัดลำดับงานค้างก่อนครบกำหนด</p>
|
|
</div>
|
|
</div>
|
|
<div class="alert" *ngFor="let alert of alerts">
|
|
<div>
|
|
<p class="alert__title">{{ alert.title }}</p>
|
|
<p class="alert__detail">{{ alert.detail }}</p>
|
|
</div>
|
|
<span class="alert__tag">{{ alert.tag }}</span>
|
|
</div>
|
|
</article>
|
|
|
|
<article class="panel tasks-panel">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>รายการยอดค้างจ่าย</h2>
|
|
<p>ช่วยเตือนความจำให้</p>
|
|
</div>
|
|
<button class="btn btn--primary btn--compact" (click)="isModalOpen = true">เพิ่มงาน</button>
|
|
</div>
|
|
<ul class="task-list">
|
|
<li class="task" *ngFor="let task of tasks">
|
|
<div>
|
|
<p class="task__title">{{ task.title }}</p>
|
|
<p class="task__due">{{ task.due }}</p>
|
|
</div>
|
|
<span class="task__badge">{{ task.priority }}</span>
|
|
</li>
|
|
</ul>
|
|
</article>
|
|
|
|
</section>
|
|
</section>
|
|
|
|
|
|
@if(isModalOpen == true){
|
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4 backdrop-blur-sm transition-all duration-300 ease-in-out" role="dialog" aria-modal="true" [formGroup]="arrearsForm">
|
|
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg mx-auto overflow-hidden transform scale-100 transition-all duration-300 ease-out">
|
|
|
|
<!-- Header -->
|
|
<header class="flex items-center justify-between gap-4 px-6 py-5 border-b bg-linear-to-r from-rose-50 to-white">
|
|
<div class="flex items-center gap-3">
|
|
<svg class="w-6 h-6 text-rose-600" viewBox="0 0 24 24" fill="none" aria-hidden>
|
|
<path d="M12 2v6M6 12h12M4 20h16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<div>
|
|
<h2 class="text-lg font-semibold text-gray-900 m-0">เพิ่มยอดค้างชำระ</h2>
|
|
<p class="text-sm text-gray-500 m-0">บันทึกยอดที่ยังค้างชำระเพื่อการติดตาม</p>
|
|
</div>
|
|
</div>
|
|
<button type="button" (click)="isModalOpen = false" class="text-gray-400 hover:text-rose-600 p-2 rounded-md transition-colors duration-200" aria-label="ปิด">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none">
|
|
<path d="M6 6l12 12M6 18L18 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
</button>
|
|
</header>
|
|
|
|
<!-- Form -->
|
|
<form class="px-6 py-6 bg-white" (ngSubmit)="onArrearsSubmit()" autocomplete="off" novalidate>
|
|
<div class="grid grid-cols-1 gap-2">
|
|
|
|
<!-- จำนวนเงิน -->
|
|
<label class="block">
|
|
<span class="text-sm font-medium text-gray-700">จำนวนเงิน (฿)</span>
|
|
<div class="mt-1 relative">
|
|
<input
|
|
type="text"
|
|
inputmode="decimal"
|
|
id="amount"
|
|
formControlName="amount"
|
|
placeholder="0.00"
|
|
class="w-full px-4 py-2 border rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-rose-400 focus:border-rose-500 transition-all"
|
|
/>
|
|
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-sm text-gray-500">THB</span>
|
|
</div>
|
|
@if(arrearsForm.get('amount')?.touched && arrearsForm.get('amount')?.invalid) {
|
|
<p class="mt-1 text-xs text-red-600">
|
|
กรุณากรอกจำนวนเงินที่ถูกต้อง
|
|
</p>
|
|
}
|
|
</label>
|
|
|
|
<label class="block">
|
|
<span class="text-sm font-medium text-gray-700">วันครบกำหนกจ่าย</span>
|
|
<div class="mt-1 relative">
|
|
<input type="datetime-local" formControlName="expdtm" class=" w-full px-4 py-2 border rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-rose-400 focus:border-rose-500 transition-all"/>
|
|
</div>
|
|
@if(arrearsForm.get('expdtm')?.touched && arrearsForm.get('expdtm')?.invalid) {
|
|
<p class="mt-1 text-xs text-red-600">
|
|
กรุณาระบุวันครบกำหนดชำระ
|
|
</p>
|
|
}
|
|
</label>
|
|
|
|
<!-- เหตุผล -->
|
|
<label class="block">
|
|
<span class="text-sm font-medium text-gray-700">เหตุผล</span>
|
|
<input
|
|
type="text"
|
|
id="reason"
|
|
formControlName="reason"
|
|
placeholder="เช่น บิลค้างชำระจากผู้ขาย"
|
|
class="mt-1 w-full px-4 py-2 border rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-rose-400 focus:border-rose-500 transition-all"
|
|
/>
|
|
@if(arrearsForm.get('reason')?.touched && arrearsForm.get('reason')?.invalid) {
|
|
<p class="mt-1 text-xs text-red-600">
|
|
กรุณากรอกเหตุผล
|
|
</p>
|
|
}
|
|
</label>
|
|
|
|
<!-- บันทึกเพิ่มเติม -->
|
|
<label class="block">
|
|
<span class="text-sm font-medium text-gray-700">บันทึกเพิ่มเติม (ไม่บังคับ)</span>
|
|
<textarea
|
|
rows="3"
|
|
formControlName="note"
|
|
placeholder="รายละเอียดเพิ่มเติม (เช่น เลขใบแจ้งหนี้ หรือ ผู้ติดต่อ)"
|
|
class="mt-1 w-full px-4 py-2 border rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-rose-400 focus:border-rose-500 resize-none transition-all"
|
|
></textarea>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer class="flex items-center justify-end gap-3 pt-4 border-t mt-4">
|
|
<button type="button" (click)="isModalOpen = false" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-xl hover:bg-gray-300 transition-colors duration-200">
|
|
ยกเลิก
|
|
</button>
|
|
<button type="submit" class="rounded-2xl">
|
|
บันทึก
|
|
</button>
|
|
</footer>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
}
|