From c838b2a979903a76ee79e248f2c6de7bf2d64939 Mon Sep 17 00:00:00 2001 From: "ttc@2026" Date: Tue, 11 Nov 2025 10:52:30 +0700 Subject: [PATCH] first --- @knowleadge/สร้างโปรเจ็ค.txt | 142 ++++ accounting-ng-nuttakit/.editorconfig | 17 + accounting-ng-nuttakit/.gitignore | 46 ++ accounting-ng-nuttakit/.postcssrc.json | 5 + .../.vscode/extensions.json | 4 + accounting-ng-nuttakit/.vscode/launch.json | 33 + accounting-ng-nuttakit/.vscode/tasks.json | 42 ++ accounting-ng-nuttakit/README.md | 59 ++ accounting-ng-nuttakit/angular.json | 160 +++++ accounting-ng-nuttakit/capacitor.config.ts | 10 + accounting-ng-nuttakit/package.json | 90 +++ accounting-ng-nuttakit/public/logo.png | Bin 0 -> 107295 bytes .../src/app/app-routing.module.ts | 37 ++ .../src/app/app.component.css | 0 .../src/app/app.component.html | 1 + .../src/app/app.component.ts | 11 + accounting-ng-nuttakit/src/app/app.module.ts | 57 ++ .../license-privacy-terms.component.css | 68 ++ .../license-privacy-terms.component.html | 82 +++ .../license-privacy-terms.component.ts | 11 + .../login-forgot/login-forgot.component.css | 290 ++++++++ .../login-forgot/login-forgot.component.html | 105 +++ .../login-forgot/login-forgot.component.ts | 91 +++ .../login-page/login-page.component.css | 289 ++++++++ .../login-page/login-page.component.html | 55 ++ .../login-page/login-page.component.ts | 90 +++ .../main-dashboard.component.css | 629 ++++++++++++++++++ .../main-dashboard.component.html | 332 +++++++++ .../main-dashboard.component.ts | 220 ++++++ .../component/sidebar/sidebar.component.css | 39 ++ .../component/sidebar/sidebar.component.html | 65 ++ .../component/sidebar/sidebar.component.ts | 66 ++ .../login-content/login-content.component.css | 19 + .../login-content.component.html | 11 + .../login-content/login-content.component.ts | 140 ++++ .../main-dashboard-content.component.css | 0 .../main-dashboard-content.component.html | 1 + .../main-dashboard-content.component.ts | 95 +++ .../sidebar-content.component.css | 11 + .../sidebar-content.component.html | 9 + .../sidebar-content.component.ts | 11 + .../login-control-routing.module.ts | 15 + .../login-control/login-control.module.ts | 25 + .../main-control-routing.module.ts | 27 + .../main-control/main-control.module.ts | 29 + .../src/app/services/auth.guard.ts | 14 + .../src/app/services/generalservice.ts | 138 ++++ .../environments/environment.development.ts | 4 + .../src/environments/environment.ts | 4 + accounting-ng-nuttakit/src/index.html | 20 + accounting-ng-nuttakit/src/main.ts | 7 + accounting-ng-nuttakit/src/styles.css | 221 ++++++ accounting-ng-nuttakit/tsconfig.app.json | 15 + accounting-ng-nuttakit/tsconfig.json | 27 + accounting-ng-nuttakit/tsconfig.spec.json | 15 + accounting-ng-nuttakit/vite.config.ts | 10 + 56 files changed, 4014 insertions(+) create mode 100644 @knowleadge/สร้างโปรเจ็ค.txt create mode 100644 accounting-ng-nuttakit/.editorconfig create mode 100644 accounting-ng-nuttakit/.gitignore create mode 100644 accounting-ng-nuttakit/.postcssrc.json create mode 100644 accounting-ng-nuttakit/.vscode/extensions.json create mode 100644 accounting-ng-nuttakit/.vscode/launch.json create mode 100644 accounting-ng-nuttakit/.vscode/tasks.json create mode 100644 accounting-ng-nuttakit/README.md create mode 100644 accounting-ng-nuttakit/angular.json create mode 100644 accounting-ng-nuttakit/capacitor.config.ts create mode 100644 accounting-ng-nuttakit/package.json create mode 100644 accounting-ng-nuttakit/public/logo.png create mode 100644 accounting-ng-nuttakit/src/app/app-routing.module.ts create mode 100644 accounting-ng-nuttakit/src/app/app.component.css create mode 100644 accounting-ng-nuttakit/src/app/app.component.html create mode 100644 accounting-ng-nuttakit/src/app/app.component.ts create mode 100644 accounting-ng-nuttakit/src/app/app.module.ts create mode 100644 accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.css create mode 100644 accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.html create mode 100644 accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.ts create mode 100644 accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.css create mode 100644 accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.html create mode 100644 accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.ts create mode 100644 accounting-ng-nuttakit/src/app/component/login-page/login-page.component.css create mode 100644 accounting-ng-nuttakit/src/app/component/login-page/login-page.component.html create mode 100644 accounting-ng-nuttakit/src/app/component/login-page/login-page.component.ts create mode 100644 accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.css create mode 100644 accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.html create mode 100644 accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.ts create mode 100644 accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.css create mode 100644 accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.html create mode 100644 accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.ts create mode 100644 accounting-ng-nuttakit/src/app/content/login-content/login-content.component.css create mode 100644 accounting-ng-nuttakit/src/app/content/login-content/login-content.component.html create mode 100644 accounting-ng-nuttakit/src/app/content/login-content/login-content.component.ts create mode 100644 accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.css create mode 100644 accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.html create mode 100644 accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.ts create mode 100644 accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.css create mode 100644 accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.html create mode 100644 accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.ts create mode 100644 accounting-ng-nuttakit/src/app/controls/login-control/login-control-routing.module.ts create mode 100644 accounting-ng-nuttakit/src/app/controls/login-control/login-control.module.ts create mode 100644 accounting-ng-nuttakit/src/app/controls/main-control/main-control-routing.module.ts create mode 100644 accounting-ng-nuttakit/src/app/controls/main-control/main-control.module.ts create mode 100644 accounting-ng-nuttakit/src/app/services/auth.guard.ts create mode 100644 accounting-ng-nuttakit/src/app/services/generalservice.ts create mode 100644 accounting-ng-nuttakit/src/environments/environment.development.ts create mode 100644 accounting-ng-nuttakit/src/environments/environment.ts create mode 100644 accounting-ng-nuttakit/src/index.html create mode 100644 accounting-ng-nuttakit/src/main.ts create mode 100644 accounting-ng-nuttakit/src/styles.css create mode 100644 accounting-ng-nuttakit/tsconfig.app.json create mode 100644 accounting-ng-nuttakit/tsconfig.json create mode 100644 accounting-ng-nuttakit/tsconfig.spec.json create mode 100644 accounting-ng-nuttakit/vite.config.ts diff --git a/@knowleadge/สร้างโปรเจ็ค.txt b/@knowleadge/สร้างโปรเจ็ค.txt new file mode 100644 index 0000000..7844c16 --- /dev/null +++ b/@knowleadge/สร้างโปรเจ็ค.txt @@ -0,0 +1,142 @@ + n nSTEP 1 — สร้างโปรเจ็กต์ใหม่ + +ng new accounting-ng-nuttakit --no-standalone + +STEP 2 + +npm install bootstrap +npm install @angular/material @angular/cdk + +และเพิ่มใน angular.json + + +"styles": [ + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "src/styles.scss" +] + + +STEP 3 สร้าง Module แรก + ng generate module main --route main --module ../app-routing.module.ts + ng g module controls/login-control --routing + +STEP 4 + +ng generate component modules/goods/goods-content +ng generate component modules/goods/goods-page +ng generate service modules/goods/goods-state + +STEP 5 + +shared/interfaces/product.interface.ts + + +export interface Product { + id: number; + name: string; + price: number; + category: string; + imgUrl: string; +} + + +goods-state.service.ts + +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { Product } from 'src/app/shared/interfaces/product.interface'; + +@Injectable({ providedIn: 'root' }) +export class GoodsStateService { + private goodsState = new BehaviorSubject([]); + goodsState$ = this.goodsState.asObservable(); + + setState(data: Product[]) { + this.goodsState.next(data); + } + + getStateResult() { + return this.goodsState$; + } +} + + +STEP 10 — สร้าง Content Component (ตัวควบคุมหลัก) + +import { Component, OnInit } from '@angular/core'; +import { GoodsStateService } from './goods-state.service'; +import { Product } from 'src/app/shared/interfaces/product.interface'; + +@Component({ + selector: 'app-goods-content', + templateUrl: './goods-content.component.html', +}) +export class GoodsContentComponent implements OnInit { + products: Product[] = []; + + constructor(private goodsState: GoodsStateService) {} + + ngOnInit(): void { + this.goodsState.getStateResult().subscribe(data => { + this.products = data; + }); + this.mockData(); // ตัวอย่าง data + } + + private mockData() { + const mock: Product[] = [ + { id: 1, name: 'Phone', price: 9000, category: 'Electronics', imgUrl: 'phone.jpg' }, + { id: 2, name: 'Laptop', price: 25000, category: 'Electronics', imgUrl: 'laptop.jpg' } + ]; + this.goodsState.setState(mock); + } +} + +goods-page.component.html + + + +
+
+
+ +
+
{{ item.name }}
+

{{ item.price | currency:'THB' }}

+
+
+
+
+ + + +app-routing.module.ts + + + +const routes: Routes = [ + { + path: 'goods', + canActivate: [AuthControl], + loadChildren: () => import('./modules/goods/goods.module').then(m => m.GoodsModule) + }, + { path: '**', redirectTo: '/goods' } +]; + + + +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ +MODULE +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + +# สร้างโฟลเดอร์ control +mkdir src/app/controls/login-control +cd src/app/controls/login-control + +# สร้าง module พร้อม routing +ng g module controls/login-control --routing + +# (optional) guard +ng g guard controls/login-control/login + diff --git a/accounting-ng-nuttakit/.editorconfig b/accounting-ng-nuttakit/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/accounting-ng-nuttakit/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/accounting-ng-nuttakit/.gitignore b/accounting-ng-nuttakit/.gitignore new file mode 100644 index 0000000..aa7d07f --- /dev/null +++ b/accounting-ng-nuttakit/.gitignore @@ -0,0 +1,46 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/ios +/dist +/android +/node_modules +npm-debug.log +package-lock.json +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/accounting-ng-nuttakit/.postcssrc.json b/accounting-ng-nuttakit/.postcssrc.json new file mode 100644 index 0000000..e092dc7 --- /dev/null +++ b/accounting-ng-nuttakit/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} diff --git a/accounting-ng-nuttakit/.vscode/extensions.json b/accounting-ng-nuttakit/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/accounting-ng-nuttakit/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/accounting-ng-nuttakit/.vscode/launch.json b/accounting-ng-nuttakit/.vscode/launch.json new file mode 100644 index 0000000..e8e08f9 --- /dev/null +++ b/accounting-ng-nuttakit/.vscode/launch.json @@ -0,0 +1,33 @@ +// { +// // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +// "version": "0.2.0", +// "configurations": [ +// { +// "name": "ng serve", +// "type": "chrome", +// "request": "launch", +// "preLaunchTask": "npm: start", +// "url": "http://localhost:4200/" +// }, +// { +// "name": "ng test", +// "type": "chrome", +// "request": "launch", +// "preLaunchTask": "npm: test", +// "url": "http://localhost:9876/debug.html" +// } +// ] +// } +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "Launch Chrome against localhost", + "type": "chrome", + "request": "launch", + "url": "http://localhost:4200", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/accounting-ng-nuttakit/.vscode/tasks.json b/accounting-ng-nuttakit/.vscode/tasks.json new file mode 100644 index 0000000..a298b5b --- /dev/null +++ b/accounting-ng-nuttakit/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ] +} diff --git a/accounting-ng-nuttakit/README.md b/accounting-ng-nuttakit/README.md new file mode 100644 index 0000000..e51cb70 --- /dev/null +++ b/accounting-ng-nuttakit/README.md @@ -0,0 +1,59 @@ +# AccountingNgNuttakit + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.18. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/accounting-ng-nuttakit/angular.json b/accounting-ng-nuttakit/angular.json new file mode 100644 index 0000000..bbd2c56 --- /dev/null +++ b/accounting-ng-nuttakit/angular.json @@ -0,0 +1,160 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "accounting-ng-nuttakit": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "standalone": false + }, + "@schematics/angular:directive": { + "standalone": false + }, + "@schematics/angular:pipe": { + "standalone": false + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "outputPath": "dist/accounting-ng-nuttakit", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/@fortawesome/fontawesome-free/css/all.min.css", + "src/styles.css" + ], + "scripts": [ + "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.ts" + } + ], + "optimization": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "1MB", + "maximumError": "2MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kB", + "maximumError": "10kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "options": { + "host": "0.0.0.0", + "allowedHosts": ["accounting.nuttakit.work", "localhost"] + }, + "configurations": { + "production": { + "buildTarget": "accounting-ng-nuttakit:build:production" + }, + "development": { + "buildTarget": "accounting-ng-nuttakit:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n" + }, + "test": { + "builder": "@angular/build:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/@fortawesome/fontawesome-free/css/all.min.css", + "src/styles.css" + ], + "scripts": [ + "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" + ] + } + } + } + } + }, + "cli": { + "analytics": false + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } + } +} diff --git a/accounting-ng-nuttakit/capacitor.config.ts b/accounting-ng-nuttakit/capacitor.config.ts new file mode 100644 index 0000000..30bc824 --- /dev/null +++ b/accounting-ng-nuttakit/capacitor.config.ts @@ -0,0 +1,10 @@ +import type { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'accounting.nuttakit.work', + appName: 'accounting-ng-nuttakit', + webDir: "dist/accounting-ng-nuttakit/browser", + // bundledWebRuntime: false +}; + +export default config; diff --git a/accounting-ng-nuttakit/package.json b/accounting-ng-nuttakit/package.json new file mode 100644 index 0000000..75bb719 --- /dev/null +++ b/accounting-ng-nuttakit/package.json @@ -0,0 +1,90 @@ +{ + "name": "accounting-ng-nuttakit", + "version": "1.0.0", + "main": "electron/main.js", + "author": "Nuttakit", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "electron": "ng build --base-href ./ && electron .", + "dist": "ng build --configuration production && electron-builder" + }, + "build": { + "appId": "accounting.nuttakit.work", + "productName": "accounting-nuttakit", + "asar": false, + "directories": { + "output": "dist_electron" + }, + "files": [ + "dist/accounting-ng-nuttakit/browser/**/*", + "electron/**/*", + "!node_modules/@capacitor/android/**/*" + ], + "win": { + "target": [ + "nsis", + "msi" + ], + "icon": "public/favicon.ico" + }, + "nsis": { + "oneClick": false, + "perMachine": true, + "allowElevation": true, + "runAfterFinish": false + } + }, + "private": true, + "dependencies": { + "@angular/animations": "^20.3.10", + "@angular/common": "^20.3.10", + "@angular/compiler": "^20.3.10", + "@angular/core": "^20.3.10", + "@angular/forms": "^20.3.10", + "@angular/platform-browser": "^20.3.10", + "@angular/platform-browser-dynamic": "^20.3.10", + "@angular/router": "^20.3.10", + "@capacitor/android": "^7.4.4", + "@capacitor/angular": "^2.0.3", + "@capacitor/core": "latest", + "@fortawesome/angular-fontawesome": "^3.0.0", + "@fortawesome/fontawesome-free": "^7.1.0", + "@fortawesome/fontawesome-svg-core": "^7.1.0", + "@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", + "bootstrap": "^5.3.8", + "chart.js": "^4.5.1", + "dotenv": "^17.2.3", + "ng2-charts": "^6.0.1", + "postcss": "^8.5.6", + "rxjs": "~7.8.0", + "tailwindcss": "^4.1.16", + "tslib": "^2.3.0", + "zone.js": "~0.15.0" + }, + "devDependencies": { + "@angular/build": "^20.3.9", + "@angular/cli": "^20.3.9", + "@angular/compiler-cli": "^20.3.10", + "@capacitor/cli": "latest", + "cross-env": "^10.1.0", + "electron": "^39.0.0", + "electron-builder": "^26.0.12", + "jasmine-core": "~5.6.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "ngx-toastr": "^19.1.0", + "postcss": "^8.5.3", + "typescript": "~5.9.3", + "@types/jasmine": "~5.1.0" + } +} \ No newline at end of file diff --git a/accounting-ng-nuttakit/public/logo.png b/accounting-ng-nuttakit/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b4b482ad889fac09f1ae41f4c0d3d26e9dc3bb65 GIT binary patch literal 107295 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%0Aa#EEy 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ⅇ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 import('./controls/login-control/login-control.module').then(m => m.LoginControlModule) }, + + { path: 'c', component: LicensePrivacyTermsComponent}, + + { + path: 'main', + component: SidebarContentComponent, + canActivate: [authGuard], + children: [ + { + path: '', + loadChildren: () => + import('./controls/main-control/main-control.module').then( + (m) => m.MainControlModule + ), + }, + ], + }, + + { path: '', redirectTo: 'login', pathMatch: 'full' }, + + { path: '**', redirectTo: 'login' } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule {} diff --git a/accounting-ng-nuttakit/src/app/app.component.css b/accounting-ng-nuttakit/src/app/app.component.css new file mode 100644 index 0000000..e69de29 diff --git a/accounting-ng-nuttakit/src/app/app.component.html b/accounting-ng-nuttakit/src/app/app.component.html new file mode 100644 index 0000000..0680b43 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/app.component.html @@ -0,0 +1 @@ + diff --git a/accounting-ng-nuttakit/src/app/app.component.ts b/accounting-ng-nuttakit/src/app/app.component.ts new file mode 100644 index 0000000..fa6282a --- /dev/null +++ b/accounting-ng-nuttakit/src/app/app.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + standalone: false, + styleUrl: './app.component.css' +}) +export class AppComponent { + title = 'accounting-ng-nuttakit'; +} diff --git a/accounting-ng-nuttakit/src/app/app.module.ts b/accounting-ng-nuttakit/src/app/app.module.ts new file mode 100644 index 0000000..90181ec --- /dev/null +++ b/accounting-ng-nuttakit/src/app/app.module.ts @@ -0,0 +1,57 @@ +import { NgModule, Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { ToastrModule } from 'ngx-toastr'; +import { AppRoutingModule } from './app-routing.module'; +// import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; + +// import { LayoutComponent } from './content/content/layout/layout.component'; +import { SidebarContentComponent } from './content/sidebar-content/sidebar-content.component'; +import { SidebarComponent } from './component/sidebar/sidebar.component'; +// import { ReactiveFormsModule } from '@angular/forms'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { HttpClientModule } from '@angular/common/http'; +import { LicensePrivacyTermsComponent } from './component/license-privacy-terms/license-privacy-terms.component'; +// import { MainDashboardContentComponent } from './content/main-dashboard-content/main-dashboard-content.component'; +// import { MainDashboardComponent } from './component/main-dashboard/main-dashboard.component'; +// import { LoginForgotComponent } from './component/login-forgot/login-forgot.component'; +// import { LoginPageComponent } from './component/login-page/login-page.component'; +// import { LoginContentComponent } from './content/login-content/login-content.component'; + +import { provideCharts, withDefaultRegisterables } from 'ng2-charts'; + +@NgModule({ + declarations: [ + AppComponent, + // LayoutComponent, + SidebarContentComponent, + SidebarComponent, + LicensePrivacyTermsComponent, + // MainDashboardContentComponent, + // MainDashboardComponent, + // LoginForgotComponent, + // LoginPageComponent, + // LoginPageComponentComponent, + ], + imports: [ + BrowserModule, + CommonModule, + ToastrModule.forRoot({ + positionClass:'toast-top-right', + preventDuplicates: true, + maxOpened: 3, + autoDismiss: true + }), + // ReactiveFormsModule, + BrowserAnimationsModule, + AppRoutingModule, + HttpClientModule, + FontAwesomeModule + ], + providers: [provideCharts(withDefaultRegisterables())], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.css b/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.css new file mode 100644 index 0000000..06045f8 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.css @@ -0,0 +1,68 @@ +.policy-container { + display: flex; + justify-content: center; + align-items: flex-start; + background: linear-gradient(135deg, #f3f6f9 0%, #e9eff5 100%); + min-height: 100vh; + padding: 40px 20px; + color: #1a1f36; + font-family: "Sarabun", "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif; +} + +.card { + background: white; + max-width: 800px; + width: 100%; + border-radius: 16px; + box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08); + padding: 40px; + line-height: 1.7; +} + +.page-title { + font-size: 26px; + font-weight: 600; + margin-bottom: 8px; +} + +.subtitle { + font-size: 13px; + color: #6b737a; + margin-bottom: 32px; +} + +section { + margin-bottom: 40px; +} + +h2 { + font-size: 20px; + margin-bottom: 10px; + color: #0b1a2b; +} + +p { + font-size: 15px; + color: #333; + margin-bottom: 12px; + text-align: justify; +} + +a { + color: #0078d4; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +footer { + text-align: center; + margin-top: 40px; +} + +.footer-text { + color: #6b737a; + font-size: 13px; +} diff --git a/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.html b/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.html new file mode 100644 index 0000000..08da7b8 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.html @@ -0,0 +1,82 @@ +
+
+

ข้อตกลงสิทธิ์การใช้งาน นโยบายความเป็นส่วนตัว และเงื่อนไขการให้บริการ

+

ปรับปรุงล่าสุด: 27 ตุลาคม 2025

+ +
+

1. ข้อตกลงสิทธิ์การใช้งาน (License Agreement)

+

+ ซอร์สโค้ด ส่วนประกอบ และทรัพย์สินการออกแบบทั้งหมดภายใต้โครงการ Nuttakit Software + อยู่ภายใต้สัญญาอนุญาตแบบ MIT License เว้นแต่จะมีการระบุเป็นอย่างอื่นโดยเฉพาะ +

+

+ ท่านได้รับสิทธิ์ในการใช้งาน คัดลอก แก้ไข รวม รวมเข้ากับซอฟต์แวร์อื่น เผยแพร่ หรือแจกจ่ายซอฟต์แวร์นี้ + เพื่อวัตถุประสงค์ส่วนตัวหรือเชิงพาณิชย์ได้ + โดยต้องคงไว้ซึ่งข้อความลิขสิทธิ์และข้อความอนุญาตนี้ในสำเนาทั้งหมดของซอฟต์แวร์ +

+

+ ซอฟต์แวร์นี้ถูกจัดให้ “ตามสภาพ” (AS IS) + โดยไม่มีการรับประกันใด ๆ ไม่ว่าจะโดยชัดแจ้งหรือโดยนัย + รวมถึงแต่ไม่จำกัดเฉพาะการรับประกันความเหมาะสมในการใช้งานหรือความถูกต้องของข้อมูล +

+
+ +
+

2. นโยบายความเป็นส่วนตัว (Privacy Policy)

+

+ NuttakitSoftwareให้ความสำคัญกับความเป็นส่วนตัวของผู้ใช้งาน + แอปพลิเคชันของเราจะเก็บข้อมูลเพียงบางส่วนเท่านั้น เช่น รหัสอุปกรณ์ + บันทึกการทำงาน หรือสถิติการใช้งาน + เพื่อใช้ในการวิเคราะห์ ปรับปรุง และพัฒนาประสิทธิภาพของระบบ +

+

+ ข้อมูลส่วนบุคคล (Personal Identifiable Information — PII) + จะไม่ถูกขาย แบ่งปัน หรือโอนไปยังบุคคลที่สามโดยไม่ได้รับความยินยอมจากท่านอย่างชัดเจน +

+

+ เราใช้มาตรฐานการเข้ารหัสระดับอุตสาหกรรม (AES-CBC) + เพื่อรักษาความปลอดภัยในการส่งข้อมูลระหว่างแอปพลิเคชัน เซิร์ฟเวอร์ และ API +

+

+ ผู้ใช้สามารถร้องขอให้ลบหรือขอรับสำเนาข้อมูลของตนเองได้ตลอดเวลา + โดยติดต่อทีมสนับสนุนของเรา +

+
+ +
+

3. เงื่อนไขการให้บริการ (Terms of Service)

+

+ เมื่อท่านใช้งานซอฟต์แวร์หรือบริการของเรา ถือว่าท่านยอมรับและปฏิบัติตามกฎหมายและข้อบังคับที่เกี่ยวข้องทั้งหมด +

+

+ ท่านจะต้องไม่ใช้ซอฟต์แวร์ของเราในทางที่ผิด + ไม่พยายามถอดรหัส แก้ไข ดัดแปลง หรือแสวงหาประโยชน์จากช่องโหว่ในระบบโดยไม่ได้รับอนุญาต +

+

+ Nuttakit ขอสงวนสิทธิ์ในการแก้ไขหรือยุติการให้บริการโดยไม่ต้องแจ้งให้ทราบล่วงหน้า + หากตรวจพบการละเมิดความปลอดภัยหรือการใช้งานที่ไม่เหมาะสม +

+

+ ทีมพัฒนา Nuttakit และผู้ร่วมพัฒนาไม่รับผิดชอบต่อความเสียหายใด ๆ + ที่อาจเกิดจากการใช้งานหรือไม่สามารถใช้งานซอฟต์แวร์นี้ได้ +

+
+ +
+

4. ช่องทางการติดต่อ (Contact)

+

+ หากท่านมีข้อสงสัยหรือข้อกังวลเกี่ยวกับข้อมูลส่วนบุคคลหรือเงื่อนไขการให้บริการ + สามารถติดต่อเราได้ที่: +
+ อีเมล: support@nuttakit.work
+ เว็บไซต์: + https://nuttakit.work +

+
+ +
+
+ +
+
+
diff --git a/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.ts b/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.ts new file mode 100644 index 0000000..9f460a8 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/license-privacy-terms/license-privacy-terms.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-license-privacy-terms', + standalone: false, + templateUrl: './license-privacy-terms.component.html', + styleUrl: './license-privacy-terms.component.css' +}) +export class LicensePrivacyTermsComponent { + +} diff --git a/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.css b/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.css new file mode 100644 index 0000000..cd46dbe --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.css @@ -0,0 +1,290 @@ +:root { + --bg-1: #f3f6f9; + --card-bg: #ffffff; + --muted: #6b737a; + --text: #0b1a2b; + --primary: #0078d4; + --primary-600: #0065b8; + --radius: 8px; + --shadow: 0 10px 30px rgba(11,26,43,0.08); + --glass: rgba(255,255,255,0.6); +} + +/* Page layout */ +.login-widget { + /* Fill the viewport and center the card. Do not let the page itself + scroll; the card gets an internal max-height instead. */ + min-height: 100vh; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 28px 18px; + background: linear-gradient(180deg, #f7f9fb 0%, var(--bg-1) 100%); + color: var(--text); +} + + +/* Card */ +.login-widget .card{ + width: 380px; + max-width: calc(100% - 40px); + background: var(--card-bg); + border-radius: calc(var(--radius) + 2px); + box-shadow: var(--shadow); + padding: 22px; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 14px; + /* Constrain the card so it never forces the page to scroll. If content + grows, the card will scroll internally. */ + max-height: calc(100vh - 56px); + overflow: auto; +} + +/* Modal/backdrop styles */ +.login-backdrop{ + position: fixed; + inset: 0; /* top:0; right:0; bottom:0; left:0; */ + background: rgba(0,0,0,0.38); + display: flex; + align-items: center; + justify-content: center; + z-index: 1040; + padding: 24px; +} + +.login-modal{ width: 480px; max-width: 480px; } + +.modal-card{ + border-radius: 12px; + padding: 0; /* card children control internal padding */ + overflow: hidden; + box-shadow: 0 20px 50px rgba(2,6,23,0.4); +} + +/* Slightly larger brand area inside modal */ +.modal-card .brand{ padding: 18px; } + +/* Make the primary button pill-shaped and slightly larger */ +button.primary{ + color: #000; + border-radius: 999px; + padding: 10px 18px; + font-size: 15px; +} + +/* Make biometric and other action buttons visually lighter */ +.biometric{ + border-radius: 999px; + padding: 8px 12px; +} + +/* On small screens reduce modal padding and width to avoid overflow */ +@media (max-width: 420px){ + .login-backdrop{ padding: 12px; } + .login-modal{ max-width: 100%; } + .modal-card .brand{ padding: 12px; } +} + +/* Brand area */ +.brand{ + text-align: center; + padding-bottom: 4px; + border-bottom: 1px solid #eef2f5; +} +.brand .logo{ + height: 44px; + width: 44px; + object-fit: contain; + display: inline-block; + margin-bottom: 10px; +} +.brand h1{ + margin: 0; + font-size: 20px; + font-weight: 600; + letter-spacing: -0.2px; + color: var(--text); +} +.brand .subtitle{ + margin: 6px 0 12px; + color: var(--muted); + font-size: 13px; +} + +/* Form area */ +.form{ + /* keep compact spacing inside the card */ + /* width: 410px; */ + margin-top: 6px; + display: flex; + flex-direction: column; + gap: 12px; + padding: 6px 0 2px; +} + +/* Field label wrapper */ +.field{ + display: flex; + flex-direction: column; + gap: 6px; +} +.field .label-text{ + font-size: 13px; + color: var(--muted); +} + +/* Inputs */ +input[type="email"], +input[type="password"], +input[type="text"]{ + width: 100%; + box-sizing: border-box; + padding: 10px 12px; + font-size: 15px; + color: var(--text); + background: #fff; + border: 1px solid #d8dee6; + border-radius: 6px; + outline: none; + transition: box-shadow .14s ease, border-color .14s ease, transform .06s ease; + -webkit-appearance: none; + appearance: none; +} + +input::placeholder{ + color: #9aa3ad; +} +input:focus{ + border-color: var(--primary); + box-shadow: 0 6px 20px rgba(0,120,212,0.10); + transform: translateZ(0); +} + +/* Checkbox / stay signed */ +.stay-signed{ + display: inline-flex; + gap: 8px; + align-items: center; + font-size: 13px; + color: var(--muted); +} +.stay-signed input[type="checkbox"]{ + width: 16px; + height: 16px; + accent-color: var(--primary); +} + +/* Actions row */ +.actions{ + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-top: 4px; +} +button.primary{ + background: linear-gradient(180deg, var(--primary) 0%, var(--primary-600) 100%); + color: #000000; + border: none; + padding: 10px 14px; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + font-size: 14px; + box-shadow: 0 6px 18px rgba(0,120,212,0.12); + transition: transform .06s ease, box-shadow .12s ease, opacity .12s ease; +} +button.primary:hover:not(:disabled){ + transform: translateY(-1px); + box-shadow: 0 10px 24px rgba(0,120,212,0.14); +} +button.primary:active{ + transform: translateY(0); +} +button.primary:disabled{ + opacity: 0.55; + cursor: not-allowed; + box-shadow: none; +} + +/* Alternative options */ +.alt-options{ + display: flex; + align-items: center; + gap: 12px; + margin-top: 6px; + flex-wrap: wrap; +} +.biometric{ + display: inline-flex; + align-items: center; + gap: 10px; + padding: 8px 10px; + background: transparent; + color: var(--primary); + border: 1px solid rgba(0,120,212,0.14); + border-radius: 6px; + cursor: pointer; + font-weight: 600; + font-size: 13px; +} +.biometric svg{ display: block; opacity: .95; } +.biometric:hover{ + background: rgba(0,120,212,0.04); +} + +/* Help link */ +.help-link{ + margin-left: auto; + font-size: 13px; + color: var(--primary); + text-decoration: none; +} +.help-link:hover{ text-decoration: underline; } + +/* Footer */ +.footer{ + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + margin-top: 6px; + padding-top: 10px; + border-top: 1px solid #eef2f5; + font-size: 13px; + color: var(--muted); +} +.footer a{ + color: var(--primary); + text-decoration: none; + font-weight: 600; +} +.footer a:hover{ text-decoration: underline; } +.divider{ color: #d0d6db; } + +/* Focus styles for keyboard users */ +:focus{ + outline: none; +} +:focus-visible{ + outline: 3px solid rgba(0,120,212,0.12); + outline-offset: 2px; + border-radius: 6px; +} + +/* Small screens */ +@media (max-width:420px){ + .login-widget .card{ + padding: 18px; + width: 100%; + } + .brand h1{ font-size: 18px; } + .brand .subtitle{ + font-family: "Kanit"; + font-weight: 1000; + font-style: normal; } + .biometric span, .primary{ font-size: 13px; } +} diff --git a/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.html b/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.html new file mode 100644 index 0000000..660b8f7 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.html @@ -0,0 +1,105 @@ + diff --git a/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.ts b/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.ts new file mode 100644 index 0000000..f9ff117 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/login-forgot/login-forgot.component.ts @@ -0,0 +1,91 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; +import { GeneralService } from '../../services/generalservice'; + + +@Component({ + selector: 'app-login-forgot', + standalone: false, + templateUrl: './login-forgot.component.html', + styleUrl: './login-forgot.component.css' +}) +export class LoginForgotComponent implements OnInit { + // @Input() brandName = 'Contoso'; + // @Input() subtitle = 'to your account'; + // @Input() mode = ''; + @Output() otpEventSubmit = new EventEmitter(); + @Output() otpVerifyEventSubmit = new EventEmitter(); + + forgotFrm!: FormGroup; + isLoading: boolean = false; + isSendOtp: boolean = false; + isModalOpen: boolean = false; + // busy = false; + // message = ''; + + constructor( + // private generalService: GeneralService + // private fb: FormBuilder + ) {} + + ngOnInit(): void { + this.setupFormControl(); + } + + setupFormControl(){ + this.forgotFrm = new FormGroup({ + email: new FormControl('',[Validators.required, Validators.email, Validators.maxLength(100)]), + otp: new FormControl('',[Validators.required, Validators.maxLength(6)]), + newPassword: new FormControl('',[Validators.required, Validators.maxLength(50)]), + confirmPassword: new FormControl('',[Validators.required, Validators.maxLength(50)]) + }); + + } + + EventSubmit(event: any){ + this.otpEventSubmit.emit(event); + } + + + VerifyEventSubmit(event: any){ + this.otpVerifyEventSubmit.emit(event); + } + + onSubmin(){ + let data = { + email: this.forgotFrm.get('email')?.value + } + + this.EventSubmit(data); + } + + onVerifySubmit(){ + let data = { + email: this.forgotFrm.get('email')?.value, + otp: this.forgotFrm.get('otp')?.value + } + this.VerifyEventSubmit(data); + } + + onSetNewPassword(){ + let newpassword = this.forgotFrm.get('newPassword')?.value; + let confirmPassword = this.forgotFrm.get('confirmPassword')?.value; + + let data = { + email: this.forgotFrm.get('email')?.value, + otp: this.forgotFrm.get('otp')?.value, + newPassword: newpassword + } + + if (newpassword.trim() === confirmPassword.trim()) { + // this.VerifyEventSubmit(data); + console.log("Password matched! (รหัสผ่านตรงกัน)"); + } else { + console.error("Password mismatched! (รหัสผ่านไม่ตรงกัน)"); + } + + // console.log(newpassword.value); + + } + // otp: } +} diff --git a/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.css b/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.css new file mode 100644 index 0000000..5a4bcfd --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.css @@ -0,0 +1,289 @@ +:root { + --bg-1: #f3f6f9; + --card-bg: #ffffff; + --muted: #6b737a; + --text: #0b1a2b; + --primary: #0078d4; + --primary-600: #0065b8; + --radius: 8px; + --shadow: 0 10px 30px rgba(11,26,43,0.08); + --glass: rgba(255,255,255,0.6); +} + +/* Page layout */ +.login-widget { + /* Fill the viewport and center the card. Do not let the page itself + scroll; the card gets an internal max-height instead. */ + min-height: 100vh; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 28px 18px; + background: linear-gradient(180deg, #f7f9fb 0%, var(--bg-1) 100%); + color: var(--text); +} + + +/* Card */ +.login-widget .card{ + width: 380px; + max-width: calc(100% - 40px); + background: var(--card-bg); + border-radius: calc(var(--radius) + 2px); + box-shadow: var(--shadow); + padding: 22px; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 14px; + /* Constrain the card so it never forces the page to scroll. If content + grows, the card will scroll internally. */ + max-height: calc(100vh - 56px); + overflow: auto; +} + +/* Modal/backdrop styles */ +.login-backdrop{ + position: fixed; + inset: 0; /* top:0; right:0; bottom:0; left:0; */ + background: rgba(0,0,0,0.38); + display: flex; + align-items: center; + justify-content: center; + z-index: 1040; + padding: 24px; +} + +.login-modal{ width: 480px; max-width: 480px; } + +.modal-card{ + border-radius: 12px; + padding: 0; /* card children control internal padding */ + overflow: hidden; + box-shadow: 0 20px 50px rgba(2,6,23,0.4); +} + +/* Slightly larger brand area inside modal */ +.modal-card .brand{ padding: 18px; } + +/* Make the primary button pill-shaped and slightly larger */ +button.primary{ + color: #000; + border-radius: 999px; + padding: 10px 18px; + font-size: 15px; +} + +/* Make biometric and other action buttons visually lighter */ +.biometric{ + border-radius: 999px; + padding: 8px 12px; +} + +/* On small screens reduce modal padding and width to avoid overflow */ +@media (max-width: 420px){ + .login-backdrop{ padding: 12px; } + .login-modal{ max-width: 100%; } + .modal-card .brand{ padding: 12px; } +} + +/* Brand area */ +.brand{ + text-align: center; + padding-bottom: 4px; + border-bottom: 1px solid #eef2f5; +} +.brand .logo{ + height: 44px; + width: 44px; + object-fit: contain; + display: inline-block; + margin-bottom: 10px; +} +.brand h1{ + margin: 0; + font-size: 20px; + font-weight: 600; + letter-spacing: -0.2px; + color: var(--text); +} +.brand .subtitle{ + margin: 6px 0 12px; + color: var(--muted); + font-size: 13px; +} + +/* Form area */ +.form{ + /* keep compact spacing inside the card */ + margin-top: 6px; + display: flex; + flex-direction: column; + gap: 12px; + padding: 6px 0 2px; +} + +/* Field label wrapper */ +.field{ + display: flex; + flex-direction: column; + gap: 6px; +} +.field .label-text{ + font-size: 13px; + color: var(--muted); +} + +/* Inputs */ +input[type="email"], +input[type="password"], +input[type="text"]{ + width: 100%; + box-sizing: border-box; + padding: 10px 12px; + font-size: 15px; + color: var(--text); + background: #fff; + border: 1px solid #d8dee6; + border-radius: 6px; + outline: none; + transition: box-shadow .14s ease, border-color .14s ease, transform .06s ease; + -webkit-appearance: none; + appearance: none; +} + +input::placeholder{ + color: #9aa3ad; +} +input:focus{ + border-color: var(--primary); + box-shadow: 0 6px 20px rgba(0,120,212,0.10); + transform: translateZ(0); +} + +/* Checkbox / stay signed */ +.stay-signed{ + display: inline-flex; + gap: 8px; + align-items: center; + font-size: 13px; + color: var(--muted); +} +.stay-signed input[type="checkbox"]{ + width: 16px; + height: 16px; + accent-color: var(--primary); +} + +/* Actions row */ +.actions{ + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-top: 4px; +} +button.primary{ + background: linear-gradient(180deg, var(--primary) 0%, var(--primary-600) 100%); + color: #000000; + border: none; + padding: 10px 14px; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + font-size: 14px; + box-shadow: 0 6px 18px rgba(0,120,212,0.12); + transition: transform .06s ease, box-shadow .12s ease, opacity .12s ease; +} +button.primary:hover:not(:disabled){ + transform: translateY(-1px); + box-shadow: 0 10px 24px rgba(0,120,212,0.14); +} +button.primary:active{ + transform: translateY(0); +} +button.primary:disabled{ + opacity: 0.55; + cursor: not-allowed; + box-shadow: none; +} + +/* Alternative options */ +.alt-options{ + display: flex; + align-items: center; + gap: 12px; + margin-top: 6px; + flex-wrap: wrap; +} +.biometric{ + display: inline-flex; + align-items: center; + gap: 10px; + padding: 8px 10px; + background: transparent; + color: var(--primary); + border: 1px solid rgba(0,120,212,0.14); + border-radius: 6px; + cursor: pointer; + font-weight: 600; + font-size: 13px; +} +.biometric svg{ display: block; opacity: .95; } +.biometric:hover{ + background: rgba(0,120,212,0.04); +} + +/* Help link */ +.help-link{ + margin-left: auto; + font-size: 13px; + color: var(--primary); + text-decoration: none; +} +.help-link:hover{ text-decoration: underline; } + +/* Footer */ +.footer{ + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + margin-top: 6px; + padding-top: 10px; + border-top: 1px solid #eef2f5; + font-size: 13px; + color: var(--muted); +} +.footer a{ + color: var(--primary); + text-decoration: none; + font-weight: 600; +} +.footer a:hover{ text-decoration: underline; } +.divider{ color: #d0d6db; } + +/* Focus styles for keyboard users */ +:focus{ + outline: none; +} +:focus-visible{ + outline: 3px solid rgba(0,120,212,0.12); + outline-offset: 2px; + border-radius: 6px; +} + +/* Small screens */ +@media (max-width:420px){ + .login-widget .card{ + padding: 18px; + width: 100%; + } + .brand h1{ font-size: 18px; } + .brand .subtitle{ + font-family: "Kanit"; + font-weight: 1000; + font-style: normal; } + .biometric span, .primary{ font-size: 13px; } +} diff --git a/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.html b/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.html new file mode 100644 index 0000000..72d7e2a --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.html @@ -0,0 +1,55 @@ + + diff --git a/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.ts b/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.ts new file mode 100644 index 0000000..a032f77 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/login-page/login-page.component.ts @@ -0,0 +1,90 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; +import { Router } from '@angular/router'; +import { faCoffee } from '@fortawesome/free-solid-svg-icons'; + +@Component({ + selector: 'app-login-page', + standalone: false, + templateUrl: './login-page.component.html', + styleUrls: ['./login-page.component.css'], +}) +export class LoginPageComponent implements OnInit { + @Input() brandName = 'Contoso'; + @Input() subtitle = 'to your account'; + @Input() mode = ''; + @Output() signedIn = new EventEmitter(); + + faCoffee = faCoffee; + loginForm!: FormGroup; + busy = false; + message = ''; + + constructor( + private router: Router + ) {} + + ngOnInit(): void { + this.setupFormControl(); + } + + setupFormControl(): void { + this.loginForm = new FormGroup({ + username: new FormControl('',[Validators.required, Validators.maxLength(100)]), + password: new FormControl( '', [Validators.required, Validators.maxLength(50)]), + remember: new FormControl(false) + }); + } + + signIn(): void { + if (this.loginForm.invalid) return; + this.signedIn.emit(this.loginForm.value); + } + + async useBiometric(): Promise { + this.message = ''; + if (!('credentials' in navigator) || !('get' in (navigator as any).credentials)) { + this.message = 'Biometric authentication is not available on this device/browser.'; + return; + } + + try { + this.busy = true; + // Example WebAuthn / PublicKeyCredential call. In a real application, + // you must obtain the challenge and allowedCredentials from your server. + const publicKeyCredentialRequestOptions = { + // challenge must be provided by server as ArrayBuffer + challenge: Uint8Array.from('server-provided-challenge', c => c.charCodeAt(0)).buffer, + timeout: 60000, + userVerification: 'preferred', + } as any; + + const credential: any = await (navigator as any).credentials.get({ + publicKey: publicKeyCredentialRequestOptions, + }); + + // Send credential to server for verification (not implemented here). + // Example: await this.authService.verifyWebAuthn(credential); + + // On success: + this.signedIn.emit({ email: '', remember: true }); + } catch (err: any) { + this.message = err?.message || 'Biometric sign-in cancelled or failed.'; + } finally { + this.busy = false; + } + } + + forgotPassword(): void { + // emit or navigate + this.router.navigate(['/login/forgot-password']); + } + + createAccount(): void { + this.message = 'Create account flow not implemented.'; + } + + privacy(): void { + this.router.navigate(['/license']); + } +} diff --git a/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.css b/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.css new file mode 100644 index 0000000..d7ecda0 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.css @@ -0,0 +1,629 @@ +: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; + } +} diff --git a/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.html b/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.html new file mode 100644 index 0000000..dd50ebc --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.html @@ -0,0 +1,332 @@ +
+
+
+

ภาพรวมบัญชี

+

ยินดีต้อนรับกลับ, {{ ownerName }}

+

+ จดบันทึกรายรับรายจ่าย และดูสรุปต่อปี เดือน สัปดาห์ ได้ในหน้าเดียว +

+
+
+ + +
+
+ +
+
+
+ + {{ summary.label }} + +

{{ summary.note }}

+
+
+
+

รายรับ

+

{{ summary.income }}

+
+
+

รายจ่าย

+

{{ summary.expense }}

+
+
+

คงเหลือสุทธิ

+

{{ summary.net }}

+
+
+
+ แนวโน้ม {{ summary.trend }} +
+
+
+ +
+
+
+
+

{{ card.label }}

+
{{ card.value }}
+

{{ card.trend }} · {{ card.context }}

+
+
+
+ +
+
+
+
+

บันทึกรายการแบบรวดเร็ว

+

จดรายรับรายจ่ายภายในไม่กี่คลิก

+
+
+
+ + +
+ + +
+ + +
+
+ +
+
+
+

สมุดบันทึกล่าสุด

+

แยกสีระหว่างรายรับและรายจ่าย

+
+ +
+
+
+ รายการ + หมวดหมู่ + ยอดเงิน + บันทึก +
+
+
+ + {{ idx.type == 'i' ? 'รับ' : 'จ่าย' }} + +
+

{{ idx.title }}

+

{{ idx.date }}

+
+
+ {{ idx.category }} + + {{ idx.amount }} + + {{ idx.note }} +
+
+
+
+ +
+ + +
+
+
+

สัดส่วนค่าใช้จ่าย

+

ดูหมวดไหนใช้เงินมากที่สุด

+
+ +
+
+
+
+

รวมเดือนนี้

+ ฿732K +
+
+
    +
  • + +
    +

    {{ part.label }}

    +

    {{ part.value }}%

    +
    +
  • +
+
+
+ +
+
+
+

สรุปสภาพคล่อง

+

อัปเดตล่าสุด 5 นาทีที่แล้ว

+
+
+
+
+
+

+ {{ ratio.label }} +

+ {{ ratio.value }} +
+
+
+
+ +
+
+
+

การแจ้งเตือนสำคัญ

+

จัดลำดับงานค้างก่อนครบกำหนด

+
+
+
+
+

{{ alert.title }}

+

{{ alert.detail }}

+
+ {{ alert.tag }} +
+
+ +
+
+
+

รายการยอดค้างจ่าย

+

ช่วยเตือนความจำให้

+
+ +
+
    +
  • +
    +

    {{ task.title }}

    +

    {{ task.due }}

    +
    + {{ task.priority }} +
  • +
+
+ +
+
+ + +@if(isModalOpen == true){ + +} diff --git a/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.ts b/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.ts new file mode 100644 index 0000000..3d6ba96 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/main-dashboard/main-dashboard.component.ts @@ -0,0 +1,220 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; +import { GeneralService } from '../../services/generalservice'; + +@Component({ + selector: 'app-main-dashboard', + standalone: false, + templateUrl: './main-dashboard.component.html', + styleUrl: './main-dashboard.component.css' +}) +export class MainDashboardComponent implements OnInit { + + mode: string = 'i'; + isModalOpen: boolean = false; + isSubmitting: boolean = false; + arrearsForm!: FormGroup; + saveFrm!: FormGroup; + + + readonly ownerName = 'Nuttakit'; + + readonly kpiCards = [ + { + label: 'รายรับรวม', + value: '฿1.28M', + trend: '+12.4%', + context: 'เทียบกับเดือนก่อน', + accent: 'mint' + }, + { + label: 'รายจ่ายรวม', + value: '฿732K', + trend: '-4.1%', + context: 'จัดการได้ดีขึ้น', + accent: 'lavender' + }, + { + label: 'ยอดค้างชำระ', + value: '฿184K', + trend: '-2 ใบแจ้งหนี้', + context: 'รอติดตาม', + accent: 'amber' + }, + { + label: 'อัตรากำไร', + value: '37.8%', + trend: '+1.9 จุด', + context: 'ระยะ 30 วัน', + accent: 'teal' + } + ]; + + readonly revenueTrend = [ + { label: 'ม.ค.', value: 52 }, + { label: 'ก.พ.', value: 61 }, + { label: 'มี.ค.', value: 73 }, + { label: 'เม.ย.', value: 68 }, + { label: 'พ.ค.', value: 82 }, + { label: 'มิ.ย.', value: 77 } + ]; + + readonly quickRatios = [ + { label: 'กระแสเงินสด', value: '+฿312K', status: 'positive' }, + { label: 'วงเงินคงเหลือ', value: '฿890K', status: 'neutral' }, + { label: 'ค่าใช้จ่ายเดือนนี้', value: '฿412K', status: 'warning' } + ]; + + readonly periodSummaries = [ + { + label: 'รายปี', + note: 'ปี 2567', + income: '฿9.6M', + expense: '฿5.1M', + net: '+฿4.5M', + trend: '+18%', + badge: 'year' + }, + { + label: 'รายเดือน', + note: 'มิถุนายน 2567', + income: '฿1.28M', + expense: '฿732K', + net: '+฿548K', + trend: '+6%', + badge: 'month' + }, + { + label: 'รายสัปดาห์', + note: 'สัปดาห์ที่ 24', + income: '฿312K', + expense: '฿188K', + net: '+฿124K', + trend: '+2%', + badge: 'week' + } + ]; + + readonly alerts = [ + { + title: 'ใบแจ้งหนี้ #INV-083 จะครบกำหนด', + detail: 'ลูกค้า Metro Engineering', + tag: 'ภายใน 3 วัน' + }, + { + title: 'มีเอกสารที่ต้องอนุมัติ 2 รายการ', + detail: 'เบิกค่าใช้จ่ายฝ่ายการตลาด', + tag: 'รออนุมัติ' + }, + { + title: 'พบรายการใช้จ่ายผิดปกติ', + detail: 'ค่าใช้จ่ายเดินทางสูงกว่าค่าเฉลี่ย 28%', + tag: 'ตรวจสอบ' + } + ]; + + readonly tasks = [ + { + title: 'กระทบยอดธนาคาร เดือน มิ.ย.', + due: 'วันนี้ 16:00', + priority: 'สูง' + }, + { + title: 'เตรียมรายงาน VAT', + due: 'พรุ่งนี้ 10:30', + priority: 'กลาง' + }, + { + title: 'ออกใบเสนอราคา โครงการใหม่', + due: 'ศุกร์ 14:00', + priority: 'ต่ำ' + } + ]; + + readonly ledgerEntries = [ + { + type: 'i', + title: 'ค่าบริการที่ปรึกษา', + category: 'บริการ', + amount: '+฿85,000', + date: 'วันนี้ · 10:15', + note: 'โครงการ Warehouse Automation' + }, + { + type: 'e', + title: 'ค่าเช่าออฟฟิศ', + category: 'ค่าใช้จ่ายคงที่', + amount: '-฿48,000', + date: 'วันนี้ · 09:00', + note: 'สำนักงานพระราม 9' + }, + { + type: 'i', + title: 'รับเงินมัดจำ', + category: 'สัญญาใหม่', + amount: '+฿120,000', + date: 'เมื่อวาน', + note: 'ลูกค้า Urbane CoWorking' + }, + { + type: 'e', + title: 'ค่าวัตถุดิบ', + category: 'ต้นทุนโครงการ', + amount: '-฿27,500', + date: '12 มิ.ย.', + note: 'สั่งผ่าน Blue Supply' + } + ]; + + readonly expenseBreakdown = [ + { label: 'ฝ่ายบริหาร', value: 32, color: '#0ea5e9' }, + { label: 'การตลาด', value: 18, color: '#f97316' }, + { label: 'ต้นทุนโครงการ', value: 27, color: '#10b981' }, + { label: 'บุคลากร', value: 15, color: '#a855f7' }, + { label: 'อื่นๆ', value: 8, color: '#e11d48' } + ]; + + readonly expenseGradient = this.buildExpenseGradient(); + + ngOnInit(): void { + this.setupFormControl(); + } + + 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(){ + + } + + private buildExpenseGradient(): string { + let current = 0; + const segments = this.expenseBreakdown + .map(part => { + const start = current; + const end = current + part.value; + current = end; + return `${part.color} ${start}% ${end}%`; + }) + .join(', '); + return `conic-gradient(${segments})`; + } +} diff --git a/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.css b/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.css new file mode 100644 index 0000000..4d8834e --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.css @@ -0,0 +1,39 @@ +/* .sidebar { + width: 220px; + background: #222; + color: white; + height: 100vh; + padding: 20px; +} */ +/* .sidebar ul { + list-style: none; + padding: 0; +} +.sidebar li { + margin: 10px 0; + cursor: pointer; + transition: 0.2s; +} +.sidebar li:hover { + color: #00bcd4; +} */ + +@keyframes spin-slow { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} +.animate-spin-slow { + animation: spin-slow 8s linear infinite; +} + +@media (max-width: 768px) { + .sidebar { + position: absolute; + z-index: 50; + transform: translateX(-100%); + } + + .sidebar.expanded { + transform: translateX(0); + } +} diff --git a/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.html b/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.html new file mode 100644 index 0000000..cd2492e --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.html @@ -0,0 +1,65 @@ +
+ + + +
+

+ Global Sidebar +

+
+ +
+ +
    + +
  • + + Dashboard +
  • + +
  • + + Profile +
  • + +
  • + + Report +
  • + + + +
  • + + Logout +
  • +
+
+ +
+
+ + + + diff --git a/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.ts b/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.ts new file mode 100644 index 0000000..893babd --- /dev/null +++ b/accounting-ng-nuttakit/src/app/component/sidebar/sidebar.component.ts @@ -0,0 +1,66 @@ +import { Component, HostListener, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { trigger, state, style, transition, animate } from '@angular/animations'; + +@Component({ + selector: 'app-sidebar', + standalone: false, + templateUrl: './sidebar.component.html', + styleUrls: ['./sidebar.component.css'], + animations: [ + trigger('sidebarState', [ + state('expanded', style({ + width: '220px', + opacity: 1 + })), + state('collapsed', style({ + width: '70px', + opacity: 0.95 + })), + transition('expanded <=> collapsed', [ + animate('300ms ease-in-out') + ]) + ]) + ] +}) +export class SidebarComponent implements OnInit { + isOpen = true; // ขยายไหม + isMobile = false; // ตรวจอุปกรณ์ + showOverlay = false; // สำหรับ mobile overlay + + constructor(private router: Router) {} + + ngOnInit() { + this.checkDevice(); + window.addEventListener('resize', () => this.checkDevice()); + } + + @HostListener('window:resize') + checkDevice() { + this.isMobile = window.innerWidth <= 768; + if (this.isMobile) { + this.isOpen = false; // ซ่อน sidebar ตอนเข้า mobile + } else { + this.isOpen = true; // เปิดไว้ตลอดใน desktop + this.showOverlay = false; + } + } + + toggleSidebar() { + if (this.isMobile) { + this.showOverlay = !this.showOverlay; + this.isOpen = this.showOverlay; + } else { + this.isOpen = !this.isOpen; + } + } + + navigate(path: string) { + this.router.navigate([path]); + } + + logout() { + localStorage.removeItem('access_token'); + this.router.navigate(['/login']); + } +} diff --git a/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.css b/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.css new file mode 100644 index 0000000..fbe7f82 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.css @@ -0,0 +1,19 @@ +:host { + display: block; + margin: 0; + padding: 0; + width: 100%; + height: 100vh; /* ครอบเต็มหน้าจอ */ + overflow: hidden; /* ปิด scroll bar */ + box-sizing: border-box; +} + +.login-content { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; /* ✅ แก้ตรงนี้ จาก row → column */ + align-items: center; /* ✅ จัดให้อยู่กลางแนวนอน */ + justify-content: center; /* ✅ จัดให้อยู่กลางแนวตั้ง */ + text-align: center; /* ✅ ให้ข้อความตรงกลาง */ +} diff --git a/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.html b/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.html new file mode 100644 index 0000000..473a2e8 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.html @@ -0,0 +1,11 @@ +
+ + @if (mode == "default") { + + } @else if(mode == "forgot-password"){ + + } + +
diff --git a/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.ts b/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.ts new file mode 100644 index 0000000..5918822 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/login-content/login-content.component.ts @@ -0,0 +1,140 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { GeneralService } from '../../services/generalservice'; +import { LoginForgotComponent } from '../../../app/component/login-forgot/login-forgot.component'; +import { LoginPageComponent } from '../../../app/component/login-page/login-page.component'; +import { finalize } from 'rxjs/operators'; + +@Component({ + selector: 'app-login-content', + standalone: false, + templateUrl: './login-content.component.html', + styleUrl: './login-content.component.css' +}) +export class LoginContentComponent implements OnInit { + @ViewChild(LoginForgotComponent) loginForgotComponent!: LoginForgotComponent; + @ViewChild(LoginPageComponent) loginPageComponent!: LoginPageComponent; + mode: 'forgot-password' | 'default' = 'default'; + + constructor( + private generalService: GeneralService, + private route: ActivatedRoute, + private router: Router + ) {} + + ngOnInit(): void { + let param = this.route.snapshot.paramMap.get('mode'); + + if (param === 'forgot-password') { + this.mode = 'forgot-password'; + } else { + // this.router.navigate(['/login']); // This can cause navigation loops + this.mode = 'default'; + } + } + + onSignInSubmit(value: any){ + const uri = '/api/login/login'; + const request = { + username: value.username, + password: value.password + }; + + if (this.loginPageComponent) { + this.loginPageComponent.busy = true; + this.loginPageComponent.message = ''; + } + + this.generalService.postRequest(uri, request) + .pipe( + finalize(() => { + if (this.loginPageComponent) { + this.loginPageComponent.busy = false; + } + }) + ).subscribe({ + next: (result: any) => { + if (result.code === '200' && result.data?.token) { + this.generalService.trowApi(result); + localStorage.setItem('access_token', result.data.token); + this.router.navigate(['main/dashboard']); + } else { + const errorMessage = result.message_th || result.message || 'Sign-in failed.'; + if (this.loginPageComponent) { + this.loginPageComponent.message = errorMessage; + } + this.generalService.trowApi(result); + } + }, + error: (error: any) => { + const errorMessage = error?.error?.message_th || error?.error?.message || 'An error occurred.'; + if (this.loginPageComponent) { + this.loginPageComponent.message = errorMessage; + } + this.generalService.trowApi(error.error || { message_th: 'เกิดข้อผิดพลาดไม่ทราบสาเหตุ' }); + } + }); + } + + + onOtpSendSubmit(value: any){ + let uri = '/api/login/otp/send'; + let request = { + email: value.email + // otp: value.otp + } + + this.loginForgotComponent.isLoading = true; + this.generalService.postRequest(uri, request) + .pipe( + // ✅ finalize จะทำงานทุกกรณี (ทั้ง success/error) + finalize(() => { + this.loginForgotComponent.isLoading = false; + }) + ).subscribe({ + next: (result: any) => { + if (result.code === '200') { + this.generalService.trowApi(result); + console.log(`✅ OTP ส่งไปที่ ${value.email}`); + } else { + console.warn('⚠️ ไม่สามารถส่ง OTP ได้:', result.message_th); + } + }, + error: (error: any) => { + this.loginForgotComponent.isSendOtp = false; + this.loginForgotComponent.isLoading = false; + this.generalService.trowApi(error); + console.error('❌ Error sending OTP:', error); + }, + complete: () => { + this.loginForgotComponent.isLoading = false; + this.loginForgotComponent.isSendOtp = true; + console.log('📨 OTP send request completed'); + } + }); + } + + onVerifySubmit(value: any){ + let uri = '/api/login/otp/verify'; + let request = { + email: value.email, + otp: value.otp + } + this.generalService.postRequest(uri, request).subscribe({ + next: (result: any) => { + if (result.code === '200') { + console.log(`OTP ส่งไปยืนยันสำเร็จ`); + } else { + console.warn('⚠️ ไม่สามารถส่ง OTP ได้:', result.message_th); + } + }, + error: (error: any) => { + console.error('❌ Error sending OTP:', error); + }, + complete: () => { + this.router.navigate(['/login']); + console.log('📨 OTP send request completed'); + } + }); + } +} diff --git a/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.css b/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.css new file mode 100644 index 0000000..e69de29 diff --git a/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.html b/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.html new file mode 100644 index 0000000..d680121 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.html @@ -0,0 +1 @@ + diff --git a/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.ts b/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.ts new file mode 100644 index 0000000..016c70a --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/main-dashboard-content/main-dashboard-content.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ChartConfiguration, ChartOptions } from 'chart.js'; +import { BaseChartDirective } from 'ng2-charts'; +import { GeneralService } from '../../services/generalservice'; + +@Component({ + selector: 'app-main-dashboard-content', + standalone: false, + templateUrl: './main-dashboard-content.component.html', + styleUrls: ['./main-dashboard-content.component.css'] +}) +export class MainDashboardContentComponent implements OnInit { + @ViewChild(BaseChartDirective) chart?: BaseChartDirective; + + public lineChartData: ChartConfiguration<'line'>['data'] = { + labels: [], + datasets: [ + { + data: [], + label: 'Revenue', + fill: true, + tension: 0.5, + borderColor: 'rgba(75,192,192,1)', + backgroundColor: 'rgba(75,192,192,0.2)' + } + ] + }; + + public lineChartOptions: ChartOptions<'line'> = { + responsive: true, + scales: { + y: { + beginAtZero: true + } + }, + plugins: { + legend: { + display: true, + }, + title: { + display: true, + text: 'Revenue Summary - Last 6 Months' + } + } + }; + + constructor(private generalService: GeneralService) {} + + ngOnInit(): void { + this.fetchChartData(); + } + + 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(); + } +} diff --git a/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.css b/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.css new file mode 100644 index 0000000..3d08045 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.css @@ -0,0 +1,11 @@ +.layout { + display: flex; + height: 100vh; + width: 100%; +} +.main-container { + flex: 1; + background: #f5f5f5; + overflow-y: auto; + padding: 20px; +} diff --git a/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.html b/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.html new file mode 100644 index 0000000..caeb126 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.html @@ -0,0 +1,9 @@ +
+ + + + +
+ +
+
diff --git a/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.ts b/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.ts new file mode 100644 index 0000000..c0ee53f --- /dev/null +++ b/accounting-ng-nuttakit/src/app/content/sidebar-content/sidebar-content.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-sidebar-content', + standalone: false, + templateUrl: './sidebar-content.component.html', + styleUrl: './sidebar-content.component.css' +}) +export class SidebarContentComponent { + +} diff --git a/accounting-ng-nuttakit/src/app/controls/login-control/login-control-routing.module.ts b/accounting-ng-nuttakit/src/app/controls/login-control/login-control-routing.module.ts new file mode 100644 index 0000000..7b041c9 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/controls/login-control/login-control-routing.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { LoginContentComponent } from '../../content/login-content/login-content.component'; + +const routes: Routes = [ + { path: '', component: LoginContentComponent }, + { path: ':mode', component: LoginContentComponent } // ตัวอย่าง param /login/reset + // { path: 'forgot-password', component: LoginContentComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class LoginControlRoutingModule { } diff --git a/accounting-ng-nuttakit/src/app/controls/login-control/login-control.module.ts b/accounting-ng-nuttakit/src/app/controls/login-control/login-control.module.ts new file mode 100644 index 0000000..e4298fd --- /dev/null +++ b/accounting-ng-nuttakit/src/app/controls/login-control/login-control.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LoginContentComponent } from '../../content/login-content/login-content.component'; +import { LoginControlRoutingModule } from './login-control-routing.module'; +import { LoginPageComponent } from '../../component/login-page/login-page.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { LoginForgotComponent } from '../../component/login-forgot/login-forgot.component'; +// import { AppModule } from '../../app.module'; + +@NgModule({ + declarations: [ + LoginContentComponent, + LoginPageComponent, + LoginForgotComponent + ], + imports: [ + CommonModule, + ReactiveFormsModule, + FontAwesomeModule, + // AppModule, + LoginControlRoutingModule + ] +}) +export class LoginControlModule { } diff --git a/accounting-ng-nuttakit/src/app/controls/main-control/main-control-routing.module.ts b/accounting-ng-nuttakit/src/app/controls/main-control/main-control-routing.module.ts new file mode 100644 index 0000000..632626a --- /dev/null +++ b/accounting-ng-nuttakit/src/app/controls/main-control/main-control-routing.module.ts @@ -0,0 +1,27 @@ +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'; + + + +const routes: Routes = [ + { path: 'dashboard', component: MainDashboardContentComponent }, + // children: [ + // { + // path: 'dashboard', + // // loadChildren: () => import('./controls/profile-control/profile-control.module').then(m => m.ProfileControlModule) + // }, + // { path: 'report', component: MainReportComponent }, + // { path: '', redirectTo: 'profile', pathMatch: 'full' } + // ] + + { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, + { path: '**', redirectTo: 'dashboard' } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class MainControlRoutingModule { } diff --git a/accounting-ng-nuttakit/src/app/controls/main-control/main-control.module.ts b/accounting-ng-nuttakit/src/app/controls/main-control/main-control.module.ts new file mode 100644 index 0000000..b2cf561 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/controls/main-control/main-control.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +// import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { MainControlRoutingModule } from './main-control-routing.module'; + +import { ReactiveFormsModule } from '@angular/forms'; + +import { MainDashboardComponent } from '../../component/main-dashboard/main-dashboard.component'; +import { MainDashboardContentComponent } from '../../content/main-dashboard-content/main-dashboard-content.component'; + +// import { MainReportComponent } from '../../component/main-report/main-report.component'; + + + +@NgModule({ + declarations: [ + MainDashboardComponent, + MainDashboardContentComponent + // MainReportComponent + ], + imports: [ + CommonModule, + MainControlRoutingModule, + ReactiveFormsModule + // BrowserAnimationsModule + ] +}) +export class MainControlModule { } diff --git a/accounting-ng-nuttakit/src/app/services/auth.guard.ts b/accounting-ng-nuttakit/src/app/services/auth.guard.ts new file mode 100644 index 0000000..ec66571 --- /dev/null +++ b/accounting-ng-nuttakit/src/app/services/auth.guard.ts @@ -0,0 +1,14 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; + +export const authGuard: CanActivateFn = (route, state) => { + const router = inject(Router); + const accessToken = localStorage.getItem('access_token'); + + if (accessToken) { + return true; + } else { + router.navigate(['/login']); + return false; + } +}; diff --git a/accounting-ng-nuttakit/src/app/services/generalservice.ts b/accounting-ng-nuttakit/src/app/services/generalservice.ts new file mode 100644 index 0000000..0d5376c --- /dev/null +++ b/accounting-ng-nuttakit/src/app/services/generalservice.ts @@ -0,0 +1,138 @@ +import { ToastrService } from 'ngx-toastr'; +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class GeneralService { + + + private baseUrl = environment.apiBaseUrl; + private mode = environment.production; + + + constructor( + private http: HttpClient, + private toastr: ToastrService + ) {} + + // Default header + private getHttpOptions() { + const token = localStorage.getItem('access_token'); + const headers = new HttpHeaders({ + 'Content-Type': 'application/json', + ...(token ? { 'Authorization': `Bearer ${token}` } : {}) + }); + return { headers }; + } + + // Log ต้นแบบ + // private logRequest(method: string, url: string, body?: any) { + // if (this.mode === 'development') { + // console.log(`📡 [${method}] ${url}`, body || ''); + // } + // } + + + // Helper: wrap body ให้มี request ครอบเสมอ + private wrapRequestBody(body: any): any { + // ถ้ามี request อยู่แล้ว จะไม่ซ้ำ + if (body && body.request) { + return body; + } + return { request: body ?? {} }; + } + + // POST Request + postRequest(uri: string, body: any): Observable { + const payload = this.wrapRequestBody(body); + const fullUrl = `${this.baseUrl}${uri}`; + return this.http.post(fullUrl, payload, this.getHttpOptions()).pipe( + map((res: any) => res), + catchError((error: any) => { + console.error('❌ [POST Request Error]:', error); + return throwError(() => error); + }) + ); + } + + // GET Request + getRequest(uri: string): Observable { + const fullUrl = `${this.baseUrl}${uri}`; + return this.http.get(fullUrl, this.getHttpOptions()).pipe( + map((res: any) => res), + catchError((error: any) => { + console.error('❌ [GET Request Error]:', error); + return throwError(() => error); + }) + ); + } + + // PUT Request + putRequest(uri: string, body: any): Observable { + const payload = this.wrapRequestBody(body); + const fullUrl = `${this.baseUrl}${uri}`; + return this.http.put(fullUrl, payload, this.getHttpOptions()).pipe( + map((res: any) => res), + catchError((error: any) => { + console.error('❌ [PUT Request Error]:', error); + return throwError(() => error); + }) + ); + } + + // DELETE Request + deleteRequest(uri: string, body?: any): Observable { + const payload = this.wrapRequestBody(body); + const fullUrl = `${this.baseUrl}${uri}`; + return this.http.delete(fullUrl, { ...this.getHttpOptions(), body: payload }).pipe( + map((res: any) => res), + catchError((error: any) => { + console.error('❌ [DELETE Request Error]:', error); + return throwError(() => error); + }) + ); + } + + // showToast(type: 'success' | 'error' | 'warning' | 'info', message: string, title?: string) { + // const options = { + // positionClass: 'toast-top-right', + // timeOut: 3000, + // progressBar: true, + // progressAnimation: 'decreasing', + // toastClass: 'ngx-toastr bg-white bg-opacity-90 shadow-lg border' + // }; + // this.toastr[type](message, title || type.toUpperCase(), options); + // } + + + trowApi(result: any){ + const code = result?.code ?? 500; + const msg = result?.message ?? 'unknow'; + const msgTh = result?.message_th ?? 'unknow'; + + if(code == 200){ + this.toastr.success(`${msgTh || msg}`,'success',{ + positionClass: 'toast-top-right', + timeOut: 2500, + progressBar: true, + progressAnimation: 'decreasing', + toastClass: + 'ngx-toastr success-toast bg-white bg-opacity-90 text-green-700 shadow-lg' + }); + } else { + this.toastr.error(`${msgTh || msg}`,'error',{ + positionClass: 'toast-top-right', + timeOut: 3500, + progressBar: true, + progressAnimation: 'decreasing', + toastClass: + 'ngx-toastr error-toast bg-white bg-opacity-90 text-red-700 shadow-lg' + }); + } + } +} diff --git a/accounting-ng-nuttakit/src/environments/environment.development.ts b/accounting-ng-nuttakit/src/environments/environment.development.ts new file mode 100644 index 0000000..d0503a7 --- /dev/null +++ b/accounting-ng-nuttakit/src/environments/environment.development.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + apiBaseUrl: 'http://localhost:8000' +}; diff --git a/accounting-ng-nuttakit/src/environments/environment.ts b/accounting-ng-nuttakit/src/environments/environment.ts new file mode 100644 index 0000000..f3e8a81 --- /dev/null +++ b/accounting-ng-nuttakit/src/environments/environment.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + apiBaseUrl: 'https://api.nuttakit.work' +}; diff --git a/accounting-ng-nuttakit/src/index.html b/accounting-ng-nuttakit/src/index.html new file mode 100644 index 0000000..c6f97e2 --- /dev/null +++ b/accounting-ng-nuttakit/src/index.html @@ -0,0 +1,20 @@ + + + + + AccountingNgNuttakit + + + + + + + + + + + + + + + diff --git a/accounting-ng-nuttakit/src/main.ts b/accounting-ng-nuttakit/src/main.ts new file mode 100644 index 0000000..6d1f71d --- /dev/null +++ b/accounting-ng-nuttakit/src/main.ts @@ -0,0 +1,7 @@ +import { platformBrowser } from '@angular/platform-browser'; +import { AppModule } from './app/app.module'; + +platformBrowser().bootstrapModule(AppModule, { + ngZoneEventCoalescing: true, +}) + .catch(err => console.error(err)); diff --git a/accounting-ng-nuttakit/src/styles.css b/accounting-ng-nuttakit/src/styles.css new file mode 100644 index 0000000..ddfe0d5 --- /dev/null +++ b/accounting-ng-nuttakit/src/styles.css @@ -0,0 +1,221 @@ +@import "tailwindcss"; + +/* Global base styles for the app. Keep lightweight and self-contained so + the login component can reliably fill the viewport without producing + an outer page scrollbar. */ + +/* Force ngx-toastr container to be fixed to the viewport and positioned correctly */ +.toast-container.toast-top-right { + position: fixed !important; + top: 12px !important; + right: 12px !important; + z-index: 999999 !important; +} + +/* Make sure the page and app root occupy full height so 100vh aligns */ +html, body, app-root { + height: 100%; + min-height: 100%; +} +/* เริ่มต้น: สำหรับ Desktop */ +.login-mobile { + width: 415px; +} + +/* ถ้าเป็น Mobile (<=768px) ให้ลบ width ออก */ +@media (max-width: 768px) { + .login-mobile { + width: auto !important; + } +} + +@media (max-width: 768px) { + .sidebar { + position: fixed; + top: 0; + left: 0; + height: 100%; + z-index: 50; + transition: transform 0.3s ease-in-out; + } +} +/* ✅ Toast Custom Style */ +.ngx-toastr { + border-radius: 8px !important; + backdrop-filter: blur(6px); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12); + font-family: "Segoe UI", Roboto, sans-serif; + padding: 12px 16px !important; + min-width: 260px; + transition: all 0.3s ease-in-out; +} + +.success-toast { + background: rgba(255, 255, 255, 0.8) !important; + color: #15803d !important; + border-left: 5px solid #16a34a; +} + +.error-toast { + background: rgba(239, 68, 68, 0.8) !important; + color: #fff !important; + border-left: 5px solid #dc2626; +} + +.toast-title { + font-weight: 600 !important; + margin-bottom: 2px; +} + +.toast-message { + font-size: 14px; +} + + +/* sensible default box model */ +*, *::before, *::after { box-sizing: border-box; } + +body { + margin: 0; + font-family: "Kanit", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* Prevent the browser from showing a scroll bar for the page itself; + the login card will scroll internally if needed. */ +} + +/* Simple utilities used by nested components in this workspace */ +.content-box { + border: 2px solid black; + padding: 10px; + margin: 20px; +} + +.comp-box { + border: 1px solid #555; + border-radius: 8px; + padding: 10px; + margin: 10px; + /* Use flex centering so nested components (like the login widget) + are centered without forcing the document to scroll. */ + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; +} + +/* If the project uses Bootstrap, the Bootstrap utilities will still apply. + These local utility rules only ensure a consistent appearance if Bootstrap + isn't available. */ + + + + + +.kanit-thin { + font-family: "Kanit", sans-serif; + font-weight: 100; + font-style: normal; +} + +.kanit-extralight { + font-family: "Kanit", sans-serif; + font-weight: 200; + font-style: normal; +} + +.kanit-light { + font-family: "Kanit", sans-serif; + font-weight: 300; + font-style: normal; +} + +.kanit-regular { + font-family: "Kanit", sans-serif; + font-weight: 400; + font-style: normal; +} + +.kanit-medium { + font-family: "Kanit", sans-serif; + font-weight: 500; + font-style: normal; +} + +.kanit-semibold { + font-family: "Kanit", sans-serif; + font-weight: 600; + font-style: normal; +} + +.kanit-bold { + font-family: "Kanit", sans-serif; + font-weight: 700; + font-style: normal; +} + +.kanit-extrabold { + font-family: "Kanit", sans-serif; + font-weight: 800; + font-style: normal; +} + +.kanit-black { + font-family: "Kanit", sans-serif; + font-weight: 900; + font-style: normal; +} + +.kanit-thin-italic { + font-family: "Kanit", sans-serif; + font-weight: 100; + font-style: italic; +} + +.kanit-extralight-italic { + font-family: "Kanit", sans-serif; + font-weight: 200; + font-style: italic; +} + +.kanit-light-italic { + font-family: "Kanit", sans-serif; + font-weight: 300; + font-style: italic; +} + +.kanit-regular-italic { + font-family: "Kanit", sans-serif; + font-weight: 400; + font-style: italic; +} + +.kanit-medium-italic { + font-family: "Kanit", sans-serif; + font-weight: 500; + font-style: italic; +} + +.kanit-semibold-italic { + font-family: "Kanit", sans-serif; + font-weight: 600; + font-style: italic; +} + +.kanit-bold-italic { + font-family: "Kanit", sans-serif; + font-weight: 700; + font-style: italic; +} + +.kanit-extrabold-italic { + font-family: "Kanit", sans-serif; + font-weight: 800; + font-style: italic; +} + +.kanit-black-italic { + font-family: "Kanit", sans-serif; + font-weight: 900; + font-style: italic; +} diff --git a/accounting-ng-nuttakit/tsconfig.app.json b/accounting-ng-nuttakit/tsconfig.app.json new file mode 100644 index 0000000..3775b37 --- /dev/null +++ b/accounting-ng-nuttakit/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/accounting-ng-nuttakit/tsconfig.json b/accounting-ng-nuttakit/tsconfig.json new file mode 100644 index 0000000..5525117 --- /dev/null +++ b/accounting-ng-nuttakit/tsconfig.json @@ -0,0 +1,27 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/accounting-ng-nuttakit/tsconfig.spec.json b/accounting-ng-nuttakit/tsconfig.spec.json new file mode 100644 index 0000000..5fb748d --- /dev/null +++ b/accounting-ng-nuttakit/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/accounting-ng-nuttakit/vite.config.ts b/accounting-ng-nuttakit/vite.config.ts new file mode 100644 index 0000000..77e4691 --- /dev/null +++ b/accounting-ng-nuttakit/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite'; +import { angular } from '@analogjs/vite-plugin-angular'; + +export default defineConfig({ + plugins: [angular()], + server: { + host: '0.0.0.0', + allowedHosts: ['accounting.nuttakit.work'], // 👈 เพิ่มโดเมนของคุณ + }, +});