-จัดการ flow ของ program ใหม่
All checks were successful
Build Docker Image / Preparing Dependecies (push) Successful in 5s
12
ng-ttc-frontend/package-lock.json
generated
@@ -25,15 +25,14 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^7.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||
"@tailwindcss/postcss": "^4.1.16",
|
||||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"bootstrap": "^5.3.8",
|
||||
"chart.js": "^4.5.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"ng2-charts": "^6.0.1",
|
||||
"postcss": "^8.5.6",
|
||||
"rxjs": "~7.8.0",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
@@ -54,7 +53,7 @@
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"ngx-toastr": "^19.1.0",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss": "^8.5.6",
|
||||
"typescript": "~5.9.3"
|
||||
}
|
||||
},
|
||||
@@ -8275,7 +8274,6 @@
|
||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -12051,7 +12049,6 @@
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -13816,7 +13813,6 @@
|
||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
@@ -19904,7 +19900,6 @@
|
||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.14.0",
|
||||
@@ -20911,6 +20906,7 @@
|
||||
"integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.13",
|
||||
"@types/connect-history-api-fallback": "^1.5.4",
|
||||
|
||||
@@ -57,15 +57,14 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^7.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||
"@tailwindcss/postcss": "^4.1.16",
|
||||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"bootstrap": "^5.3.8",
|
||||
"chart.js": "^4.5.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"ng2-charts": "^6.0.1",
|
||||
"postcss": "^8.5.6",
|
||||
"rxjs": "~7.8.0",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
@@ -86,7 +85,7 @@
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"ngx-toastr": "^19.1.0",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss": "^8.5.6",
|
||||
"typescript": "~5.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
ng-ttc-frontend/public/ballot_0.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
ng-ttc-frontend/public/bell_0.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
ng-ttc-frontend/public/calendar_0.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
ng-ttc-frontend/public/chart-histogram_0.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
ng-ttc-frontend/public/chart-simple.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
ng-ttc-frontend/public/check_0.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
ng-ttc-frontend/public/coins_0.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
ng-ttc-frontend/public/exclamation_0.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
ng-ttc-frontend/public/exit_0.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
ng-ttc-frontend/public/form_0.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ng-ttc-frontend/public/home (1)_0.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
ng-ttc-frontend/public/home_0.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
ng-ttc-frontend/public/icons8-home-384.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ng-ttc-frontend/public/interrogation.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
ng-ttc-frontend/public/interrogation_0.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
ng-ttc-frontend/public/locked-computer_0.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
ng-ttc-frontend/public/menu-burger_0.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
ng-ttc-frontend/public/pencil_0.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
ng-ttc-frontend/public/plus-small_0.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
ng-ttc-frontend/public/search_0.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
ng-ttc-frontend/public/settings (1)_0.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
ng-ttc-frontend/public/stamp_0.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
ng-ttc-frontend/public/user_0.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { SidebarContentComponent } from './content/sidebar-content/sidebar-content.component';
|
||||
import { LicensePrivacyTermsComponent } from './component/license-privacy-terms/license-privacy-terms.component';
|
||||
// import { authGuard } from './services/auth.guard';
|
||||
import { authGuard } from './services/auth.guard';
|
||||
|
||||
const routes: Routes = [
|
||||
|
||||
@@ -14,7 +14,7 @@ const routes: Routes = [
|
||||
path: 'main',
|
||||
component: SidebarContentComponent,
|
||||
canActivate: [
|
||||
// authGuard
|
||||
authGuard
|
||||
],
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ import { LicensePrivacyTermsComponent } from './component/license-privacy-terms/
|
||||
|
||||
|
||||
import { provideCharts, withDefaultRegisterables } from 'ng2-charts';
|
||||
// import { BudgetAproval } from './component/budget-aproval/budget-aproval';
|
||||
// import { AccDateFormatPipe } from './pipe/dtmtodatetime.pipe';
|
||||
// import { DtmtodatetimePipe } from './dtmtodatetime.pipe';
|
||||
|
||||
@@ -33,6 +34,7 @@ import { provideCharts, withDefaultRegisterables } from 'ng2-charts';
|
||||
SidebarContentComponent,
|
||||
SidebarComponent,
|
||||
LicensePrivacyTermsComponent,
|
||||
// BudgetAproval,
|
||||
// AccDateFormatPipe
|
||||
// DtmtodatetimePipe,
|
||||
// MainDashboardContentComponent,
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
<div class="p-6">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-semibold">
|
||||
รายการงบประมาณของโครงการ: {{ project?.name }}
|
||||
</h2>
|
||||
|
||||
<div class="text-gray-600 text-sm">
|
||||
แสดงข้อมูล {{ budgetItems.length }} รายการ
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Add New Budget Item -->
|
||||
<form [formGroup]="addItemForm" class="bg-gray-50 border rounded-xl p-4 mb-6">
|
||||
|
||||
<h3 class="font-semibold mb-3 text-gray-700">เพิ่มรายการงบประมาณ</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
|
||||
<!-- หมวดงบ -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-600">หมวดงบประมาณ</label>
|
||||
<select
|
||||
formControlName="category"
|
||||
class="w-full px-4 py-2 border rounded-xl bg-white mt-1
|
||||
focus:ring-2 focus:ring-blue-200 focus:border-blue-300"
|
||||
>
|
||||
<option value="">-- เลือกหมวดงบ --</option>
|
||||
|
||||
@for (item of budgetCategoriesDrop.expense; track item.dtlcod) {
|
||||
<option [value]="item.dtlcod">{{ item.dtlnam }}</option>
|
||||
}
|
||||
</select>
|
||||
|
||||
<!-- error -->
|
||||
<div
|
||||
*ngIf="addItemForm.controls['category'].invalid && addItemForm.controls['category'].touched"
|
||||
class="text-red-500 text-xs mt-1"
|
||||
>
|
||||
กรุณาเลือกหมวดงบ
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- รายการ -->
|
||||
<div>
|
||||
<!-- <label class="text-sm text-gray-600">ชื่อรายการ</label>
|
||||
<select
|
||||
formControlName="name"
|
||||
class="w-full px-4 py-2 border rounded-xl bg-white mt-1
|
||||
focus:ring-2 focus:ring-blue-200 focus:border-blue-300"
|
||||
>
|
||||
<option value="">-- เลือกรายการ --</option>
|
||||
|
||||
<option *ngFor="let it of masterItems" [value]="it.name">
|
||||
{{ it.name }}
|
||||
</option>
|
||||
</select> -->
|
||||
|
||||
<div
|
||||
*ngIf="addItemForm.controls['name'].invalid && addItemForm.controls['name'].touched"
|
||||
class="text-red-500 text-xs mt-1"
|
||||
>
|
||||
กรุณาเลือกรายการ
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- จำนวน -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-600">จำนวน</label>
|
||||
<input
|
||||
type="number"
|
||||
formControlName="qty"
|
||||
class="w-full px-4 py-2 border rounded-xl bg-white mt-1
|
||||
focus:ring-2 focus:ring-blue-200 focus:border-blue-300"
|
||||
/>
|
||||
|
||||
<div
|
||||
*ngIf="addItemForm.controls['qty'].invalid && addItemForm.controls['qty'].touched"
|
||||
class="text-red-500 text-xs mt-1"
|
||||
>
|
||||
จำนวนต้องมากกว่า 0
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ราคา -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-600">ราคา</label>
|
||||
<input
|
||||
type="number"
|
||||
formControlName="price"
|
||||
class="w-full px-4 py-2 border rounded-xl bg-white mt-1
|
||||
focus:ring-2 focus:ring-blue-200 focus:border-blue-300"
|
||||
/>
|
||||
|
||||
<div
|
||||
*ngIf="addItemForm.controls['price'].invalid && addItemForm.controls['price'].touched"
|
||||
class="text-red-500 text-xs mt-1"
|
||||
>
|
||||
ราคาต้องมากกว่า 0
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Add button -->
|
||||
<button
|
||||
type="button"
|
||||
(click)="addBudgetItem()"
|
||||
class="mt-4 bg-green-600 hover:bg-green-700 text-white px-5 py-2 rounded-xl shadow"
|
||||
>
|
||||
➕ เพิ่มเข้าตาราง
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- Table -->
|
||||
<div class="overflow-x-auto bg-white shadow rounded-xl border">
|
||||
<table class="min-w-full border-collapse text-sm">
|
||||
|
||||
<thead class="bg-gray-100 border-b">
|
||||
<tr class="text-gray-700">
|
||||
<th class="py-3 px-4">ลำดับ</th>
|
||||
<th class="py-3 px-4">สถานะ</th>
|
||||
<th class="py-3 px-4">รหัส</th>
|
||||
<th class="py-3 px-4">ชื่อรายการ</th>
|
||||
<th class="py-3 px-4 text-center">จำนวน</th>
|
||||
<th class="py-3 px-4 text-right">ราคา/หน่วย</th>
|
||||
<th class="py-3 px-4 text-right">ยอดชำระ</th>
|
||||
<th class="py-3 px-4 text-center">ดำเนินการ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<tr
|
||||
*ngFor="let item of budgetItems; let i = index"
|
||||
class="border-b hover:bg-gray-50 transition"
|
||||
>
|
||||
<td class="py-3 px-4">{{ i + 1 }}</td>
|
||||
|
||||
<td class="py-3 px-4 text-center">
|
||||
<span class="text-green-500 text-xl">●</span>
|
||||
</td>
|
||||
|
||||
<td class="py-3 px-4 font-medium">{{ item.code }}</td>
|
||||
|
||||
<td class="py-3 px-4 font-semibold">{{ item.name }}</td>
|
||||
|
||||
<td class="py-3 px-4 text-center">
|
||||
<input
|
||||
type="number"
|
||||
class="w-20 px-3 py-2 border rounded-lg text-center"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td class="py-3 px-4 text-right">
|
||||
{{ item.price | number:'1.0-2' }}
|
||||
</td>
|
||||
|
||||
<td class="py-3 px-4 text-right font-semibold">
|
||||
{{ item.qty * item.price | number:'1.0-2' }}
|
||||
</td>
|
||||
|
||||
<td class="py-3 px-4 text-center space-x-2">
|
||||
<button
|
||||
class="bg-red-500 hover:bg-red-600 text-white px-3 py-2 rounded-lg shadow"
|
||||
>
|
||||
🗑
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="bg-gray-600 hover:bg-gray-700 text-white px-3 py-2 rounded-lg shadow"
|
||||
>
|
||||
👁
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Summary -->
|
||||
<div class="text-right mt-4 text-lg font-semibold">
|
||||
ยอดรวมทั้งหมด:
|
||||
<span class="text-blue-600">
|
||||
{{ getTotalAmount() | number:'1.0-2' }} บาท
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,109 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
||||
import { GeneralService } from '../../services/generalservice';
|
||||
import { IDropAct, IStateDrop, IStateResultResponse, IActData, IActSumData } from '../../interfaces/dashboard.interface'
|
||||
import { DashboardStateService } from '../../services/state/dashboard-state.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-budget-aproval',
|
||||
standalone: false,
|
||||
templateUrl: './budget-aproval.html',
|
||||
styleUrl: './budget-aproval.css',
|
||||
})
|
||||
export class BudgetAproval {
|
||||
projectCode: any;
|
||||
project: any;
|
||||
addItemForm!: FormGroup;
|
||||
|
||||
budgetItems = [
|
||||
{ code: 'ITEM001', name: 'เอกซ์เรย์', qty: 1, price: 1000 },
|
||||
{ code: 'ITEM002', name: 'กรอกคำสั่งซื้อ', qty: 1, price: 1500 },
|
||||
{ code: 'ITEM003', name: 'ตรวจพื้นฐาน', qty: 1, price: 1000 },
|
||||
];
|
||||
|
||||
budgetCategoriesDrop = {
|
||||
expense: [
|
||||
{ dtlcod: 'BDG001', dtlnam: 'เงินรายได้' },
|
||||
{ dtlcod: 'BDG002', dtlnam: 'งบดำเนินงาน ปวส.' },
|
||||
{ dtlcod: 'BDG003', dtlnam: 'โครงการส่งเสริมพัฒนาทักษะวิชาชีพทักษะพื้นฐาน' },
|
||||
{ dtlcod: 'BDG004', dtlnam: 'ค่ากิจกรรมพัฒนาคุณภาพผู้เรียน' },
|
||||
{ dtlcod: 'BDG005', dtlnam: 'อุดหนุนส่งเสริมและพัฒนาผู้เรียนองค์การวิชาชีพแห่งประเทศไทย (อวท.)' },
|
||||
{ dtlcod: 'BDG006', dtlnam: 'งบดำเนินงาน ระยะสั้น' },
|
||||
{ dtlcod: 'BDG007', dtlnam: 'โครงการบูรณาการการพัฒนาทักษะทางวิชาชีพกับการเสริมสร้างคุณลักษณะอันพึงประสงค์ (FIX IT)' },
|
||||
{ dtlcod: 'BDG008', dtlnam: 'โครงการพัฒนาทักษะและสมรรถนะวิชาชีพกำลังคน (Up-skill, Re-skill)' },
|
||||
{ dtlcod: 'BDG009', dtlnam: 'งบดำเนินงาน ปวช.' },
|
||||
{ dtlcod: 'BDG010', dtlnam: 'โครงการขยายและยกระดับการจัดอาชีวศึกษาระบบทวิภาคีคุณภาพสูง' },
|
||||
{ dtlcod: 'BDG011', dtlnam: 'ปวช.(สอจ)' },
|
||||
{ dtlcod: 'BDG012', dtlnam: 'ค่าจัดการเรียนการสอน' },
|
||||
{ dtlcod: 'BDG013', dtlnam: 'งบดำเนินงาน 170000' },
|
||||
{ dtlcod: 'BDG014', dtlnam: 'โครงการพัฒนาทักษะและศักยภาพภาพการจัดการเรียนการสอนอาชีวศึกษา' },
|
||||
{ dtlcod: 'BDG015', dtlnam: 'ศึกษาธิการ' },
|
||||
{ dtlcod: 'BDG016', dtlnam: 'Up-skill' },
|
||||
{ dtlcod: 'BDG017', dtlnam: 'ติดตามผู้สำเร็จ' },
|
||||
{ dtlcod: 'BDG018', dtlnam: 'ทวิภาคี' },
|
||||
{ dtlcod: 'BDG019', dtlnam: 'พันธุกรรม' },
|
||||
{ dtlcod: 'BDG020', dtlnam: 'ปวส(สาธารณูประโภค)' },
|
||||
{ dtlcod: 'BDG021', dtlnam: 'ปวส(ค่าสาธารณูปโภค)' },
|
||||
{ dtlcod: 'BDG022', dtlnam: 'งบดำเนินงาน (ค่าสาธารณูปโภค)' },
|
||||
{ dtlcod: 'BDG023', dtlnam: 'ค่าหนังสือเรียน' },
|
||||
{ dtlcod: 'BDG024', dtlnam: 'โครงการอาชีวะต้านยาเสพติด' },
|
||||
{ dtlcod: 'BDG025', dtlnam: 'ค่าตอบแทนพนักงานราชการ' },
|
||||
{ dtlcod: 'BDG026', dtlnam: 'ค่าอุปกรณ์การเรียน' },
|
||||
{ dtlcod: 'BDG027', dtlnam: 'โครงการยกระดับและพัฒนาขีดความสามารถด้านภาษาและทักษะดิจิทัลเพื่อพัฒนากำลังคนให้มีสมรรถนะสูง' },
|
||||
{ dtlcod: 'BDG028', dtlnam: 'โครงการอนุรักษ์พันธุกรรมพืชอันเนื่องมาจากพระราชดำริ' },
|
||||
{ dtlcod: 'BDG029', dtlnam: 'ปวช.(สอจ.)' },
|
||||
{ dtlcod: 'BDG030', dtlnam: 'โครงการพัฒนาศักยภาพผู้เรียนอาชีวศึกษาในการเป็นผู้ประกอบการ (บ่มเพาะ)' },
|
||||
{ dtlcod: 'BDG031', dtlnam: 'โครงการพัฒนาและยกระดับการติดตามผู้สำเร็จการศึกษาอาชีวศึกษา' },
|
||||
{ dtlcod: 'BDG032', dtlnam: 'โครงการเสริมสร้างคุณธรรม จริยธรรมและธรรมาภิบาลในสถานศึกษา' },
|
||||
{ dtlcod: 'BDG033', dtlnam: 'โครงการจัดการอาชีวศึกษาเพื่อสนองพระราชดำริ' },
|
||||
{ dtlcod: 'BDG034', dtlnam: 'เงินรายได้ ป.ตรี' },
|
||||
{ dtlcod: 'BDG035', dtlnam: 'งบดำเนินงาน 235200' }
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setupForm();
|
||||
}
|
||||
|
||||
setupForm() {
|
||||
this.addItemForm = new FormGroup({
|
||||
category: new FormControl('', [Validators.required]),
|
||||
name: new FormControl('', [Validators.required]),
|
||||
qty: new FormControl(1, [Validators.required, Validators.min(1)]),
|
||||
price: new FormControl(0, [Validators.required, Validators.min(1)])
|
||||
});
|
||||
}
|
||||
|
||||
addBudgetItem() {
|
||||
if (this.addItemForm.invalid) {
|
||||
this.addItemForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
const formValue = this.addItemForm.value;
|
||||
|
||||
this.budgetItems.push({
|
||||
code: 'NEW' + (this.budgetItems.length + 1).toString().padStart(3, '0'),
|
||||
name: formValue.name,
|
||||
qty: formValue.qty,
|
||||
price: formValue.price,
|
||||
// category: formValue.category
|
||||
});
|
||||
|
||||
// reset form
|
||||
this.addItemForm.reset({
|
||||
category: '',
|
||||
name: '',
|
||||
qty: 1,
|
||||
price: 0
|
||||
});
|
||||
}
|
||||
|
||||
getTotalAmount() {
|
||||
return this.budgetItems.reduce((sum, item) => sum + item.qty * item.price, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -643,4 +643,4 @@
|
||||
max-height: 25rem;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<div class="min-h-screen bg-gray-100 p-4">
|
||||
<header class="flex justify-between items-center py-4 px-2">
|
||||
<h1 class="text-xl font-semibold text-gray-800">หน้าหลัก ( landing )</h1>
|
||||
<div class="flex space-x-4 text-2xl text-gray-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c0-.542.448-.99 1.042-.99.594 0 1.042.448 1.042.99 0 1.62-.99 2.534-1.99 3.438-1.042 1.042-2.042 2.042-2.042 3.042h4" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 21a9 9 0 110-18 9 9 0 010 18z" />
|
||||
</svg> -->
|
||||
<img src="interrogation.png" alt="" class="h-7 w-7 opacity-70">
|
||||
<div class="h-7 w-7 rounded-full bg-gray-300">
|
||||
<img src="user_0.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<main class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-6">
|
||||
<div class="md:col-span-2">
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg">
|
||||
<h2 class="text-lg font-medium text-gray-700 mb-4">เมนู</h2>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m4 2h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
<span class="text-gray-700">การเบิกงวดงบ</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
|
||||
</svg>
|
||||
<span class="text-gray-700">สัญญา / ข้อตกลง</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer">
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.102a9 9 0 11-12.728 0m12.728 0a9 9 0 00-12.728 0M3 12a9 9 0 1118 0" />
|
||||
</svg> -->
|
||||
<img src="stamp_0.png" alt="" class="h-6 w-6 text-gray-500 opacity-50">
|
||||
<span class="text-gray-700">การอนุมัติโครงการ</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<span class="text-gray-700">เอกสารฟอร์มวิทยาลัย</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer">
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.992 9.992 0 0112 21c-3.142 0-6.186-1.042-8.625-2.999M21 12a9.992 9.992 0 00-3-6.945M3 12a9.992 9.992 0 013-6.945M12 15a3 3 0 100-6 3 3 0 000 6z" />
|
||||
</svg> -->
|
||||
<img src="chart-simple.png" alt="" class="h-6 w-6 text-gray-500 opacity-50">
|
||||
<span class="text-gray-700">จัดสรรงบประมาณ</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
|
||||
</svg>
|
||||
<span class="text-gray-700">วิเคราะห์ & รายงาน</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg">
|
||||
<h2 class="text-lg font-medium text-gray-700 mb-4">กล่องแจ้งเตือน</h2>
|
||||
<div class="space-y-3">
|
||||
<div class="h-4 bg-gray-200 rounded w-11/12"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-10/12"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-9/12"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<section class="mt-6">
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg flex items-center space-x-6">
|
||||
<div class="shrink-0">
|
||||
<div class="h-24 w-24 rounded-full bg-white flex items-center justify-center overflow-hidden">
|
||||
<img src="logo.png" alt="">
|
||||
<!-- <span class="text-center text-white font-bold text-sm leading-tight">วิทยาลัยเทคนิคตรัง</span> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-lg font-medium text-gray-700 mb-3">อัพเดตซอฟต์แวร์</h2>
|
||||
<div class="space-y-2">
|
||||
<div class="h-4 bg-red-100 rounded w-64"></div>
|
||||
<div class="h-4 bg-red-100 rounded w-56"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-landing',
|
||||
standalone: false,
|
||||
templateUrl: './main-landing.component.html',
|
||||
styleUrls: ['./main-landing.component.css']
|
||||
})
|
||||
export class MainLandingComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
:host {
|
||||
display: block;
|
||||
padding: 2rem clamp(1.25rem, 4vw, 3rem) 3rem;
|
||||
background: radial-gradient(120% 120% at 0% 0%, #f6f8ff 0%, #eef5ff 55%, #ffffff 100%);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dashboard__hero {
|
||||
background: #0f172a;
|
||||
color: #f8fafc;
|
||||
padding: 2rem;
|
||||
border-radius: 28px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.5rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.3);
|
||||
}
|
||||
|
||||
.hero__text h1 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: clamp(1.8rem, 3vw, 2.5rem);
|
||||
}
|
||||
|
||||
.hero__subtitle {
|
||||
margin: 0;
|
||||
color: rgba(248, 250, 252, 0.7);
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
font-size: 0.8rem;
|
||||
color: rgba(248, 250, 252, 0.8);
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.hero__actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
padding: 0.65rem 1.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.btn--primary {
|
||||
background: linear-gradient(135deg, #22d3ee, #0ea5e9);
|
||||
color: #0f172a;
|
||||
box-shadow: 0 15px 30px rgba(14, 165, 233, 0.35);
|
||||
}
|
||||
|
||||
.btn--ghost {
|
||||
background: rgba(248, 250, 252, 0.12);
|
||||
color: #f8fafc;
|
||||
border: 1px solid rgba(248, 250, 252, 0.2);
|
||||
}
|
||||
|
||||
.btn--compact {
|
||||
padding: 0.45rem 1.15rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.btn:focus-visible {
|
||||
outline: 3px solid rgba(14, 165, 233, 0.4);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.dashboard__stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.dashboard__periods {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.period-card {
|
||||
background: rgba(15, 23, 42, 0.85);
|
||||
color: #f8fafc;
|
||||
border-radius: 22px;
|
||||
padding: 1.25rem 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
box-shadow: 0 18px 35px rgba(15, 23, 42, 0.4);
|
||||
}
|
||||
|
||||
.period-card__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.9rem;
|
||||
color: rgba(248, 250, 252, 0.75);
|
||||
}
|
||||
|
||||
.period-card__badge {
|
||||
padding: 0.2rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.period-card__badge--year { background: rgba(248, 250, 252, 0.14); }
|
||||
.period-card__badge--month { background: rgba(125, 211, 252, 0.25); }
|
||||
.period-card__badge--week { background: rgba(110, 231, 183, 0.2); }
|
||||
|
||||
.period-card__values {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.muted {
|
||||
margin: 0;
|
||||
font-size: 0.8rem;
|
||||
color: rgba(248, 250, 252, 0.65);
|
||||
}
|
||||
|
||||
.income,
|
||||
.expense,
|
||||
.net {
|
||||
margin: 0.1rem 0 0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.income { color: #34d399; }
|
||||
.expense { color: #fbbf24; }
|
||||
.net { color: #38bdf8; }
|
||||
|
||||
.trend-chip {
|
||||
background: rgba(248, 250, 252, 0.12);
|
||||
padding: 0.35rem 0.9rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: #ffffff;
|
||||
border-radius: 20px;
|
||||
padding: 1.25rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
box-shadow: 0 8px 30px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.stat-card__icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 16px;
|
||||
flex-shrink: 0;
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
.accent-mint { background: linear-gradient(135deg, #a7f3d0, #34d399); }
|
||||
.accent-lavender { background: linear-gradient(135deg, #ddd6fe, #a78bfa); }
|
||||
.accent-amber { background: linear-gradient(135deg, #fde68a, #fbbf24); }
|
||||
.accent-teal { background: linear-gradient(135deg, #99f6e4, #14b8a6); }
|
||||
|
||||
.stat-card__label {
|
||||
margin: 0;
|
||||
color: #475569;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.stat-card__value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.stat-card__trend {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.dashboard__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: #ffffff;
|
||||
border-radius: 24px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 12px 35px rgba(15, 23, 42, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
}
|
||||
|
||||
.panel--main {
|
||||
grid-column: span 2;
|
||||
min-height: 280px;
|
||||
}
|
||||
|
||||
.panel--side {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.panel--main {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
|
||||
.panel__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.panel__header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.panel__header p {
|
||||
margin: 0;
|
||||
color: #94a3b8;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.ledger-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.quick-log__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.9rem;
|
||||
}
|
||||
|
||||
.quick-log__form label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
font-size: 0.9rem;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.quick-log__form input,
|
||||
.quick-log__form textarea {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 14px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-family: inherit;
|
||||
font-size: 0.95rem;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.quick-log__form input:focus,
|
||||
.quick-log__form textarea:focus {
|
||||
border-color: #0ea5e9;
|
||||
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.15);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.quick-log__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.quick-log__toggle {
|
||||
display: inline-flex;
|
||||
gap: 0.4rem;
|
||||
background: #f1f5f9;
|
||||
border-radius: 999px;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 999px;
|
||||
padding: 0.4rem 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-btn.is-active {
|
||||
background: #0ea5e9;
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.ledger-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.pie-panel__content {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 1fr) 1fr;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pie-chart {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
box-shadow: inset 0 0 20px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.pie-chart__center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.1);
|
||||
}
|
||||
|
||||
.pie-chart__center p {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.pie-chart__center strong {
|
||||
font-size: 1.2rem;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.pie-legend {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.pie-legend__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.4rem 0;
|
||||
}
|
||||
|
||||
.swatch {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pie-legend__label {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.pie-legend__value {
|
||||
margin: 0;
|
||||
color: #94a3b8;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.ledger-row {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 0.8fr 1.2fr;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0.4rem;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.ledger-head {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.ledger-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
padding: 0.2rem 0.7rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.pill--income {
|
||||
background: rgba(16, 185, 129, 0.12);
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.pill--expense {
|
||||
background: rgba(248, 113, 113, 0.15);
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.ledger-title {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ledger-date {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.ledger-category {
|
||||
font-weight: 500;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.ledger-amount {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ledger-note {
|
||||
color: #64748b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.trend-chart {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: flex-end;
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.trend-chart__bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.trend-chart__value {
|
||||
width: 100%;
|
||||
border-radius: 16px 16px 6px 6px;
|
||||
background: linear-gradient(180deg, rgba(14, 165, 233, 0.8) 0%, rgba(56, 189, 248, 0.4) 100%);
|
||||
}
|
||||
|
||||
.trend-chart__label {
|
||||
font-size: 0.85rem;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.ratio-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.ratio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 18px;
|
||||
padding: 0.85rem 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ratio--positive {
|
||||
background: rgba(16, 185, 129, 0.12);
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
.ratio--neutral {
|
||||
background: rgba(59, 130, 246, 0.12);
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.ratio--warning {
|
||||
background: rgba(251, 191, 36, 0.15);
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.alerts-panel .alert {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #f8fafc;
|
||||
border-radius: 18px;
|
||||
padding: 1rem 1.2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.alert__title {
|
||||
margin: 0 0 0.3rem;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.alert__detail {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.alert__tag {
|
||||
padding: 0.4rem 0.9rem;
|
||||
border-radius: 999px;
|
||||
background: #e0f2fe;
|
||||
color: #0369a1;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.task-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.task {
|
||||
background: #f8fafc;
|
||||
border-radius: 18px;
|
||||
padding: 1rem 1.2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.task__title {
|
||||
margin: 0 0 0.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.task__due {
|
||||
margin: 0;
|
||||
color: #94a3b8;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.task__badge {
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: 12px;
|
||||
background: #e2e8f0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.is-credit {
|
||||
color: #10b981;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.is-debit {
|
||||
color: #ef4444;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.dashboard__hero,
|
||||
.panel {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.quick-log__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.pie-panel__content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.pie-chart {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.ledger-row {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.ledger-row span:nth-child(3),
|
||||
.ledger-row span:nth-child(4) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-log__form select {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
|
||||
background-position: right 0.5rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.5em 1.5em;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.ledger-table.is-scrollable {
|
||||
max-height: 25rem;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<div class="w-full p-6">
|
||||
|
||||
<!-- Summary Cards -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-6 mb-8">
|
||||
|
||||
<div class="bg-white border border-gray-200 rounded-2xl p-5 shadow-sm hover:shadow-md transition">
|
||||
<div class="text-gray-500 text-sm">งบทั้งหมด</div>
|
||||
<div class="text-3xl font-bold text-gray-800 mt-1">
|
||||
{{ totalBudget | number:'1.0-2' }} บาท
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white border border-gray-200 rounded-2xl p-5 shadow-sm hover:shadow-md transition">
|
||||
<div class="text-gray-500 text-sm">งบที่อนุมัติแล้ว</div>
|
||||
<div class="text-3xl font-bold text-green-600 mt-1">
|
||||
{{ approvedBudget | number:'1.0-2' }} บาท
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white border border-gray-200 rounded-2xl p-5 shadow-sm hover:shadow-md transition">
|
||||
<div class="text-gray-500 text-sm">งบคงเหลือ</div>
|
||||
<div
|
||||
class="text-3xl font-bold mt-1"
|
||||
[ngClass]="remainingBudget >= 0 ? 'text-blue-600' : 'text-red-600'"
|
||||
>
|
||||
{{ remainingBudget | number:'1.0-2' }} บาท
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Add Button -->
|
||||
<div class="mb-4">
|
||||
<button
|
||||
class=" rounded-3xl bg-green-600 hover:bg-green-700 text-white px-5 py-2.5 text-sm font-medium shadow-sm flex items-center gap-2 transition"
|
||||
>
|
||||
➕ เพิ่มโครงการใหม่
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="overflow-x-auto bg-white border border-gray-200 rounded-2xl shadow-sm">
|
||||
<table class="min-w-full text-left border-collapse">
|
||||
<thead class="bg-gray-100 border-b border-gray-200 text-gray-700 text-sm">
|
||||
<tr>
|
||||
<th class="py-3 px-4 font-semibold">ลำดับ</th>
|
||||
<th class="py-3 px-4 font-semibold">รหัสโครงการ</th>
|
||||
<th class="py-3 px-4 font-semibold">ชื่อโครงการ</th>
|
||||
<th class="py-3 px-4 font-semibold">ผู้รับผิดชอบ</th>
|
||||
<th class="py-3 px-4 font-semibold">งบที่ขออนุมัติ</th>
|
||||
<th class="py-3 px-4 font-semibold">หมวดงบ</th>
|
||||
<th class="py-3 px-4 font-semibold">จำนวนที่อนุมัติ</th>
|
||||
<th class="py-3 px-4 font-semibold text-center">สถานะ</th>
|
||||
<th class="py-3 px-4 font-semibold text-center">ดำเนินการ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr
|
||||
*ngFor="let p of projects; let i = index"
|
||||
class="border-b border-gray-100 hover:bg-blue-50/20 transition"
|
||||
>
|
||||
<td class="py-4 px-4 text-gray-700">{{ i + 1 }}</td>
|
||||
|
||||
<td class="py-4 px-4 font-medium text-gray-700">
|
||||
{{ p.code }}
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 text-gray-800 font-semibold leading-tight">
|
||||
{{ p.name }}
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 text-gray-700">
|
||||
{{ p.owner }}
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 text-blue-700 font-bold whitespace-nowrap">
|
||||
{{ p.budget | number:'1.0-0' }} บาท
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 w-64">
|
||||
<select
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-xl bg-white
|
||||
focus:outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-300
|
||||
text-sm transition"
|
||||
>
|
||||
<option value="">ไม่เลือก</option>
|
||||
|
||||
@for (item of budgetCategoriesDrop.expense; track item.dtlcod) {
|
||||
<option [value]="item.dtlcod">
|
||||
{{ item.dtlnam }}
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 w-40">
|
||||
<input
|
||||
type="number"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-xl bg-white
|
||||
focus:outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-300
|
||||
text-sm transition"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 text-center">
|
||||
<span
|
||||
class="px-3 py-1.5 rounded-full text-xs font-semibold inline-flex items-center gap-1
|
||||
shadow-sm border"
|
||||
[ngClass]="{
|
||||
'bg-yellow-50 text-yellow-700 border-yellow-200': p.status === 'WAIT',
|
||||
'bg-green-50 text-green-700 border-green-200': p.status === 'APPROVED',
|
||||
'bg-red-50 text-red-700 border-red-200': p.status === 'REJECTED'
|
||||
}"
|
||||
>
|
||||
<ng-container *ngIf="p.status === 'APPROVED'">อนุมัติแล้ว</ng-container>
|
||||
<ng-container *ngIf="p.status === 'WAIT'">รออนุมัติ</ng-container>
|
||||
<ng-container *ngIf="p.status === 'REJECTED'">ไม่อนุมัติ</ng-container>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="py-4 px-4 text-center space-x-2 whitespace-nowrap">
|
||||
|
||||
<button
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-xl text-sm
|
||||
shadow-sm font-medium transition"
|
||||
(click)="openBudgetDetail(p)"
|
||||
>
|
||||
จัดการงบประมาณ
|
||||
</button>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Pagination
|
||||
<div class="flex justify-end items-center gap-3 mt-5">
|
||||
<button class="px-4 py-2 border border-gray-300 rounded-xl bg-white hover:bg-gray-100">
|
||||
1
|
||||
</button>
|
||||
<button class="px-4 py-2 border border-gray-300 rounded-xl bg-white hover:bg-gray-100">
|
||||
2
|
||||
</button>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,168 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
||||
import { GeneralService } from '../../services/generalservice';
|
||||
import { IDropAct, IStateDrop, IStateResultResponse, IActData, IActSumData } from '../../interfaces/dashboard.interface'
|
||||
import { DashboardStateService } from '../../services/state/dashboard-state.service';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-manager',
|
||||
standalone: false,
|
||||
templateUrl: './main-manager.component.html',
|
||||
styleUrl: './main-manager.component.css'
|
||||
})
|
||||
export class MainManagerComponent implements OnInit {
|
||||
|
||||
mode: string = 'i';
|
||||
|
||||
totalBudget = 200000; // งบทั้งหมด
|
||||
approvedBudget = 65000; // งบที่อนุมัติแล้ว
|
||||
|
||||
|
||||
|
||||
get remainingBudget() {
|
||||
return this.totalBudget - this.approvedBudget;
|
||||
}
|
||||
|
||||
|
||||
|
||||
projects = [
|
||||
{ code: 'PRJ001', name: 'ระบบจัดการน้ำดื่ม', owner: 'นาย A', budget: 20000, status: 'WAIT' },
|
||||
{ code: 'PRJ002', name: 'ปรับปรุงอาคาร B', owner: 'นางสาว B', budget: 45000, status: 'WAIT' },
|
||||
{ code: 'PRJ003', name: 'ซื้อคอมพิวเตอร์', owner: 'นาย C', budget: 30000, status: 'APPROVED' }
|
||||
];
|
||||
|
||||
approveProject(p: any) {
|
||||
p.status = 'APPROVED';
|
||||
this.approvedBudget += p.budget;
|
||||
}
|
||||
|
||||
rejectProject(p: any) {
|
||||
p.status = 'REJECTED';
|
||||
}
|
||||
|
||||
openBudgetDetail(project: any) {
|
||||
this.router.navigate(['/main/manager/budget', project.code]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
budgetCategoriesDrop = {
|
||||
expense: [
|
||||
{ dtlcod: 'BDG001', dtlnam: 'เงินรายได้' },
|
||||
{ dtlcod: 'BDG002', dtlnam: 'งบดำเนินงาน ปวส.' },
|
||||
{ dtlcod: 'BDG003', dtlnam: 'โครงการส่งเสริมพัฒนาทักษะวิชาชีพทักษะพื้นฐาน' },
|
||||
{ dtlcod: 'BDG004', dtlnam: 'ค่ากิจกรรมพัฒนาคุณภาพผู้เรียน' },
|
||||
{ dtlcod: 'BDG005', dtlnam: 'อุดหนุนส่งเสริมและพัฒนาผู้เรียนองค์การวิชาชีพแห่งประเทศไทย (อวท.)' },
|
||||
{ dtlcod: 'BDG006', dtlnam: 'งบดำเนินงาน ระยะสั้น' },
|
||||
{ dtlcod: 'BDG007', dtlnam: 'โครงการบูรณาการการพัฒนาทักษะทางวิชาชีพกับการเสริมสร้างคุณลักษณะอันพึงประสงค์ (FIX IT)' },
|
||||
{ dtlcod: 'BDG008', dtlnam: 'โครงการพัฒนาทักษะและสมรรถนะวิชาชีพกำลังคน (Up-skill, Re-skill)' },
|
||||
{ dtlcod: 'BDG009', dtlnam: 'งบดำเนินงาน ปวช.' },
|
||||
{ dtlcod: 'BDG010', dtlnam: 'โครงการขยายและยกระดับการจัดอาชีวศึกษาระบบทวิภาคีคุณภาพสูง' },
|
||||
{ dtlcod: 'BDG011', dtlnam: 'ปวช.(สอจ)' },
|
||||
{ dtlcod: 'BDG012', dtlnam: 'ค่าจัดการเรียนการสอน' },
|
||||
{ dtlcod: 'BDG013', dtlnam: 'งบดำเนินงาน 170000' },
|
||||
{ dtlcod: 'BDG014', dtlnam: 'โครงการพัฒนาทักษะและศักยภาพภาพการจัดการเรียนการสอนอาชีวศึกษา' },
|
||||
{ dtlcod: 'BDG015', dtlnam: 'ศึกษาธิการ' },
|
||||
{ dtlcod: 'BDG016', dtlnam: 'Up-skill' },
|
||||
{ dtlcod: 'BDG017', dtlnam: 'ติดตามผู้สำเร็จ' },
|
||||
{ dtlcod: 'BDG018', dtlnam: 'ทวิภาคี' },
|
||||
{ dtlcod: 'BDG019', dtlnam: 'พันธุกรรม' },
|
||||
{ dtlcod: 'BDG020', dtlnam: 'ปวส(สาธารณูประโภค)' },
|
||||
{ dtlcod: 'BDG021', dtlnam: 'ปวส(ค่าสาธารณูปโภค)' },
|
||||
{ dtlcod: 'BDG022', dtlnam: 'งบดำเนินงาน (ค่าสาธารณูปโภค)' },
|
||||
{ dtlcod: 'BDG023', dtlnam: 'ค่าหนังสือเรียน' },
|
||||
{ dtlcod: 'BDG024', dtlnam: 'โครงการอาชีวะต้านยาเสพติด' },
|
||||
{ dtlcod: 'BDG025', dtlnam: 'ค่าตอบแทนพนักงานราชการ' },
|
||||
{ dtlcod: 'BDG026', dtlnam: 'ค่าอุปกรณ์การเรียน' },
|
||||
{ dtlcod: 'BDG027', dtlnam: 'โครงการยกระดับและพัฒนาขีดความสามารถด้านภาษาและทักษะดิจิทัลเพื่อพัฒนากำลังคนให้มีสมรรถนะสูง' },
|
||||
{ dtlcod: 'BDG028', dtlnam: 'โครงการอนุรักษ์พันธุกรรมพืชอันเนื่องมาจากพระราชดำริ' },
|
||||
{ dtlcod: 'BDG029', dtlnam: 'ปวช.(สอจ.)' },
|
||||
{ dtlcod: 'BDG030', dtlnam: 'โครงการพัฒนาศักยภาพผู้เรียนอาชีวศึกษาในการเป็นผู้ประกอบการ (บ่มเพาะ)' },
|
||||
{ dtlcod: 'BDG031', dtlnam: 'โครงการพัฒนาและยกระดับการติดตามผู้สำเร็จการศึกษาอาชีวศึกษา' },
|
||||
{ dtlcod: 'BDG032', dtlnam: 'โครงการเสริมสร้างคุณธรรม จริยธรรมและธรรมาภิบาลในสถานศึกษา' },
|
||||
{ dtlcod: 'BDG033', dtlnam: 'โครงการจัดการอาชีวศึกษาเพื่อสนองพระราชดำริ' },
|
||||
{ dtlcod: 'BDG034', dtlnam: 'เงินรายได้ ป.ตรี' },
|
||||
{ dtlcod: 'BDG035', dtlnam: 'งบดำเนินงาน 235200' }
|
||||
]
|
||||
};
|
||||
|
||||
// isModalOpen: boolean = false;
|
||||
// isSubmitting: boolean = false;
|
||||
// arrearsForm!: FormGroup;
|
||||
// saveFrm!: FormGroup;
|
||||
// myActData: IActData[] = [];
|
||||
// // myDropAct: IStateDrop[] = [];
|
||||
// myDropAct: IStateDrop = { income: [], expense: [] };
|
||||
// myActSumData: IActSumData = {
|
||||
// summary: {
|
||||
// totalIncome: '',
|
||||
// totalExpense: '',
|
||||
// netProfit: '',
|
||||
// profitRate: '',
|
||||
// adjustedProfitRate: '',
|
||||
// period: ''
|
||||
// },
|
||||
// pie: {
|
||||
// income: [],
|
||||
// expense: []
|
||||
// }
|
||||
// };
|
||||
// ActSumDataGradient: any
|
||||
|
||||
|
||||
// readonly ownerName = 'Nuttakit';
|
||||
|
||||
constructor(
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setupFormControl();
|
||||
// this.dashboardStateService.getStateResult().subscribe(data => {
|
||||
// if (data) {
|
||||
// this.myDropAct = data;
|
||||
// }
|
||||
// });
|
||||
// // ผลลับท์ ของ รายการ
|
||||
// this.dashboardStateService.getStateAccountResult().subscribe(data => {
|
||||
// if (data) {
|
||||
// this.myActData = data;
|
||||
// }
|
||||
// });
|
||||
// // ผลลัพการ คำนวณ ของ ปัญชี ต่างๆ
|
||||
// this.dashboardStateService.getStateSumResult().subscribe(data => {
|
||||
// if (data) {
|
||||
// this.myActSumData = data;
|
||||
// this.ActSumDataGradient = this.buildExpenseGradient()
|
||||
// }
|
||||
// });
|
||||
}
|
||||
setupFormControl(){
|
||||
// this.arrearsForm = new FormGroup({
|
||||
// // email: new FormControl('',[Validators.required, Validators.email, Validators.maxLength(100)]),
|
||||
// amount: new FormControl('',[Validators.required, Validators.maxLength(20)]),
|
||||
// expdtm: new FormControl('',[Validators.required, Validators.maxLength(12)]),
|
||||
// note: new FormControl('',[Validators.maxLength(200)]),
|
||||
// reason: new FormControl('',[Validators.required, Validators.maxLength(200)])
|
||||
// });
|
||||
|
||||
// this.saveFrm = new FormGroup({
|
||||
// actacpdtm: new FormControl('',[Validators.required, Validators.maxLength(12)]),
|
||||
// actqty: new FormControl('',[Validators.required]),
|
||||
// actcat: new FormControl('',[Validators.required, Validators.maxLength(1)]),
|
||||
// actcmt: new FormControl('',[Validators.maxLength(200)])
|
||||
// });
|
||||
}
|
||||
|
||||
onSaveSubmit(){
|
||||
|
||||
}
|
||||
|
||||
onArrearsSubmit(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,33 +10,54 @@
|
||||
|
||||
<div class="flex items-center gap-3 p-5">
|
||||
<h3 *ngIf="isOpen" class="text-2xl font-bold transition-all duration-300">
|
||||
Global Sidebar
|
||||
<img src="logo.png" alt="">
|
||||
</h3>
|
||||
|
||||
|
||||
<!-- <div class="h-24 w-24 rounded-full bg-white flex items-center justify-center overflow-hidden">
|
||||
<img src="logo.png" alt="">
|
||||
<span class="text-center text-white font-bold text-sm leading-tight">วิทยาลัยเทคนิคตรัง</span>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<hr class="border-t border-amber-700 mx-4 my-4 opacity-70">
|
||||
|
||||
<ul class="flex flex-col gap-2 px-2 grow">
|
||||
<li class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer
|
||||
hover:bg-amber-800 hover:shadow-lg transition-all duration-300 ease-in-out"
|
||||
(click)="navigate('/main/landing')">
|
||||
<i class="text-xl group-hover:scale-110 transition-transform"><img src="icons8-home-384.png" alt="" class="h-6 grayscale"></i>
|
||||
@if(isOpen){
|
||||
<span class="text-lg font-medium">หน้าหลัก</span>
|
||||
}
|
||||
</li>
|
||||
|
||||
<li class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer
|
||||
|
||||
<li class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer
|
||||
hover:bg-amber-800 hover:shadow-lg transition-all duration-300 ease-in-out"
|
||||
(click)="navigate('/main/dashboard')">
|
||||
<i class="fas fa-tachometer-alt text-xl group-hover:scale-110 transition-transform"></i>
|
||||
<span *ngIf="isOpen" class="text-lg font-medium">Dashboard</span>
|
||||
@if(isOpen){
|
||||
<span class="text-lg font-medium">Dashboard</span>
|
||||
}
|
||||
</li>
|
||||
|
||||
<!-- <li class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer
|
||||
hover:bg-amber-800 hover:shadow-lg transition-all duration-300 ease-in-out"
|
||||
(click)="navigate('/main/profile')">
|
||||
<i class="fas fa-user-circle text-xl group-hover:scale-110 transition-transform"></i>
|
||||
<span *ngIf="isOpen" class="text-lg font-medium">Profile</span>
|
||||
</li> -->
|
||||
<li class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer
|
||||
hover:bg-amber-800 hover:shadow-lg transition-all duration-300 ease-in-out"
|
||||
(click)="navigate('/main/manager')">
|
||||
<i class="fas fa-user-circle text-xl group-hover:scale-110 transition-transform"></i>
|
||||
@if(isOpen){
|
||||
<span class="text-lg font-medium">Manager</span>
|
||||
}
|
||||
</li>
|
||||
|
||||
<li class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer
|
||||
hover:bg-amber-800 hover:shadow-lg transition-all duration-300 ease-in-out"
|
||||
(click)="navigate('/main/report')">
|
||||
<i class="fas fa-chart-bar text-xl group-hover:scale-110 transition-transform"></i>
|
||||
<span *ngIf="isOpen" class="text-lg font-medium">Report</span>
|
||||
@if(isOpen){
|
||||
<span class="text-lg font-medium">Report</span>
|
||||
}
|
||||
</li>
|
||||
|
||||
|
||||
@@ -45,7 +66,9 @@
|
||||
hover:bg-red-700 hover:shadow-lg transition-all duration-300 ease-in-out"
|
||||
(click)="logout()">
|
||||
<i class="fas fa-sign-out-alt text-xl group-hover:scale-110 transition-transform"></i>
|
||||
<span *ngIf="isOpen" class="text-lg font-medium text-red-200">Logout</span>
|
||||
@if(isOpen){
|
||||
<span class="text-lg font-medium text-red-200">Logout</span>
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<!-- <app-main-dashboard></app-main-dashboard> -->
|
||||
<app-main-manager></app-main-manager>
|
||||
@@ -0,0 +1,162 @@
|
||||
import { DashboardStateService } from '../../services/state/dashboard-state.service';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ChartConfiguration, ChartOptions } from 'chart.js';
|
||||
import { BaseChartDirective } from 'ng2-charts';
|
||||
import { GeneralService } from '../../services/generalservice';
|
||||
import { IDropAct, IStateDrop, IActData, IActSumData } from '../../interfaces/dashboard.interface';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-manager-content',
|
||||
standalone: false,
|
||||
templateUrl: './main-manager-content.component.html',
|
||||
styleUrls: ['./main-manager-content.component.css']
|
||||
})
|
||||
export class MainManagerContentComponent implements OnInit {
|
||||
// @ViewChild(BaseChartDirective) chart?: BaseChartDirective;
|
||||
myDropAct!: IStateDrop;
|
||||
myActData: IActData[] = [];
|
||||
myActSumData: IActSumData = {
|
||||
summary: {
|
||||
totalIncome: '',
|
||||
totalExpense: '',
|
||||
netProfit: '',
|
||||
profitRate: '',
|
||||
adjustedProfitRate: '',
|
||||
period: ''
|
||||
},
|
||||
pie: {
|
||||
income: [],
|
||||
expense: []
|
||||
}
|
||||
};
|
||||
|
||||
constructor(
|
||||
private generalService: GeneralService,
|
||||
private dashboardStateService: DashboardStateService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
let token = localStorage.getItem('access_token')
|
||||
this.OnSearchAct(token, true);
|
||||
this.OnSetupDashboard(token, true);
|
||||
this.OnSearchSum(token, true);
|
||||
}
|
||||
|
||||
OnSearchAct(value: any, setupFirst: boolean): void {
|
||||
const uri = '/api/web/accountingSearch';
|
||||
let request = {
|
||||
token: value
|
||||
}
|
||||
this.generalService.postRequest(uri, request).subscribe({
|
||||
next: (result: any) => {
|
||||
if (result.code === '200') {
|
||||
this.generalService.trowApi(result);
|
||||
this.myActData = result.data;
|
||||
this.dashboardStateService.setStateAccountResult(this.myActData);
|
||||
}else{
|
||||
this.generalService.trowApi(result);
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.generalService.trowApi(error);
|
||||
},
|
||||
complete: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
OnSetupDashboard(value: any, setupFirst: boolean): void {
|
||||
const uri = '/api/web/accountingSetup';
|
||||
let request = {
|
||||
token: value
|
||||
}
|
||||
this.generalService.postRequest(uri, request).subscribe({
|
||||
next: (result: any) => {
|
||||
if (result.code === '200') {
|
||||
this.generalService.trowApi(result);
|
||||
this.myDropAct = result.data
|
||||
this.dashboardStateService.setStateResult(this.myDropAct)
|
||||
}else{
|
||||
this.generalService.trowApi(result);
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.generalService.trowApi(error);
|
||||
},
|
||||
complete: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
OnSearchSum(value: any, setupFirst: boolean): void {
|
||||
const uri = '/api/web/accountingSum';
|
||||
let request = {
|
||||
token: value
|
||||
}
|
||||
this.generalService.postRequest(uri, request).subscribe({
|
||||
next: (result: any) => {
|
||||
if (result.code === '200') {
|
||||
this.generalService.trowApi(result);
|
||||
this.myActSumData = result.data
|
||||
this.dashboardStateService.setStateSumResult(this.myActSumData);
|
||||
}else{
|
||||
this.generalService.trowApi(result);
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.generalService.trowApi(error);
|
||||
},
|
||||
complete: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// fetchChartData(): void {
|
||||
// // NOTE: Using a placeholder endpoint as the actual one was not provided.
|
||||
// const uri = '/api/dashboard/summary-last-6-months';
|
||||
|
||||
// this.generalService.getRequest(uri).subscribe({
|
||||
// next: (result: any) => {
|
||||
// if (result.code === '200' && result.data) {
|
||||
// this.processChartData(result.data);
|
||||
// } else {
|
||||
// console.warn('Could not fetch chart data:', result.message_th);
|
||||
// // Optionally, display placeholder data or an error message
|
||||
// this.setupPlaceholderData();
|
||||
// }
|
||||
// },
|
||||
// error: (error: any) => {
|
||||
// console.error('Error fetching chart data:', error);
|
||||
// // Display placeholder data on error to show the graph structure
|
||||
// this.setupPlaceholderData();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// processChartData(data: any[]): void {
|
||||
// const labels = data.map(item => item.month);
|
||||
// const revenues = data.map(item => item.revenue);
|
||||
|
||||
// this.lineChartData.labels = labels;
|
||||
// this.lineChartData.datasets[0].data = revenues;
|
||||
|
||||
// this.chart?.update();
|
||||
// }
|
||||
|
||||
// setupPlaceholderData(): void {
|
||||
// // This function is called if the API fails, to show a sample graph.
|
||||
// const labels = ['January', 'February', 'March', 'April', 'May', 'June'];
|
||||
// const revenues = [1200, 1900, 3000, 5000, 2300, 3200]; // Sample data
|
||||
|
||||
// this.lineChartData.labels = labels;
|
||||
// this.lineChartData.datasets[0].data = revenues;
|
||||
|
||||
// this.chart?.update();
|
||||
// }
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { MainManagerContentComponent } from './../../content/main-manager-content/main-manager-content.component';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { MainDashboardContentComponent } from '../../content/main-dashboard-content/main-dashboard-content.component';
|
||||
import { MainReportComponent } from '../../component/main-report/main-report.component';
|
||||
import { BudgetAproval } from '../../component/budget-aproval/budget-aproval';
|
||||
import { MainLandingComponent } from '../../component/main-landing/main-landing.component';
|
||||
// import { MainReportComponent } from '../../component/main-report/main-report.component';
|
||||
|
||||
|
||||
@@ -9,6 +12,15 @@ import { MainReportComponent } from '../../component/main-report/main-report.com
|
||||
const routes: Routes = [
|
||||
{ path: 'dashboard', component: MainDashboardContentComponent },
|
||||
{ path: 'report', component: MainReportComponent },
|
||||
{ path: 'landing', component: MainLandingComponent },
|
||||
{ path: 'manager', component: MainManagerContentComponent },
|
||||
{
|
||||
path: 'manager',
|
||||
children: [
|
||||
{ path: '', component: MainManagerContentComponent }, // รายการโครงการ
|
||||
{ path: 'budget/:code', component: BudgetAproval }, // จัดการงบประมาณ
|
||||
]
|
||||
},
|
||||
// children: [
|
||||
// {
|
||||
// path: 'dashboard',
|
||||
@@ -18,8 +30,8 @@ const routes: Routes = [
|
||||
// { path: '', redirectTo: 'profile', pathMatch: 'full' }
|
||||
// ]
|
||||
|
||||
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||
{ path: '**', redirectTo: 'dashboard' }
|
||||
{ path: '', redirectTo: 'landing', pathMatch: 'full' },
|
||||
{ path: '**', redirectTo: 'landing' }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MainManagerContentComponent } from './../../content/main-manager-content/main-manager-content.component';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
// import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
@@ -10,6 +11,9 @@ import { MainDashboardComponent } from '../../component/main-dashboard/main-dash
|
||||
import { MainDashboardContentComponent } from '../../content/main-dashboard-content/main-dashboard-content.component';
|
||||
import { AccDateFormatPipe } from '../../pipe/dtmtodatetime.pipe';
|
||||
import { MainReportComponent } from '../../component/main-report/main-report.component';
|
||||
import { MainManagerComponent } from '../../component/main-manager/main-manager.component';
|
||||
import { BudgetAproval } from '../../component/budget-aproval/budget-aproval';
|
||||
import { MainLandingComponent } from '../../component/main-landing/main-landing.component';
|
||||
|
||||
// import { MainReportComponent } from '../../component/main-report/main-report.component';
|
||||
|
||||
@@ -20,6 +24,10 @@ import { MainReportComponent } from '../../component/main-report/main-report.com
|
||||
MainDashboardComponent,
|
||||
MainDashboardContentComponent,
|
||||
MainReportComponent,
|
||||
MainManagerContentComponent,
|
||||
MainManagerComponent,
|
||||
BudgetAproval,
|
||||
MainLandingComponent,
|
||||
AccDateFormatPipe
|
||||
// MainReportComponent
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AccountingNgNuttakit</title>
|
||||
<title>TTC</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
|
||||
@@ -72,6 +72,8 @@ html, body, app-root {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* sensible default box model */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
|
||||
@@ -219,3 +221,45 @@ body {
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
/* Custom-Table */
|
||||
.ledger-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.ledger-table.is-scrollable {
|
||||
max-height: 25rem;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
.ledger-row {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 0.8fr 1.2fr;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0.4rem;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
|
||||
.ledger-row {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.ledger-row span:nth-child(3),
|
||||
.ledger-row span:nth-child(4) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
.ledger-head {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
10
ng-ttc-frontend/tailwind.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{html,ts}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||