diff --git a/UI/package-lock.json b/UI/package-lock.json
index 8ac828b..e5503af 100644
--- a/UI/package-lock.json
+++ b/UI/package-lock.json
@@ -14,6 +14,9 @@
"@angular/forms": "^21.0.0",
"@angular/platform-browser": "^21.0.0",
"@angular/router": "^21.0.0",
+ "@ngrx/effects": "^21.0.1",
+ "@ngrx/store": "^21.0.1",
+ "@ngrx/store-devtools": "^21.0.1",
"@primeuix/themes": "^2.0.2",
"@tailwindcss/postcss": "^4.1.17",
"file-saver": "^2.0.5",
@@ -2611,6 +2614,47 @@
"@tybys/wasm-util": "^0.10.1"
}
},
+ "node_modules/@ngrx/effects": {
+ "version": "21.0.1",
+ "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-21.0.1.tgz",
+ "integrity": "sha512-hSdpToAiSYa5FJ/CAygQHpnCaF2S1HO7q/57ob3XvNTWmkofa0VqI/IIe4W57bojh2YOWCJ91SCn3kAjymaV3g==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^21.0.0",
+ "@ngrx/store": "21.0.1",
+ "rxjs": "^6.5.3 || ^7.5.0"
+ }
+ },
+ "node_modules/@ngrx/store": {
+ "version": "21.0.1",
+ "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-21.0.1.tgz",
+ "integrity": "sha512-2hGnw/c5o8nmKzyx7TrUUM7FXjE2zqjX0EF+wLbw9Oy/L+VdCmx+ZI1BFjuAR4B8PKEWHG2KSbOM13SMNkpZiA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^21.0.0",
+ "rxjs": "^6.5.3 || ^7.5.0"
+ }
+ },
+ "node_modules/@ngrx/store-devtools": {
+ "version": "21.0.1",
+ "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-21.0.1.tgz",
+ "integrity": "sha512-G9fO7CFwYUpz8+JZ9uny+lVJ7iv6PcFJDg+jae5CCrAUIiflnR8gbwD8exAd2AODpxPCpmnSojuLNpLDAcwGzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^21.0.0",
+ "@ngrx/store": "21.0.1",
+ "rxjs": "^6.5.3 || ^7.5.0"
+ }
+ },
"node_modules/@npmcli/agent": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz",
diff --git a/UI/package.json b/UI/package.json
index fd88027..02b8212 100644
--- a/UI/package.json
+++ b/UI/package.json
@@ -29,6 +29,9 @@
"@angular/forms": "^21.0.0",
"@angular/platform-browser": "^21.0.0",
"@angular/router": "^21.0.0",
+ "@ngrx/effects": "^21.0.1",
+ "@ngrx/store": "^21.0.1",
+ "@ngrx/store-devtools": "^21.0.1",
"@primeuix/themes": "^2.0.2",
"@tailwindcss/postcss": "^4.1.17",
"file-saver": "^2.0.5",
@@ -51,4 +54,4 @@
"typescript": "~5.9.2",
"vitest": "^4.0.8"
}
-}
+}
\ No newline at end of file
diff --git a/UI/src.rar b/UI/src.rar
new file mode 100644
index 0000000..2fe2df1
Binary files /dev/null and b/UI/src.rar differ
diff --git a/UI/src/app/app.config.ts b/UI/src/app/app.config.ts
index dd21d28..a86d774 100644
--- a/UI/src/app/app.config.ts
+++ b/UI/src/app/app.config.ts
@@ -8,23 +8,57 @@ import { routes } from './app.routes';
import MyPreset from './mythem';
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
+import { provideStore } from '@ngrx/store';
+import { provideStoreDevtools } from '@ngrx/store-devtools';
+import { isDevMode } from '@angular/core';
+import { provideEffects } from '@ngrx/effects';
+import { staffEffects,staffsReducer } from './state';
+
export const appConfig: ApplicationConfig = {
providers: [
+ provideStore({
+ myStaffs: staffsReducer,
+ }),
+ provideStoreDevtools({
+ maxAge: 25,
+ logOnly: !isDevMode()
+ }),
provideBrowserGlobalErrorListeners(),
- MessageService, ConfirmationService,
- provideAppInitializer(initializeApp()),
- provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
+ MessageService, ConfirmationService,
+ provideAppInitializer(initializeApp()),
+ provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
provideRouter(routes, withComponentInputBinding()),
- providePrimeNG({
- theme: {
- preset: MyPreset,
- options: {
- cssLayer: {
+ providePrimeNG({
+ theme: {
+ preset: MyPreset,
+ options: {
+ cssLayer: {
name: 'primeng',
order: 'theme, base, primeng'
}
- }
}
- }),
- ]
+ }
+ }),
+ provideEffects([staffEffects])
+]
};
+
+
+
+/*
+
+import { Store } from '@ngrx/store';
+ private readonly store = inject(Store);
+ protected books = this.store.selectSignal(selectBooks);
+ protected onAdd(id: number) {
+ this.store.dispatch(StaffsActions.addStaff({ id }));
+ }
+
+ protected onRemove(id: number) {
+ this.store.dispatch(StaffsActions.removeStaff({ id }));
+ }
+ on ngOnInit() {
+ use it normal get from API first
+and subscribe( (x) => this.store.dispatch(StaffApiAction.LoadStaffList({x})))
+}
+*/
\ No newline at end of file
diff --git a/UI/src/app/person/person.edit.ts b/UI/src/app/person/person.edit.ts
index d0ff381..17d1ac5 100644
--- a/UI/src/app/person/person.edit.ts
+++ b/UI/src/app/person/person.edit.ts
@@ -85,15 +85,16 @@ export class PersonEdit implements OnInit, OnDestroy {
alive: [false], //Validators.required
fatherId:[0],
motherId:[0]
-
-
});
-constructor( private cdr: ChangeDetectorRef,public dialogService: DialogService,
- public ref: DynamicDialogRef, public config: DynamicDialogConfig,
-
- private router: Router, private route: ActivatedRoute,
- private appSetting: AppSettingService,
+constructor(
+ private cdr: ChangeDetectorRef,
+ public dialogService: DialogService,
+ public ref: DynamicDialogRef,
+ public config: DynamicDialogConfig,
+ private router: Router,
+ private route: ActivatedRoute,
+ private appSetting: AppSettingService,
) {
this.hostsite = this.appSetting.appSetting.attachment;
}
@@ -118,11 +119,8 @@ constructor( private cdr: ChangeDetectorRef,public dialogService: DialogService,
this.motherList.push(item);
else
this.fatherList.push(item);
-
}
- }
-
-
+ }
}
getClassForRequire(prev:string,name:string){
diff --git a/UI/src/app/staff/staff.component.html b/UI/src/app/staff/staff.component.html
index 898fd58..6a4b7dc 100644
--- a/UI/src/app/staff/staff.component.html
+++ b/UI/src/app/staff/staff.component.html
@@ -28,7 +28,8 @@
-
+
diff --git a/UI/src/app/staff/staff.component.ts b/UI/src/app/staff/staff.component.ts
index d40f117..01cd78d 100644
--- a/UI/src/app/staff/staff.component.ts
+++ b/UI/src/app/staff/staff.component.ts
@@ -2,13 +2,8 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDe
import { StaffView ,StaffSearch } from '../models';
-/* ngRx */
-/*
-import { Store, select } from '@ngrx/store';
-import * as validationActions from './store/actions/validationpoint.action';
-import * as validationSelector from './store/selectors/validationpoint.selector';
-import { appValidationState } from './store/reducers';
-*/
+import { Store } from '@ngrx/store';
+import { StaffsActions, StaffsApiActions } from '../state/staff.actions';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
@@ -19,6 +14,7 @@ import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
+import { selectStaffLoaded,selectState } from '../state/staff.selectors';
@Component({
selector: 'staff-list',
@@ -28,13 +24,16 @@ import { InputTextModule } from 'primeng/inputtext';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StaffComponent implements OnInit, OnDestroy{
-
+ private readonly store = inject(Store);
private subscription:Subscription = new Subscription();
firstname = '';
email = '';
lastname = '';
loading = false;
userList:StaffView[] = [];
+ appState = this.store.selectSignal(selectState);
+ users = this.appState().staffs;
+ staffloadyet = this.appState().staffloaded;
private cd = inject(ChangeDetectorRef);
msg ="[Staff component]";
@@ -99,25 +98,32 @@ export class StaffComponent implements OnInit, OnDestroy{
const canSearch = true; // this.canSearch();
if (canSearch)
{
- this.loading = true;
+
const criteria = this.getSearchCiteria();
this.staffService.searchCriteria = criteria;
- this.subscription.add(
- this.staffService.searchStaffs(criteria).subscribe( {
- next: result => {
- this.loading = false;
- // console.log(this.msg + "search load Data", result);
- this.userList = result.data;
- this.cd.detectChanges();
- },
- error: e => {
- const message = e || e.message;
- // this.toastr.error(message);
- this.loading = false;
- console.log("error ", e);
+ console.log("search function store staffload yet",this.staffloadyet, this.users);
+ if (this.staffloadyet != true) {
+ this.loading = true;
+ const loadStaff$ = this.staffService.searchStaffs(criteria);
+ this.subscription.add(
+ loadStaff$.subscribe( {
+ next: result => {
+
+ this.loading = false;
+
+ this.userList = result.data;
+ this.store.dispatch(StaffsApiActions.loadStaffSuccess({staffs:this.userList, staffLoad: true}));
+ this.cd.detectChanges();
+ },
+ error: e => {
+ const message = e || e.message;
+ // this.toastr.error(message);
+ this.loading = false;
+ console.log("error ", e);
+ }
+ })
+ );
}
- })
- );
}
}
newUser():void {
diff --git a/UI/src/app/state/app.state.ts b/UI/src/app/state/app.state.ts
new file mode 100644
index 0000000..4ae9696
--- /dev/null
+++ b/UI/src/app/state/app.state.ts
@@ -0,0 +1,6 @@
+import { StaffView } from '../models';
+
+export interface AppState {
+ staffs: Array;
+ staffloaded : boolean;
+}
\ No newline at end of file
diff --git a/UI/src/app/state/index.ts b/UI/src/app/state/index.ts
new file mode 100644
index 0000000..442b98a
--- /dev/null
+++ b/UI/src/app/state/index.ts
@@ -0,0 +1,5 @@
+export * from './app.state';
+export * from './staff.actions';
+export * from './staff.effects';
+export * from './staff.reducer';
+export * from './staff.selectors';
\ No newline at end of file
diff --git a/UI/src/app/state/staff.actions.ts b/UI/src/app/state/staff.actions.ts
new file mode 100644
index 0000000..9a63a2f
--- /dev/null
+++ b/UI/src/app/state/staff.actions.ts
@@ -0,0 +1,19 @@
+import { createActionGroup, emptyProps, props } from '@ngrx/store';
+import { StaffSearch, StaffView } from '../models';
+
+export const StaffsActions = createActionGroup({
+ source: 'Staffs',
+ events: {
+ 'Add Staff': props<{ staffId: number }>(),
+ 'Remove Staff': props<{ staffId: number }>(),
+ 'Load Staff': props<{criteria: StaffSearch}>(),
+ },
+});
+
+export const StaffsApiActions = createActionGroup({
+ source: 'Staffs API',
+ events: {
+ 'LoadStaffSuccess': props<{ staffs: Array; staffLoad: boolean }>(),
+ 'LoadStaffFail': props<{ error: string }>(),
+ },
+});
\ No newline at end of file
diff --git a/UI/src/app/state/staff.effects.ts b/UI/src/app/state/staff.effects.ts
new file mode 100644
index 0000000..5b1f1f9
--- /dev/null
+++ b/UI/src/app/state/staff.effects.ts
@@ -0,0 +1,29 @@
+import { Actions, createEffect, ofType } from '@ngrx/effects';
+import { catchError, filter, map, of, switchMap, exhaustMap } from 'rxjs';
+//import { concatLatestFrom } from '@ngrx/operators';
+import { HttpErrorResponse } from '@angular/common/http';
+import { inject, Injectable } from '@angular/core';
+import { Store } from '@ngrx/store';
+
+import { StaffsActions, StaffsApiActions } from './staff.actions';
+
+import { StaffService } from '../staff/staff.service';
+import { StaffSearch } from '../models';
+
+@Injectable()
+export class staffEffects {
+ private actions$: Actions = inject(Actions);
+ private staffService: StaffService = inject(StaffService);
+ private store: Store = inject(Store);
+
+ LoadStaffs$ = createEffect(() =>
+ this.actions$.pipe(
+ ofType(StaffsActions.loadStaff),
+ exhaustMap((action: {criteria:StaffSearch}) =>
+ this.staffService.searchStaffs(action.criteria).pipe(
+ map((response) => StaffsApiActions.loadStaffSuccess({staffs: response.data, staffLoad: true})),
+ catchError((err: HttpErrorResponse) => of(StaffsApiActions.loadStaffFail({ error: err.error})))
+ )
+ )
+ ));
+}
\ No newline at end of file
diff --git a/UI/src/app/state/staff.reducer.ts b/UI/src/app/state/staff.reducer.ts
new file mode 100644
index 0000000..f48c90c
--- /dev/null
+++ b/UI/src/app/state/staff.reducer.ts
@@ -0,0 +1,24 @@
+import { createReducer, on } from '@ngrx/store';
+
+import { StaffsApiActions } from './staff.actions';
+import { StaffView } from '../models';
+import { AppState } from './app.state';
+
+export const initialState: AppState ={
+ staffs:[],
+ staffloaded: false
+}
+
+export const staffsReducer = createReducer(
+ initialState,
+ on(StaffsApiActions.loadStaffSuccess, (
+ state, action) => {
+ return {
+ ...state,
+ staffs: action.staffs,
+ staffloaded: true
+
+ }
+ }
+)
+);
diff --git a/UI/src/app/state/staff.selectors.ts b/UI/src/app/state/staff.selectors.ts
new file mode 100644
index 0000000..fc6df81
--- /dev/null
+++ b/UI/src/app/state/staff.selectors.ts
@@ -0,0 +1,16 @@
+import { createSelector, createFeatureSelector } from '@ngrx/store';
+import { StaffView } from '../models';
+import { AppState } from './app.state';
+
+export const selectState = createFeatureSelector('myStaffs');
+
+// 2. Create a selector for a specific piece of state
+export const selectStaff = createSelector(
+ selectState,
+ (state: AppState) => state.staffs
+);
+
+// 2. Create a selector for a specific piece of state
+export const selectStaffLoaded = createSelector(
+ selectState,
+ (state: AppState) => state.staffloaded);