- catching
All checks were successful
Build Docker Image / Preparing Dependecies (push) Successful in 5s
All checks were successful
Build Docker Image / Preparing Dependecies (push) Successful in 5s
This commit is contained in:
14747
ng-ttc-frontend/package-lock.json
generated
14747
ng-ttc-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,41 +1,91 @@
|
|||||||
{
|
{
|
||||||
"name": "ng-ttc-frontend",
|
"name": "ng-ttc-frontend",
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
|
"main": "electron/main.js",
|
||||||
|
"author": "Nuttakit",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"watch": "ng build --watch --configuration development",
|
"watch": "ng build --watch --configuration development",
|
||||||
"test": "ng test"
|
"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/ng-ttc-frontend/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,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^19.0.0",
|
"@angular/animations": "^20.3.10",
|
||||||
"@angular/common": "^19.0.0",
|
"@angular/common": "^20.3.10",
|
||||||
"@angular/compiler": "^19.0.0",
|
"@angular/compiler": "^20.3.10",
|
||||||
"@angular/core": "^19.0.0",
|
"@angular/core": "^20.3.10",
|
||||||
"@angular/forms": "^19.0.0",
|
"@angular/forms": "^20.3.10",
|
||||||
"@angular/platform-browser": "^19.0.0",
|
"@angular/platform-browser": "^20.3.10",
|
||||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
"@angular/platform-browser-dynamic": "^20.3.10",
|
||||||
"@angular/router": "^19.0.0",
|
"@angular/router": "^20.3.10",
|
||||||
"@tailwindcss/postcss": "^4.1.17",
|
"@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",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
|
"ng2-charts": "^6.0.1",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.16",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^19.0.7",
|
"@angular/build": "^20.3.9",
|
||||||
"@angular/cli": "^19.0.7",
|
"@angular/cli": "^20.3.9",
|
||||||
"@angular/compiler-cli": "^19.0.0",
|
"@angular/compiler-cli": "^20.3.10",
|
||||||
|
"@capacitor/cli": "latest",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"jasmine-core": "~5.4.0",
|
"cross-env": "^10.1.0",
|
||||||
|
"electron": "^39.0.0",
|
||||||
|
"electron-builder": "^26.0.12",
|
||||||
|
"jasmine-core": "~5.6.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
"karma-coverage": "~2.2.0",
|
"karma-coverage": "~2.2.0",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"typescript": "~5.6.2"
|
"ngx-toastr": "^19.1.0",
|
||||||
|
"postcss": "^8.5.3",
|
||||||
|
"typescript": "~5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,48 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { SidebarContentComponent } from './content/sidebar-content/sidebar-content.component';
|
||||||
|
import { LicensePrivacyTermsComponent } from './component/license-privacy-terms/license-privacy-terms.component';
|
||||||
|
// import { authGuard } from './services/auth.guard';
|
||||||
|
|
||||||
const routes: Routes = [];
|
const routes: Routes = [
|
||||||
|
|
||||||
|
{ path: 'login', loadChildren: () => import('./controls/login-control/login-control.module').then(m => m.LoginControlModule) },
|
||||||
|
|
||||||
|
{ path: 'license', component: LicensePrivacyTermsComponent},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: 'main',
|
||||||
|
component: SidebarContentComponent,
|
||||||
|
canActivate: [
|
||||||
|
// authGuard
|
||||||
|
],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./controls/main-control/main-control.module').then(
|
||||||
|
(m) => m.MainControlModule
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: 'report',
|
||||||
|
// loadChildren: () =>
|
||||||
|
// import('./controls/report-control/report-control.module').then(
|
||||||
|
// (m) => m.ReportControlModule
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// {path: 'license' , component: LicensePrivacyTermsComponent}
|
||||||
|
|
||||||
|
{ path: '', redirectTo: 'login', pathMatch: 'full' },
|
||||||
|
|
||||||
|
{ path: '**', redirectTo: 'login' }
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule {}
|
||||||
|
|||||||
12
ng-ttc-frontend/src/app/config/caching.config.ts
Normal file
12
ng-ttc-frontend/src/app/config/caching.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const CACHEABLE_URLS = {
|
||||||
|
GET: [
|
||||||
|
// Add GET URIs here that you want to cache
|
||||||
|
// e.g., '/api/data'
|
||||||
|
],
|
||||||
|
POST: [
|
||||||
|
'/api/web/accountingSetup',
|
||||||
|
'/api/nigga'
|
||||||
|
// Add POST URIs here that you want to cache
|
||||||
|
// e.g., '/api/search'
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@ import { MainControlRoutingModule } from './main-control-routing.module';
|
|||||||
|
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { MainDashboardComponent } from '../../component/main-dashboard/main-dashboard.component';
|
// import { MainDashboardComponent } from '../../component/main-dashboard/main-dashboard.component';
|
||||||
import { MainDashboardContentComponent } from '../../content/main-dashboard-content/main-dashboard-content.component';
|
import { MainDashboardContentComponent } from '../../content/main-dashboard-content/main-dashboard-content.component';
|
||||||
import { AccDateFormatPipe } from '../../pipe/dtmtodatetime.pipe';
|
import { AccDateFormatPipe } from '../../pipe/dtmtodatetime.pipe';
|
||||||
import { MainReportComponent } from '../../component/main-report/main-report.component';
|
import { MainReportComponent } from '../../component/main-report/main-report.component';
|
||||||
@@ -17,7 +17,7 @@ import { MainReportComponent } from '../../component/main-report/main-report.com
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
MainDashboardComponent,
|
// MainDashboardComponent,
|
||||||
MainDashboardContentComponent,
|
MainDashboardContentComponent,
|
||||||
MainReportComponent,
|
MainReportComponent,
|
||||||
AccDateFormatPipe
|
AccDateFormatPipe
|
||||||
|
|||||||
94
ng-ttc-frontend/src/app/interfaces/dashboard.interface.ts
Normal file
94
ng-ttc-frontend/src/app/interfaces/dashboard.interface.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
export interface IStateDrop {
|
||||||
|
income: IDropAct[];
|
||||||
|
expense: IDropAct[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDropAct {
|
||||||
|
dtlnam?: string,
|
||||||
|
dtlcod?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IActData {
|
||||||
|
actseq?: number,
|
||||||
|
actnum?: number,
|
||||||
|
acttyp?: string,
|
||||||
|
acttypnam?: string,
|
||||||
|
actcatnam?: string,
|
||||||
|
actqty?: number,
|
||||||
|
actcmt?: string,
|
||||||
|
actacpdtm?: string
|
||||||
|
}
|
||||||
|
export interface IStateResultResponse {
|
||||||
|
data: IStateDrop;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStateResultResponse {
|
||||||
|
data: IStateDrop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface IActSumData {
|
||||||
|
summary: IActSummary;
|
||||||
|
pie: IActPie;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IActSummary {
|
||||||
|
totalIncome: string;
|
||||||
|
totalExpense: string;
|
||||||
|
netProfit: string;
|
||||||
|
profitRate: string;
|
||||||
|
adjustedProfitRate: string;
|
||||||
|
period: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IActPie {
|
||||||
|
expense: IActCategory[];
|
||||||
|
income: IActCategory[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IActCategory {
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
percent: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ข้อมูลสินค้าหลัก
|
||||||
|
// export interface IProduct {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// price: number;
|
||||||
|
// category: string;
|
||||||
|
// inStock: boolean;
|
||||||
|
// description?: string; // optional
|
||||||
|
// imageUrl?: string;
|
||||||
|
// tags: string[];
|
||||||
|
// createdAt: Date;
|
||||||
|
// updatedAt: Date;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ข้อมูลสินค้าแบบย่อ (ใช้ในรายการ)
|
||||||
|
// export interface IProductSummary {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// price: number;
|
||||||
|
// imageUrl?: string;
|
||||||
|
// inStock: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ข้อมูลสำหรับฟอร์ม
|
||||||
|
// export interface IProductForm {
|
||||||
|
// name: string;
|
||||||
|
// price: number;
|
||||||
|
// category: string;
|
||||||
|
// description?: string;
|
||||||
|
// inStock: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ข้อมูลการจัดหมวดหมู่
|
||||||
|
// export interface IProductCategory {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// parentId?: string;
|
||||||
|
// productCount: number;
|
||||||
|
// }
|
||||||
24
ng-ttc-frontend/src/app/pipe/dtmtodatetime.pipe.ts
Normal file
24
ng-ttc-frontend/src/app/pipe/dtmtodatetime.pipe.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'dtmtodatetime',
|
||||||
|
standalone: false
|
||||||
|
})
|
||||||
|
export class AccDateFormatPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: string | number): string {
|
||||||
|
if (value === null || value === undefined) return '';
|
||||||
|
|
||||||
|
const str = value.toString();
|
||||||
|
if (str.length !== 12) return str;
|
||||||
|
|
||||||
|
const dd = str.slice(0, 2);
|
||||||
|
const mm = str.slice(2, 4);
|
||||||
|
const yyyy = str.slice(4, 8);
|
||||||
|
const hh = str.slice(8, 10);
|
||||||
|
const min = str.slice(10, 12);
|
||||||
|
|
||||||
|
return `${dd}/${mm}/${yyyy} ${hh}:${min}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
ng-ttc-frontend/src/app/services/auth.guard.ts
Normal file
32
ng-ttc-frontend/src/app/services/auth.guard.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { inject } from '@angular/core';
|
||||||
|
import { CanActivateFn, Router } from '@angular/router';
|
||||||
|
import { jwtDecode } from 'jwt-decode';
|
||||||
|
|
||||||
|
export const authGuard: CanActivateFn = (route, state) => {
|
||||||
|
const router = inject(Router);
|
||||||
|
const accessToken = localStorage.getItem('access_token');
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
try {
|
||||||
|
const decodedToken: any = jwtDecode(accessToken);
|
||||||
|
const currentTime = Date.now() / 1000;
|
||||||
|
|
||||||
|
if (decodedToken.exp < currentTime) {
|
||||||
|
// Token expired
|
||||||
|
localStorage.removeItem('access_token');
|
||||||
|
router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
// Error decoding token
|
||||||
|
localStorage.removeItem('access_token');
|
||||||
|
router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
52
ng-ttc-frontend/src/app/services/caching.interceptor.ts
Normal file
52
ng-ttc-frontend/src/app/services/caching.interceptor.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
import { CachingService } from './caching.service';
|
||||||
|
import { CACHEABLE_URLS } from '../config/caching.config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CachingInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
constructor(private cache: CachingService) {}
|
||||||
|
|
||||||
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
if (!this.isCacheable(req)) {
|
||||||
|
return next.handle(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedResponse = this.cache.get(this.getCacheKey(req));
|
||||||
|
if (cachedResponse) {
|
||||||
|
return of(cachedResponse.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(req).pipe(
|
||||||
|
tap(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
this.cache.put(this.getCacheKey(req), event.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isCacheable(req: HttpRequest<any>): boolean {
|
||||||
|
if (req.method === 'GET') {
|
||||||
|
return CACHEABLE_URLS.GET.some(url => req.urlWithParams.includes(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
return CACHEABLE_URLS.POST.some(url => req.urlWithParams.includes(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCacheKey(req: HttpRequest<any>): string {
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
return req.urlWithParams + JSON.stringify(req.body);
|
||||||
|
}
|
||||||
|
return req.urlWithParams;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
ng-ttc-frontend/src/app/services/caching.service.ts
Normal file
41
ng-ttc-frontend/src/app/services/caching.service.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpResponse } from '@angular/common/http';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CachingService {
|
||||||
|
private cache = new Map<string, [Date, HttpResponse<any>]>();
|
||||||
|
private cacheDurationInMs = 600000; // 5 minutes
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
get(key: string): HttpResponse<any> | null {
|
||||||
|
const tuple = this.cache.get(key);
|
||||||
|
if (!tuple) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expires = tuple[0];
|
||||||
|
const httpResponse = tuple[1];
|
||||||
|
|
||||||
|
// Don't observe expired keys
|
||||||
|
const now = new Date();
|
||||||
|
if (expires && expires.getTime() < now.getTime()) {
|
||||||
|
this.cache.delete(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
put(key: string, value: HttpResponse<any>): void {
|
||||||
|
const expires = new Date();
|
||||||
|
expires.setMilliseconds(expires.getMilliseconds() + this.cacheDurationInMs);
|
||||||
|
this.cache.set(key, [expires, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.cache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
149
ng-ttc-frontend/src/app/services/generalservice.ts
Normal file
149
ng-ttc-frontend/src/app/services/generalservice.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
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<any> {
|
||||||
|
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) => {
|
||||||
|
const response = error?.error;
|
||||||
|
console.error('❌ [POST Request Error]:', error);
|
||||||
|
return throwError(() => ({
|
||||||
|
status: error.status,
|
||||||
|
code: response?.code ?? '500',
|
||||||
|
message: response?.message ?? 'Internal Server Error',
|
||||||
|
message_th: response?.message_th ?? 'เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์'
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET Request
|
||||||
|
getRequest(uri: string): Observable<any> {
|
||||||
|
const fullUrl = `${this.baseUrl}${uri}`;
|
||||||
|
return this.http.get(fullUrl, this.getHttpOptions()).pipe(
|
||||||
|
map((res: any) => res),
|
||||||
|
catchError((error: any) => {
|
||||||
|
const response = error?.error;
|
||||||
|
console.error('❌ [GET Request Error]:', error);
|
||||||
|
return throwError(() => ({
|
||||||
|
status: error.status,
|
||||||
|
code: response?.code ?? '500',
|
||||||
|
message: response?.message ?? 'Internal Server Error',
|
||||||
|
message_th: response?.message_th ?? 'เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์'
|
||||||
|
})); })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT Request
|
||||||
|
putRequest(uri: string, body: any): Observable<any> {
|
||||||
|
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<any> {
|
||||||
|
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'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { IDropAct, IStateDrop, IActData, IActSumData } from '../../interfaces/dashboard.interface';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DashboardStateService {
|
||||||
|
// ประกาศ BehaviorSubject ด้วย Interface
|
||||||
|
private dashboardState = new BehaviorSubject<IStateDrop | null>(null);
|
||||||
|
private accounttingState = new BehaviorSubject<IActData[] | null>(null);
|
||||||
|
private actsumState = new BehaviorSubject<IActSumData | null>(null);
|
||||||
|
|
||||||
|
// ส่ง Observable ไปให้ components subscribe
|
||||||
|
getStateResult(): Observable<IStateDrop | null> {
|
||||||
|
return this.dashboardState.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// เซ็ท state
|
||||||
|
setStateResult(dashboard: IStateDrop): void {
|
||||||
|
this.dashboardState.next(dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setStateAccountResult(dashboard: IActData[]): void {
|
||||||
|
this.accounttingState.next(dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setStateSumResult(sumResult: IActSumData): void {
|
||||||
|
this.actsumState.next(sumResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// เคลียร์ state
|
||||||
|
clearState(): void {
|
||||||
|
this.dashboardState.next(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateAccountResult(): Observable<IActData[] | null> {
|
||||||
|
return this.accounttingState.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getStateSumResult(): Observable<IActSumData | null> {
|
||||||
|
return this.actsumState.asObservable();
|
||||||
|
}
|
||||||
|
// ดึงค่า current state (ไม่ใช่ observable)
|
||||||
|
// getCurrentState(): IDropAct | null {
|
||||||
|
// return this.dashboardState.value;
|
||||||
|
// }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user