put in ignore file
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": {
|
||||
"@tailwindcss/postcss": {}
|
||||
}
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
Vendored
+42
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# FamilyTree
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.1.1.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"FamilyTree": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"browser": "src/main.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "5MB",
|
||||
"maximumError": "6MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "FamilyTree:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "FamilyTree:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular/build:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular/build:karma",
|
||||
"options": {
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
Generated
+10246
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "family-tree",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"prettier": {
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^20.1.0",
|
||||
"@angular/compiler": "^20.1.0",
|
||||
"@angular/core": "^20.1.0",
|
||||
"@angular/forms": "^20.1.0",
|
||||
"@angular/platform-browser": "^20.1.0",
|
||||
"@angular/router": "^20.1.0",
|
||||
"@primeuix/themes": "^1.2.1",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"moment": "^2.30.1",
|
||||
"postcss": "^8.5.6",
|
||||
"primeicons": "^7.0.0",
|
||||
"primeng": "^20.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tailwindcss-primeui": "^0.6.1",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^20.1.1",
|
||||
"@angular/cli": "^20.1.1",
|
||||
"@angular/compiler-cli": "^20.1.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.8.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",
|
||||
"typescript": "~5.8.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:5015",
|
||||
"attachment": "http://localhost/document/family"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Angular Routes" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="./index.html" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ApplicationConfig, provideAppInitializer, provideBrowserGlobalErrorListeners, provideZonelessChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
import { providePrimeNG } from 'primeng/config';
|
||||
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||
import { routes } from './app.routes';
|
||||
import MyPreset from './mythem';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners(),
|
||||
MessageService, ConfirmationService,
|
||||
provideAppInitializer(initializeApp()),
|
||||
provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
|
||||
provideZonelessChangeDetection(),
|
||||
provideAnimationsAsync(),
|
||||
providePrimeNG({
|
||||
theme: {
|
||||
preset: MyPreset,
|
||||
options: {
|
||||
cssLayer: {
|
||||
name: 'primeng',
|
||||
order: 'theme, base, primeng'
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
provideRouter(routes)
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
<p-toast position="bottom-center" />
|
||||
<app-toolbar> </app-toolbar>
|
||||
<router-outlet />
|
||||
<p-confirmDialog [style]="{width: '50vw'}"
|
||||
rejectButtonStyleClass="p-button-text"> </p-confirmDialog>
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { Login } from './login';
|
||||
import { StaffComponent, StaffEditComponent } from './staff';
|
||||
import { AuthGuard } from './route-guard';
|
||||
import { FamilyTree, FamilyList} from './person';
|
||||
export const routes: Routes = [
|
||||
{ path: '', redirectTo: 'login', pathMatch: 'full' },
|
||||
// { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},},
|
||||
{ path: 'login', component: Login},
|
||||
{ path: 'staff', component: StaffComponent},
|
||||
{ path: 'person', component: FamilyList},
|
||||
{ path: 'familytree', component: FamilyTree},
|
||||
{ path: 'staff/:id', component: StaffEditComponent},
|
||||
|
||||
];
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { ToolbarComponent } from './toolbar/toolbar.component';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.css'
|
||||
})
|
||||
export class App {
|
||||
protected readonly title = signal('FamilyTree');
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './login';
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.btnloginRigh {
|
||||
display: flex;
|
||||
flex-direction:reverse;
|
||||
justify-content:flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
.myerror {
|
||||
background-color: red;
|
||||
color: white;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<div class="flex justify-center items-center h-full">
|
||||
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" style="max-width: 600px;min-width: 450px;">
|
||||
<p-card>
|
||||
<div>
|
||||
<img class="border-round" style="width:100%;height:100px;" alt="Logo" src="images/application_image_login.png">
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="flex flex-col">
|
||||
<label for="email" class="w-full">Email<strong class="validateStar">*</strong></label>
|
||||
<input type="text" pInputText formControlName="username" name="email"
|
||||
[ngClass]="{ 'is-invalid': submitted && f.username.errors }" class="p-inputtext-sm inputfield">
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label for="ppassword" class="w-full">Password <strong class="validateStar">*</strong></label>
|
||||
<input type="password" formControlName="password" name="ppassword" pInputText
|
||||
[ngClass]="{ 'is-invalid': submitted && f.password.errors }" class="p-inputtext-sm inputfield">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--p-footer -->
|
||||
<div class="btnloginRigh">
|
||||
<div class="mt-4 pt-2">
|
||||
<button pButton type="submit" label="Login" class="p-button-sm" [disabled]="isFieldsChange"
|
||||
[loading]="loading"></button>
|
||||
</div>
|
||||
</div>
|
||||
<!--/p-footer-->
|
||||
|
||||
</p-card>
|
||||
<div class="myerror">{{error}}</div>
|
||||
<div style="padding-top: 10px;"></div>
|
||||
<!--div
|
||||
style="text-align: center;display:inline-block;border-radius: var(--border-radius) !important;background-color:#e1e1e1; color:#222;font-size:13px;width:100%; margin-left:5px;margin-right: 5px;">
|
||||
<p >
|
||||
For management access to this application please email
|
||||
</p>
|
||||
<a href="mailto:NBMLHD-fleetteamreceipt@health.nsw.gov.au">NBMLHD-fleetteamreceipt@health.nsw.gov.au
|
||||
</a>
|
||||
|
||||
</div-->
|
||||
<!--pre> {{loginForm.value|json}} </pre-->
|
||||
</form>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,135 @@
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
|
||||
import { Router, ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
import { AuthenticationService } from '../user-services';
|
||||
|
||||
import { ConfirmationService } from 'primeng/api';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CardModule } from 'primeng/card';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
|
||||
import { Utils } from '../shares';
|
||||
@Component({
|
||||
templateUrl: './login.html',
|
||||
styleUrl: './login.css',
|
||||
selector: 'login',
|
||||
imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule,
|
||||
RouterModule, CommonModule, CardModule],
|
||||
|
||||
})
|
||||
export class Login implements OnInit, OnDestroy {
|
||||
loginForm: FormGroup;
|
||||
confirmationService = inject(ConfirmationService);
|
||||
route = inject(ActivatedRoute);
|
||||
router = inject(Router);
|
||||
authenticationService = inject(AuthenticationService);
|
||||
formBuilder = inject(FormBuilder);
|
||||
loading = false;
|
||||
homeUrl = "/person";
|
||||
submitted = false;
|
||||
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
|
||||
subChanged$ = new Subject<boolean>();
|
||||
// get return url from route parameters or default to '/'
|
||||
returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl;
|
||||
error = '';
|
||||
private subscription: Subscription = new Subscription();
|
||||
|
||||
constructor() {
|
||||
|
||||
this.loginForm = this.formBuilder.group({
|
||||
username: ['kham.vilaythong@gmail.com', Validators.required],
|
||||
password: ['password', Validators.required],
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// reset login status
|
||||
this.loading = false;
|
||||
this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false));
|
||||
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
|
||||
|
||||
}
|
||||
|
||||
updateData($event: Event) {
|
||||
console.log("onChange click", $event);
|
||||
}
|
||||
|
||||
// convenience getter for easy access to form fields
|
||||
get f() { return this.loginForm.value; }
|
||||
get isFieldsChange() {
|
||||
const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not
|
||||
//console.log(this.msg + 'is fields change', chan);
|
||||
return chan;
|
||||
}
|
||||
onSubmit() {
|
||||
|
||||
//this.playAudio();
|
||||
|
||||
// stop here if form is invalid
|
||||
if (this.loginForm.invalid) {
|
||||
return;
|
||||
}
|
||||
const fvalue = this.loginForm.value;
|
||||
this.loading = true;
|
||||
this.error = "";
|
||||
let username = "";
|
||||
let password = "";
|
||||
if (fvalue.username != null)
|
||||
username = fvalue.username;
|
||||
if (fvalue.password != null)
|
||||
password = fvalue.password;
|
||||
this.authenticationService.login(username, password)
|
||||
.pipe(first())
|
||||
.subscribe({
|
||||
next: (x: any) => {
|
||||
this.loading = false;
|
||||
console.log("login result ", x.data, this.returnUrl);
|
||||
if (x.statusCode == 1) {
|
||||
// if (x.data.role == 1) {
|
||||
// this.homeService.reportDate = Utils.getLastMonth();
|
||||
// this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } });
|
||||
|
||||
// }
|
||||
// else if (x.data.role == 3) {
|
||||
// this.homeService.reportDate = Utils.getLastMonth();
|
||||
// this.router.navigate(['/approval/' + x.data.id], { queryParams: { returnUrl: '/login' } });
|
||||
|
||||
//}
|
||||
// else
|
||||
this.router.navigate([this.homeUrl]);
|
||||
}
|
||||
else {
|
||||
this.loginForm.patchValue({ password: '' });
|
||||
// alert("Invalid Username or Password. Please try again.");
|
||||
console.error("error in login", x);
|
||||
this.confirmationService.confirm({
|
||||
message: 'Error in Login: Invalid username or password. May be your password is expired',
|
||||
header: 'Error Login',
|
||||
icon: 'pi pi-info-circle',
|
||||
rejectVisible: false,
|
||||
acceptLabel: 'OK',
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
error: er => {
|
||||
console.log("error in login", er);
|
||||
this.error = er.message;
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface Code {
|
||||
id:number;
|
||||
name:string;
|
||||
status?:string;
|
||||
active?:boolean;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export enum ConfigureUrl
|
||||
{
|
||||
//baseUrl = "http://localhost:61744",
|
||||
loginApiUrl = "api/Users/Login",
|
||||
searchStaffUrl = "api/Users/SearchADStaff",
|
||||
staffUrl = "api/Staff",
|
||||
personUrl = "api/Person",
|
||||
staffWorkUrl = "api/StaffWork",
|
||||
jobUrl ="api/Job",
|
||||
clientUrl ="api/Client",
|
||||
relationShipUrl ="api/RelationShip",
|
||||
logoutUrl ="api/Users/Logout",
|
||||
adminUserUrl = "api/Staff",
|
||||
lookupUrl = "api/Lookup",
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export const MIMEType = {
|
||||
png: 'image/png',
|
||||
jpg: 'image/jpg',
|
||||
jpeg: 'image/jpeg',
|
||||
gif: 'image/gif',
|
||||
txt: 'text/plain',
|
||||
pdf: 'application/pdf'
|
||||
};
|
||||
|
||||
|
||||
//for status of object 0 no change, 1 changed , -1 is deleted.
|
||||
export enum mState {
|
||||
Delete = -1,
|
||||
NoChange = 0,
|
||||
Modified = 1,
|
||||
New =2,
|
||||
};
|
||||
|
||||
export enum userRole {
|
||||
Normal = 1,
|
||||
Admin = 2,
|
||||
ServiceManager =3,
|
||||
Accounting = 4,
|
||||
WorkShop = 5,
|
||||
|
||||
};
|
||||
|
||||
export enum enumReqStatus {
|
||||
Canel = -1,
|
||||
Decline = -2,
|
||||
NewRequest = 0,
|
||||
Allocate = 1,
|
||||
Arrival = 2,
|
||||
Completed = 3,
|
||||
OnHold = 4,
|
||||
OffHold = 5
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
export * from './code';
|
||||
export * from './configureUrl';
|
||||
export * from './user';
|
||||
export * from './resultmodel';
|
||||
export * from './enum';
|
||||
export * from './lookup';
|
||||
export * from './staff';
|
||||
export * from './person';
|
||||
export * from './job';
|
||||
export * from './relationship';
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
export interface JobSearch {
|
||||
code:string;
|
||||
description:string;
|
||||
}
|
||||
export interface Job {
|
||||
id:number;
|
||||
code:string|null|undefined;
|
||||
description:string |null|undefined;
|
||||
active:boolean |null|undefined;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// using for priority and infectionType
|
||||
export interface Lookup {
|
||||
id:number;
|
||||
codeId:string;
|
||||
description:string;
|
||||
|
||||
}
|
||||
|
||||
export interface LookupEdit {
|
||||
id:number;
|
||||
codeId:string;
|
||||
description:string;
|
||||
active:boolean;
|
||||
type:string;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface MyDetail{
|
||||
id:number,
|
||||
login:string,
|
||||
firstname:string,
|
||||
surname:string,
|
||||
jobTitle:string,
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { RelationShip } from "./relationship";
|
||||
|
||||
export interface FamilySearch {
|
||||
email:string|null;
|
||||
phone:string |null;
|
||||
clientname:string |null;
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
|
||||
id: number;
|
||||
title?:string| null| undefined;
|
||||
firstName: string | null|undefined;
|
||||
lastName: string | null|undefined;
|
||||
email: string | null|undefined;
|
||||
phone: string | null|undefined;
|
||||
address?: string | null|undefined;
|
||||
alive: boolean | null|undefined;
|
||||
dob? : string | null|undefined;
|
||||
fatherId?: number| null|undefined;
|
||||
motherId?: number| null|undefined;
|
||||
image?: string|null|undefined;
|
||||
sex?: string|null|undefined;
|
||||
fatherName?:string |null;
|
||||
motherName?:string |null;
|
||||
relationShips?: RelationShip[];
|
||||
}
|
||||
|
||||
export interface PersonContainer
|
||||
{
|
||||
person: Person;
|
||||
formData?:FormData;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { mState } from "./enum";
|
||||
|
||||
export interface RelationShip {
|
||||
id: number;
|
||||
relatePersonId: number;
|
||||
personId: number;
|
||||
state: mState;
|
||||
}
|
||||
//relationship id this relatePersonId person has relation with other person
|
||||
// also other person has also has relation with you. look in two way
|
||||
// person id = 1 has to relation id = 90
|
||||
// that means when you edit person 90 their relation ship also be there.
|
||||
// so personid = 1 and relation id = 90 we need to show other way around.
|
||||
//example first name = Smith has partner Jennifer
|
||||
// when we edit Jennifer it should show Smith as her partner too.
|
||||
|
||||
export interface RelationShipView {
|
||||
id: number;
|
||||
relatePersonId:number;
|
||||
personId : number;
|
||||
pfirstName: string |null |undefined;
|
||||
plastName: string |null |undefined;
|
||||
sex:string |null |undefined;
|
||||
state: mState;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface ResultModel<T> {
|
||||
data: T;
|
||||
message: string;
|
||||
statusCode:number;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export interface StaffView {
|
||||
id:number,
|
||||
email:string,
|
||||
firstname:string,
|
||||
lastname:string,
|
||||
active:boolean,
|
||||
}
|
||||
|
||||
export interface ResetPassword{
|
||||
id:number;
|
||||
password:string;
|
||||
}
|
||||
|
||||
export interface Staff {
|
||||
id:number,
|
||||
email:string,
|
||||
firstname:string,
|
||||
lastname:string,
|
||||
phone:string,
|
||||
type:number;
|
||||
active:boolean;
|
||||
roleType: number;
|
||||
password?:string,
|
||||
}
|
||||
|
||||
export interface StaffSearch {
|
||||
email:string;
|
||||
firstName:string;
|
||||
lastName:string;
|
||||
}
|
||||
|
||||
//this use for new user only.
|
||||
export interface AdminUserNew {
|
||||
loginId:number;
|
||||
user: Staff;// first get userid from this table and
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
export class User {
|
||||
id: number = 0;
|
||||
username: string = '';
|
||||
role: number = -1;
|
||||
firstName: string = '';
|
||||
lastName: string = '';
|
||||
email:string = '';
|
||||
token:string ='';
|
||||
position: string ='';
|
||||
department: string = '';
|
||||
managerEmail:string ='';
|
||||
phone:string='';
|
||||
|
||||
}
|
||||
|
||||
export interface UserAD
|
||||
{
|
||||
id: number,
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
email: string,
|
||||
username: string,
|
||||
department: string,
|
||||
position: string,
|
||||
role: number,
|
||||
phone:string,
|
||||
managerEmail:string
|
||||
}
|
||||
|
||||
export class SecAccessLevel {
|
||||
accessName:string='';
|
||||
request:number=0;
|
||||
report:number=0;
|
||||
admin:number=0;
|
||||
allocation:number=0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
//mypreset.ts
|
||||
import { definePreset } from '@primeuix/themes';
|
||||
import Aura from '@primeuix/themes/aura';
|
||||
|
||||
const MyPreset = definePreset(Aura, {
|
||||
semantic: {
|
||||
colorScheme: {
|
||||
primary: {
|
||||
50: '{zinc.50}',
|
||||
100: '{zinc.100}',
|
||||
200: '{zinc.200}',
|
||||
300: '{zinc.300}',
|
||||
400: '{zinc.400}',
|
||||
500: '{zinc.500}',
|
||||
600: '{zinc.600}',
|
||||
700: '{zinc.700}',
|
||||
800: '{zinc.800}',
|
||||
900: '{zinc.900}',
|
||||
950: '{zinc.950}'
|
||||
},
|
||||
light: {
|
||||
surface: {
|
||||
0: '#ffffff',
|
||||
50: '{zinc.50}',
|
||||
100: '{zinc.100}',
|
||||
200: '{zinc.200}',
|
||||
300: '{zinc.300}',
|
||||
400: '{zinc.400}',
|
||||
500: '{zinc.500}',
|
||||
600: '{zinc.600}',
|
||||
700: '{zinc.700}',
|
||||
800: '{zinc.800}',
|
||||
900: '{zinc.900}',
|
||||
950: '{zinc.950}'
|
||||
}
|
||||
},
|
||||
dark: {
|
||||
surface: {
|
||||
0: '#ffffff',
|
||||
50: '{slate.50}',
|
||||
100: '{slate.100}',
|
||||
200: '{slate.200}',
|
||||
300: '{slate.300}',
|
||||
400: '{slate.400}',
|
||||
500: '{slate.500}',
|
||||
600: '{slate.600}',
|
||||
700: '{slate.700}',
|
||||
800: '{slate.800}',
|
||||
900: '{slate.900}',
|
||||
950: '{slate.950}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default MyPreset;
|
||||
@@ -0,0 +1,12 @@
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
td.mat-column-edit, .mat-column-delete {
|
||||
width: 35px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="shadow-2xl rounded p-2 mt-2">
|
||||
<h3>Family Tree</h3>
|
||||
<div>
|
||||
<div >
|
||||
<p-organization-chart [value]="familyTree()" selectionMode="multiple" [(selection)]="selectedNodes" [collapsible]="true">
|
||||
<ng-template let-node pTemplate="default">
|
||||
<div class="flex flex-col items-center">
|
||||
<!--img src="https://primefaces.org/cdn/primeng/images/flag/flag_placeholder.png" [alt]="node.label" [class]="'flag' + ' flag-' + node.data" width="32" /-->
|
||||
<div class="mt-4 font-medium text-lg">{{ node.label }}</div>
|
||||
<div>{{ node.data }}</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-organization-chart>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button pButton pRipple class="p-button-sm mr-2 p-button-success" type="button" icon="pi pi-sign-in" label="Close"
|
||||
(click)="close()"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
|
||||
|
||||
import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
|
||||
import { OrganizationChartModule } from 'primeng/organizationchart';
|
||||
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { PersonService } from './person.service';
|
||||
import { AuthenticationService } from '../user-services';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { TreeNode } from 'primeng/api';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { PersonEdit } from './person.edit';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
|
||||
import { Utils } from '../shares';
|
||||
|
||||
@Component({
|
||||
selector: 'family-orga',
|
||||
templateUrl: './family.orga.html',
|
||||
imports:[TableModule,FormsModule,TreeModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
|
||||
styleUrls: ['./family.orga.css'],
|
||||
providers: [DialogService]
|
||||
})
|
||||
export class FamilyOrga implements OnInit, OnDestroy{
|
||||
|
||||
private subscription:Subscription = new Subscription();
|
||||
person!: Person;
|
||||
selectedNode!: TreeNode;
|
||||
selectedNodes!: TreeNode[];
|
||||
private cd = inject(ChangeDetectorRef);
|
||||
familyTree = signal<TreeNode[]>([]);
|
||||
relations: RelationShip[] =[];
|
||||
_id = -10;
|
||||
loading = false;
|
||||
familyList:Person[] = [];
|
||||
msg ="[Person organise component]";
|
||||
private messageService = inject(MessageService);
|
||||
/*
|
||||
private store: Store<appValidationState>
|
||||
*/
|
||||
constructor(
|
||||
public dialogService: DialogService,
|
||||
private personService: PersonService,
|
||||
|
||||
public ref: DynamicDialogRef, public config: DynamicDialogConfig,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
const id = this.config.data.id;
|
||||
this.familyList = this.config.data.familyList;
|
||||
const item = this.familyList.find(x => x.id == id);
|
||||
if (item != undefined)
|
||||
this.person = item;
|
||||
this.loadPersonFamilyTree(id);
|
||||
// this. populateTree();
|
||||
|
||||
}
|
||||
populateTree() : void {
|
||||
const tree = [
|
||||
{
|
||||
label: 'F.C Barcelona',
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
label: 'Argentina',
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
label: 'Argentina'
|
||||
},
|
||||
{
|
||||
label: 'France'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'France',
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
label: 'France'
|
||||
},
|
||||
{
|
||||
label: 'Morocco'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
this.familyTree.set(tree);
|
||||
}
|
||||
loadPersonFamilyTree(id: number): void {
|
||||
const relationShip$ = this.personService.loadPersonFamily(id);
|
||||
this.subscription.add(relationShip$.subscribe(
|
||||
{
|
||||
next: x => {
|
||||
if (x.statusCode == 1)
|
||||
{
|
||||
let tree : TreeNode[] =[];
|
||||
tree.push(x.data);
|
||||
this.familyTree.set(tree);
|
||||
console.log("load person family", this.familyTree());
|
||||
|
||||
}
|
||||
},
|
||||
error: e => {
|
||||
console.error("error load loadPersonFamily by personId", id);
|
||||
this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message});
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
nodeSelect(event: TreeNodeSelectEvent) {
|
||||
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
|
||||
}
|
||||
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
|
||||
// console.log("double click node", event.node.key);
|
||||
const id = event.node.key;
|
||||
this.edit(Number(id));
|
||||
}
|
||||
|
||||
getName(id:number): string
|
||||
{
|
||||
let result ="";
|
||||
const item = this.familyList.find(x => x.id == id);
|
||||
if (item)
|
||||
result = item.lastName + " " + item.firstName;
|
||||
return result;
|
||||
}
|
||||
updateParent(list:Person[]):void {
|
||||
let i = 0;
|
||||
let item:Person;
|
||||
for (i = 0; i< list.length; i++)
|
||||
{
|
||||
item = list[i];
|
||||
if (item.fatherId && item.fatherId > 0)
|
||||
{
|
||||
item.fatherName = this.getName(item.fatherId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newFamily():void {
|
||||
//console.log("add new employee");
|
||||
this.personService.parentList = this.familyList;
|
||||
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
|
||||
this.showEdit(this._id--);
|
||||
}
|
||||
edit(id: number) : void {
|
||||
//console.log("edit family", id);
|
||||
this.personService.parentList = this.familyList;
|
||||
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
|
||||
this.showEdit(id);
|
||||
}
|
||||
showEdit(id:number) {
|
||||
const ref = this.dialogService.open(PersonEdit, {
|
||||
data: {
|
||||
id,
|
||||
familyList: this.familyList,
|
||||
},
|
||||
header: 'Family',
|
||||
width: '80%',
|
||||
maximizable: true
|
||||
});
|
||||
|
||||
ref.onClose.subscribe((item: Person) => {
|
||||
if (item) {
|
||||
//console.log("after close ward edit", item);
|
||||
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
|
||||
//update the current list
|
||||
this.updateList(item);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
updateList(item: Person) :void {
|
||||
const idx = this.familyList.findIndex( x => x.id == item.id);
|
||||
if (item.fatherId && item.fatherId > 0)
|
||||
item.fatherName = this.getName(item.fatherId);
|
||||
if (item.motherId && item.motherId > 0)
|
||||
item.motherName = this.getName(item.motherId);
|
||||
if (idx < 0)
|
||||
{
|
||||
|
||||
const olist = [... this.familyList, item];
|
||||
this.familyList = olist;
|
||||
}
|
||||
else
|
||||
{
|
||||
const oitem = this.familyList[idx];
|
||||
oitem.firstName = item.firstName;
|
||||
oitem.lastName = item.lastName;
|
||||
oitem.address = item.address;
|
||||
oitem.alive = item.alive;
|
||||
oitem.dob = item.dob;
|
||||
oitem.email = item.email;
|
||||
oitem.fatherId = item.fatherId;
|
||||
oitem.motherId = item.motherId;
|
||||
oitem.motherName = item.motherName;
|
||||
oitem.fatherName = item.fatherName;
|
||||
|
||||
}
|
||||
}
|
||||
deleteItem(id: number): void {
|
||||
this.personService.deletePerson(id)
|
||||
.pipe(take(1))
|
||||
.subscribe({ next: result => {
|
||||
console.log(this.msg + " deleteItem success", result);
|
||||
|
||||
const nlist = this.familyList.filter(d => d.id !== id);
|
||||
this.familyList = nlist;
|
||||
},
|
||||
error: e => console.error(e)
|
||||
});
|
||||
//console.log(this.msg + "click button to delete");
|
||||
}
|
||||
close() :void {
|
||||
this.ref.close(null);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
td.mat-column-edit, .mat-column-delete {
|
||||
width: 35px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<div class="shadow-2xl rounded p-2 mt-2">
|
||||
<h3>Family Tree</h3>
|
||||
<div>
|
||||
<form>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<div class="">
|
||||
<label for="login">Email</label>
|
||||
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="lastname1">Surname</label>
|
||||
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="firstname">First Name</label>
|
||||
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
|
||||
(click)="search()"></button>
|
||||
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" iconPos="left" label="New Family"
|
||||
(click)="newFamily()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<p-tree [value]="familyTree" class="w-full md:w-[30rem]"
|
||||
(onNodeDoubleClick)="nodeDoubleSelect($event)"
|
||||
selectionMode="single" [(selection)]="selectedNode" (onNodeSelect)="nodeSelect($event)" />
|
||||
<!--div class="card flex justify-center overflow-x-auto">
|
||||
<p-organization-chart [value]="familyTree" selectionMode="multiple" [(selection)]="selectedNodes" [collapsible]="true">
|
||||
<ng-template let-node pTemplate="person">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col items-center">
|
||||
<img [src]="node.data.image" class="mb-4 w-12 h-12" />
|
||||
<div class="font-bold mb-2">{{ node.data.name }}</div>
|
||||
<div>{{ node.data.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-organization-chart>
|
||||
</div-->
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef} from '@angular/core';
|
||||
|
||||
import { StaffView ,StaffSearch, Person } from '../models';
|
||||
import { OrganizationChartModule } from 'primeng/organizationchart';
|
||||
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { PersonService } from './person.service';
|
||||
import { AuthenticationService } from '../user-services';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { TreeNode } from 'primeng/api';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { DialogService } from 'primeng/dynamicdialog';
|
||||
import { PersonEdit } from './person.edit';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
|
||||
import { Utils } from '../shares';
|
||||
|
||||
@Component({
|
||||
selector: 'family-tree',
|
||||
templateUrl: './family.tree.html',
|
||||
imports:[TableModule,FormsModule,TreeModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
|
||||
styleUrls: ['./family.tree.css'],
|
||||
providers: [DialogService]
|
||||
})
|
||||
export class FamilyTree implements OnInit, OnDestroy{
|
||||
|
||||
private subscription:Subscription = new Subscription();
|
||||
firstname = '';
|
||||
selectedNode!: TreeNode;
|
||||
private cd = inject(ChangeDetectorRef);
|
||||
familyTree: TreeNode[] = [];
|
||||
email = '';
|
||||
lastname = '';
|
||||
_id = -10;
|
||||
loading = false;
|
||||
familyList:Person[] = [];
|
||||
msg ="[Person tree component]";
|
||||
private messageService = inject(MessageService);
|
||||
/*
|
||||
private store: Store<appValidationState>
|
||||
*/
|
||||
constructor(
|
||||
public dialogService: DialogService,
|
||||
private personService: PersonService,
|
||||
private authenticationService: AuthenticationService,
|
||||
private router: Router
|
||||
) {}
|
||||
getSearchCiteria(): StaffSearch {
|
||||
let criteria:StaffSearch = {
|
||||
email: this.email,
|
||||
firstName: this.firstname,
|
||||
lastName: this.lastname,
|
||||
};
|
||||
|
||||
return criteria;
|
||||
|
||||
}
|
||||
canSearch():boolean {
|
||||
let result = false;
|
||||
result = this.email !== "";
|
||||
result = result || this.lastname !== "";
|
||||
result = result || this.firstname !== "";
|
||||
return result;
|
||||
}
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.authenticationService.isHome = false;
|
||||
this.authenticationService.isReport = false;
|
||||
const prev = this.personService.searchCriteria;
|
||||
let goload = true;
|
||||
if (prev.lastName !== '')
|
||||
{
|
||||
this.lastname = prev.lastName;
|
||||
goload = true;
|
||||
}
|
||||
if (prev.firstName !== '')
|
||||
{
|
||||
this.firstname = prev.firstName;
|
||||
goload = true;
|
||||
}
|
||||
if (prev.email !== '')
|
||||
{
|
||||
this.email = prev.email;
|
||||
goload = true;
|
||||
}
|
||||
if (goload)
|
||||
{
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
getActive(active:boolean):string {
|
||||
let result = 'false-icon pi-times-circle';
|
||||
if (active)
|
||||
result = 'true-icon pi-check-circle';
|
||||
return result;
|
||||
}
|
||||
search():void {
|
||||
const canSearch = true; // this.canSearch();
|
||||
if (canSearch)
|
||||
{
|
||||
this.loading = true;
|
||||
//const criteria = this.getSearchCiteria();
|
||||
// this.personService.searchCriteria = criteria;
|
||||
this.subscription.add(
|
||||
this.personService.loadPersonFamilyTree(true, false).subscribe(
|
||||
{
|
||||
next: x => {
|
||||
if (x.statusCode == 1)
|
||||
{
|
||||
this.familyTree = x.data;
|
||||
this.loading = false;
|
||||
this.cd.detectChanges();
|
||||
console.log("the family tree", this.familyTree);
|
||||
}
|
||||
},
|
||||
|
||||
error: e => {
|
||||
const message = e || e.message;
|
||||
// this.toastr.error(message);
|
||||
this.loading = false;
|
||||
console.log("error ", e);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/*
|
||||
this.personService.searchPersons(criteria).subscribe( {
|
||||
next: result => {
|
||||
// console.log(this.msg + "search load Data", result);
|
||||
this.familyList = result.data;
|
||||
this.familyTree = Utils.populateNode( "fatherId", this.familyList);
|
||||
this.loading = false;
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
},
|
||||
|
||||
error: e => {
|
||||
const message = e || e.message;
|
||||
// this.toastr.error(message);
|
||||
this.loading = false;
|
||||
console.log("error ", e);
|
||||
}
|
||||
})
|
||||
);
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
nodeSelect(event: TreeNodeSelectEvent) {
|
||||
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
|
||||
}
|
||||
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
|
||||
// console.log("double click node", event.node.key);
|
||||
const id = event.node.key;
|
||||
this.edit(Number(id));
|
||||
}
|
||||
|
||||
getName(id:number): string
|
||||
{
|
||||
let result ="";
|
||||
const item = this.familyList.find(x => x.id == id);
|
||||
if (item)
|
||||
result = item.lastName + " " + item.firstName;
|
||||
return result;
|
||||
}
|
||||
updateParent(list:Person[]):void {
|
||||
let i = 0;
|
||||
let item:Person;
|
||||
for (i = 0; i< list.length; i++)
|
||||
{
|
||||
item = list[i];
|
||||
if (item.fatherId && item.fatherId > 0)
|
||||
{
|
||||
item.fatherName = this.getName(item.fatherId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newFamily():void {
|
||||
//console.log("add new employee");
|
||||
this.personService.parentList = this.familyList;
|
||||
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
|
||||
this.showEdit(this._id--);
|
||||
}
|
||||
edit(id: number) : void {
|
||||
//console.log("edit family", id);
|
||||
this.personService.parentList = this.familyList;
|
||||
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
|
||||
this.showEdit(id);
|
||||
}
|
||||
showEdit(id:number) {
|
||||
const ref = this.dialogService.open(PersonEdit, {
|
||||
data: {
|
||||
id,
|
||||
familyList: this.familyList,
|
||||
},
|
||||
header: 'Family',
|
||||
width: '80%',
|
||||
maximizable: true
|
||||
});
|
||||
|
||||
ref.onClose.subscribe((item: Person) => {
|
||||
if (item) {
|
||||
//console.log("after close ward edit", item);
|
||||
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
|
||||
//update the current list
|
||||
this.updateList(item);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
updateList(item: Person) :void {
|
||||
const idx = this.familyList.findIndex( x => x.id == item.id);
|
||||
if (item.fatherId && item.fatherId > 0)
|
||||
item.fatherName = this.getName(item.fatherId);
|
||||
if (item.motherId && item.motherId > 0)
|
||||
item.motherName = this.getName(item.motherId);
|
||||
if (idx < 0)
|
||||
{
|
||||
|
||||
const olist = [... this.familyList, item];
|
||||
this.familyList = olist;
|
||||
}
|
||||
else
|
||||
{
|
||||
const oitem = this.familyList[idx];
|
||||
oitem.firstName = item.firstName;
|
||||
oitem.lastName = item.lastName;
|
||||
oitem.address = item.address;
|
||||
oitem.alive = item.alive;
|
||||
oitem.dob = item.dob;
|
||||
oitem.email = item.email;
|
||||
oitem.fatherId = item.fatherId;
|
||||
oitem.motherId = item.motherId;
|
||||
oitem.motherName = item.motherName;
|
||||
oitem.fatherName = item.fatherName;
|
||||
|
||||
}
|
||||
}
|
||||
deleteItem(id: number): void {
|
||||
this.personService.deletePerson(id)
|
||||
.pipe(take(1))
|
||||
.subscribe({ next: result => {
|
||||
console.log(this.msg + " deleteItem success", result);
|
||||
|
||||
const nlist = this.familyList.filter(d => d.id !== id);
|
||||
this.familyList = nlist;
|
||||
},
|
||||
error: e => console.error(e)
|
||||
});
|
||||
//console.log(this.msg + "click button to delete");
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
td.mat-column-edit, .mat-column-delete {
|
||||
width: 35px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<div class="shadow-2xl rounded p-2 mt-2">
|
||||
<h3>Person List</h3>
|
||||
<div>
|
||||
<form>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<div class="">
|
||||
<label for="login">Email</label>
|
||||
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="lastname1">Surname</label>
|
||||
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="firstname">First Name</label>
|
||||
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
|
||||
(click)="search()"></button>
|
||||
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" iconPos="left" label="New Person"
|
||||
(click)="newFamily()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<p-table [value]="familyList()" sortMode="multiple" dataKey="id" class="p-datatable-sm"
|
||||
class="p-datatable-sm" selectionMode="single" [(selection)]="selectedPerson"
|
||||
[paginator]="true" rowHover="true" [globalFilterFields]="['lastName', 'firstName', 'email', 'fatherName', 'motherName']"
|
||||
[rows]="20" [rowsPerPageOptions]="[10,20,50,100]"
|
||||
[loading]="loading">
|
||||
<ng-template #caption>
|
||||
<div class="flex">
|
||||
<p-iconfield iconPosition="left" class="ml-auto">
|
||||
<p-inputicon>
|
||||
<i class="pi pi-search"></i>
|
||||
</p-inputicon>
|
||||
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
|
||||
</p-iconfield>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
|
||||
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="sex">Sex <p-sortIcon field="sex"></p-sortIcon></th>
|
||||
<th pSortableColumn="fatherName">Father<p-sortIcon field="fatherName"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="motherName">Mother<p-sortIcon field="motherName"></p-sortIcon>
|
||||
</th>
|
||||
|
||||
<th>Edit</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-user>
|
||||
<tr>
|
||||
<td>{{user.lastName}}</td>
|
||||
<td>{{user.firstName}}</td>
|
||||
<td>{{user.sex}}</td>
|
||||
<td>{{user.fatherName}}</td>
|
||||
<td>{{user.motherName}}</td>
|
||||
|
||||
<td>
|
||||
<p-button type="button" icon="pi pi-ellipsis-v"
|
||||
[rounded]="true" [text]="true" [raised]="true"
|
||||
class="p-button-rounded p-button-text"
|
||||
(onClick)="actionClick(user.id,$event)"></p-button>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
<p-menu #rowmenu [model]="items" [popup]="true" (onShow)="onMenuShow()" />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core';
|
||||
|
||||
import { StaffView ,StaffSearch, Person } from '../models';
|
||||
|
||||
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { PersonService } from './person.service';
|
||||
import { AuthenticationService } from '../user-services';
|
||||
import { Table, TableModule } from 'primeng/table';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { DialogService } from 'primeng/dynamicdialog';
|
||||
import { PersonEdit } from './person.edit';
|
||||
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
|
||||
import { IconFieldModule } from 'primeng/iconfield';
|
||||
import { InputIconModule } from 'primeng/inputicon';
|
||||
import { Menu, MenuModule } from 'primeng/menu';
|
||||
import { FamilyOrga } from './family.orga';
|
||||
@Component({
|
||||
selector: 'family-list',
|
||||
templateUrl: './familylist.html',
|
||||
imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule,
|
||||
InputTextModule,IconFieldModule,InputIconModule],
|
||||
styleUrls: ['./familylist.css'],
|
||||
providers: [DialogService]
|
||||
})
|
||||
export class FamilyList implements OnInit, OnDestroy{
|
||||
|
||||
private subscription:Subscription = new Subscription();
|
||||
//private cd = inject(ChangeDetectorRef);
|
||||
items: MenuItem[] | undefined;
|
||||
selectedPerson!: Person;
|
||||
firstname = '';
|
||||
email = '';
|
||||
lastname = '';
|
||||
_id = -10;
|
||||
selectId = -1;
|
||||
loading = false;
|
||||
familyList = signal<Person[]>([]);
|
||||
msg ="[Person component]";
|
||||
@ViewChild(Table) dt2!: Table;
|
||||
@ViewChild('rowmenu') popMenu?: Menu;
|
||||
private messageService = inject(MessageService);
|
||||
public dialogService= inject( DialogService);
|
||||
private personService= inject( PersonService);
|
||||
private confirmationService = inject(ConfirmationService);
|
||||
private cd = inject(ChangeDetectorRef);
|
||||
private authenticationService= inject( AuthenticationService);
|
||||
private router= inject( Router);
|
||||
constructor() {}
|
||||
getSearchCiteria(): StaffSearch {
|
||||
let criteria:StaffSearch = {
|
||||
email: this.email,
|
||||
firstName: this.firstname,
|
||||
lastName: this.lastname,
|
||||
};
|
||||
console.log("get search citeria", criteria);
|
||||
return criteria;
|
||||
|
||||
}
|
||||
initMenu(): void {
|
||||
this.items = [
|
||||
{
|
||||
label: 'Edit',
|
||||
icon: 'pi pi-pencil',
|
||||
command: () => {
|
||||
this.edit(this.selectId);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
icon: 'pi pi-times',
|
||||
command: () => {
|
||||
this.delete(this.selectId);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Show Organization',
|
||||
icon: 'pi pi-sitemap',
|
||||
command: () => {
|
||||
this.showChildren(this.selectId);
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
}
|
||||
actionClick(id: number, event:Event): void {
|
||||
// console.log("action edit "+ id);
|
||||
this.selectId = id;
|
||||
this.popMenu!.toggle(event);
|
||||
}
|
||||
showChildren(id: number): void {
|
||||
console.log("show children of id", id);
|
||||
this.showOrganise(id);
|
||||
|
||||
}
|
||||
handleInput(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
this.dt2.filterGlobal(value, 'contains');
|
||||
}
|
||||
canSearch():boolean {
|
||||
let result = false;
|
||||
result = this.email !== "";
|
||||
result = result || this.lastname !== "";
|
||||
result = result || this.firstname !== "";
|
||||
return result;
|
||||
}
|
||||
onMenuShow(): void {
|
||||
console.log("this is show", this.selectedPerson);
|
||||
}
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.initMenu();
|
||||
this.authenticationService.isHome = false;
|
||||
this.authenticationService.isReport = false;
|
||||
const prev = this.personService.searchCriteria;
|
||||
let goload = true;
|
||||
if (prev.lastName !== '')
|
||||
{
|
||||
this.lastname = prev.lastName;
|
||||
goload = true;
|
||||
}
|
||||
if (prev.firstName !== '')
|
||||
{
|
||||
this.firstname = prev.firstName;
|
||||
goload = true;
|
||||
}
|
||||
if (prev.email !== '')
|
||||
{
|
||||
this.email = prev.email;
|
||||
goload = true;
|
||||
}
|
||||
if (goload)
|
||||
{
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
getActive(active:boolean):string {
|
||||
let result = 'false-icon pi-times-circle';
|
||||
if (active)
|
||||
result = 'true-icon pi-check-circle';
|
||||
return result;
|
||||
}
|
||||
search():void {
|
||||
const canSearch = true; // this.canSearch();
|
||||
if (canSearch)
|
||||
{
|
||||
this.loading = true;
|
||||
const criteria = this.getSearchCiteria();
|
||||
this.personService.searchCriteria = criteria;
|
||||
this.subscription.add(
|
||||
this.personService.searchPersons(criteria).subscribe( {
|
||||
next: result => {
|
||||
// console.log(this.msg + "search load Data", result);
|
||||
const familyList = result.data;
|
||||
this.familyList.set(familyList);
|
||||
this.updateParent( this.familyList());
|
||||
//this.familyList.set(familyList);
|
||||
|
||||
console.log("the person from load", this.familyList());
|
||||
this.loading = false;
|
||||
this.cd.detectChanges();
|
||||
},
|
||||
error: e => {
|
||||
const message = e || e.message;
|
||||
// this.toastr.error(message);
|
||||
this.loading = false;
|
||||
console.log("error ", e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
getName(id:number): string
|
||||
{
|
||||
let result ="";
|
||||
const item = this.familyList().find(x => x.id == id);
|
||||
if (item)
|
||||
result = item.lastName + " " + item.firstName;
|
||||
return result;
|
||||
}
|
||||
updateParent(list:Person[]):void {
|
||||
let i = 0;
|
||||
let item:Person;
|
||||
for (i = 0; i< list.length; i++)
|
||||
{
|
||||
item = list[i];
|
||||
if (item.fatherId && item.fatherId > 0)
|
||||
{
|
||||
item.fatherName = this.getName(item.fatherId);
|
||||
}
|
||||
if (item.motherId && item.motherId > 0)
|
||||
{
|
||||
item.motherName = this.getName(item.motherId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(id: number): void {
|
||||
this.confirmationService.confirm({
|
||||
|
||||
message: 'Do you want to delete this record?',
|
||||
header: 'Confirmation Delete',
|
||||
icon: 'pi pi-info-circle',
|
||||
rejectLabel: 'Cancel',
|
||||
rejectButtonProps: {
|
||||
label: 'Cancel',
|
||||
severity: 'secondary',
|
||||
outlined: true,
|
||||
},
|
||||
acceptButtonProps: {
|
||||
label: 'Delete',
|
||||
severity: 'danger',
|
||||
},
|
||||
|
||||
accept: () => {
|
||||
this.deleteItem(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteItem(id: number): void {
|
||||
this.personService.deletePerson(id)
|
||||
.pipe(take(1))
|
||||
.subscribe({ next: result => {
|
||||
console.log(this.msg + " deleteItem success", result);
|
||||
|
||||
const nlist = this.familyList().filter(d => d.id !== id);
|
||||
this.familyList.set(nlist);
|
||||
},
|
||||
error: e => console.error(e)
|
||||
});
|
||||
//console.log(this.msg + "click button to delete");
|
||||
}
|
||||
|
||||
newFamily():void {
|
||||
//console.log("add new employee");
|
||||
this.personService.parentList = this.familyList();
|
||||
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
|
||||
this.showEdit(this._id--);
|
||||
}
|
||||
edit(id: number) : void {
|
||||
//console.log("edit family", id);
|
||||
this.personService.parentList = this.familyList();
|
||||
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
|
||||
this.showEdit(id);
|
||||
}
|
||||
showEdit(id:number) {
|
||||
const ref = this.dialogService.open(PersonEdit, {
|
||||
data: {
|
||||
id,
|
||||
familyList: this.familyList(),
|
||||
},
|
||||
header: 'Person',
|
||||
width: '80%',
|
||||
maximizable: true
|
||||
});
|
||||
|
||||
ref.onClose.subscribe((item: Person) => {
|
||||
if (item) {
|
||||
//console.log("after close ward edit", item);
|
||||
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
|
||||
//update the current list
|
||||
this.updateList(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showOrganise(id:number) {
|
||||
const ref = this.dialogService.open(FamilyOrga, {
|
||||
data: {
|
||||
id,
|
||||
familyList: this.familyList(),
|
||||
},
|
||||
header: 'Children',
|
||||
width: '80%',
|
||||
maximizable: true
|
||||
});
|
||||
|
||||
ref.onClose.subscribe((item: Person) => {
|
||||
if (item) {
|
||||
//console.log("after close ward edit", item);
|
||||
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
|
||||
//update the current list
|
||||
//this.updateList(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateList(item: Person) :void {
|
||||
const idx = this.familyList().findIndex( x => x.id == item.id);
|
||||
if (item.fatherId && item.fatherId > 0)
|
||||
item.fatherName = this.getName(item.fatherId);
|
||||
if (item.motherId && item.motherId > 0)
|
||||
item.motherName = this.getName(item.motherId);
|
||||
if (idx < 0)
|
||||
{
|
||||
const olist = [... this.familyList(), item];
|
||||
this.familyList.set(olist);
|
||||
}
|
||||
else
|
||||
{
|
||||
const oitem = this.familyList()[idx];
|
||||
oitem.firstName = item.firstName;
|
||||
oitem.lastName = item.lastName;
|
||||
oitem.address = item.address;
|
||||
oitem.alive = item.alive;
|
||||
oitem.dob = item.dob;
|
||||
oitem.sex = item.sex;
|
||||
oitem.email = item.email;
|
||||
oitem.fatherId = item.fatherId;
|
||||
oitem.motherId = item.motherId;
|
||||
oitem.fatherName = item.fatherName;
|
||||
oitem.motherName = item.motherName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './familylist';
|
||||
export * from './person.edit';
|
||||
export * from './family.tree';
|
||||
export * from './person.service';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<div class=" mt-2">
|
||||
|
||||
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit($event)" >
|
||||
<div class="ml-2 grid grid-cols-2 gap-3 p-2">
|
||||
<div class="">
|
||||
<label for="lastname1">Surname<strong class="app-require">*</strong></label>
|
||||
<input id="lastname1" pInputText formControlName="lastname" type="text"
|
||||
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="firstname">First Name<strong class="app-require">*</strong></label>
|
||||
<input id="firstname" pInputText formControlName="firstname" type="text"
|
||||
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
|
||||
</div>
|
||||
<div class="">
|
||||
<label class="flex w-full">Sex <strong class="app-require">*</strong></label>
|
||||
<p-select [options]="sexList" optionLabel="name" optionValue="status" placeholder="Select Gender"
|
||||
formControlName="sex"
|
||||
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','sex')"
|
||||
styleClass="w-full p-inputtext-sm mr-1"></p-select >
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="dob" class="flex w-full">DOB</label>
|
||||
<p-datepicker ariaLabelledBy="dob" formControlName="dob"></p-datepicker>
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="login">Email</label>
|
||||
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
|
||||
type="text" class="inputfield w-full">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="phone" class="w-full">Phone</label>
|
||||
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
|
||||
</div>
|
||||
<div class="">
|
||||
<label class="flex w-full">Father</label>
|
||||
<p-select [filter]="true" filterBy="name" [options]="fatherList" optionLabel="name" appendTo="body" optionValue="id" placeholder="Select Parent"
|
||||
formControlName="fatherId" class="w-full p-inputtext-sm mr-1"></p-select >
|
||||
</div>
|
||||
<div class="">
|
||||
<label class="flex w-full">Mother</label>
|
||||
<p-select [options]="motherList" [filter]="true" filterBy="name" optionLabel="name" appendTo="body" optionValue="id" placeholder="Select Parent"
|
||||
formControlName="motherId" class="w-full p-inputtext-sm mr-1"></p-select >
|
||||
</div>
|
||||
|
||||
<div class="grid align-end">
|
||||
<div class="p-field-checkbox">
|
||||
<p-checkbox inputId="alive" formControlName="alive" [binary]="true"></p-checkbox>
|
||||
<label for="alive" class="ml-2 w-full">Alive</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="field col-12 md:col-6 sm:col-8">
|
||||
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
|
||||
{
|
||||
<div class="ml-6 file-upload">
|
||||
<a href="{{hostsite}}/{{adminuserForm.value.image}}" target="_blank" class="text-blue-400">View Attachment
|
||||
</a>
|
||||
<button pButton type="button" icon="pi pi-times" pTooltip="remove attach file"
|
||||
class="p-button-rounded p-button-text text-red-500 p-button-raised ml-2"
|
||||
(click)="deleteFile()"></button>
|
||||
</div>
|
||||
}
|
||||
@else
|
||||
{
|
||||
<div class="ml-6">
|
||||
<input type="file" class="file-input" (change)="onFileSelected($event)" #fileUpload>
|
||||
<div class="file-upload">
|
||||
<label>{{ adminuserForm.value.image || "No attachment file uploaded yet."}}</label>
|
||||
<button pButton type="button" icon="pi pi-paperclip"
|
||||
class="p-button-rounded p-button-text p-button-raised ml-2" pTooltip="attach file"
|
||||
(click)="fileUpload.click()"></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 w-full">
|
||||
<p-button class="flex justify-end mr-2" icon="pi pi-user" label="Add Partner"
|
||||
severity="warn" (onClick)="addPartner()"/>
|
||||
<div class="shadow rounded mt-2 mb-2">
|
||||
<p-table [value]="partners()">
|
||||
<ng-template #header>
|
||||
<tr>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Sex</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template #body let-user>
|
||||
<tr>
|
||||
<td>{{ user.pfirstName }}</td>
|
||||
<td>{{ user.plastName }}</td>
|
||||
<td>{{ user.sex }}</td>
|
||||
<td>
|
||||
<button pButton type="button" icon="pi pi-times" class="p-button-rounded p-button-text p-button-danger"
|
||||
(click)="onDeletePartner(user)"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mr-2 mb-2 p-2">
|
||||
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" label="Save" [disabled]="isFieldsChange"></button>
|
||||
<button pButton pRipple class="p-button-sm p-button-secondary" type="button" icon="pi pi-times" label="Cancel" (click)="cancel($event)"></button>
|
||||
</div>
|
||||
</form>
|
||||
<!--pre>{{ adminuserForm.value|json}}</pre-->
|
||||
</div>
|
||||
@@ -0,0 +1,561 @@
|
||||
import { ChangeDetectorRef, Component, inject, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
|
||||
import {DynamicDialogConfig} from 'primeng/dynamicdialog';
|
||||
import { FormControl, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { Subject, Subscription} from 'rxjs';
|
||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||
import { DatePickerModule } from 'primeng/datepicker';
|
||||
import { Code, RelationShipView, Person, mState, RelationShip} from '../models';
|
||||
import { AppSettingService, LookupService, Utils } from '../shares';
|
||||
import { PersonService } from './person.service';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { SelectModule } from 'primeng/select';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import moment from 'moment';
|
||||
import { HttpEvent } from '@angular/common/http';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { Pickperson } from '../pickperson/pickperson';
|
||||
|
||||
|
||||
export interface FileUploadEvent {
|
||||
originalEvent: HttpEvent<any>;
|
||||
files: File[];
|
||||
};
|
||||
export interface FileUploadHandlerEvent {
|
||||
files: File[];
|
||||
};
|
||||
|
||||
@Component({
|
||||
templateUrl: 'person.edit.html',
|
||||
selector: 'person-edit',
|
||||
imports:[ButtonModule,TableModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule,DatePickerModule],
|
||||
styleUrls: ['person.edit.css'],
|
||||
providers: [DialogService]
|
||||
|
||||
})
|
||||
export class PersonEdit implements OnInit, OnDestroy {
|
||||
editRef: DynamicDialogRef | undefined;
|
||||
returnUrl ='';
|
||||
loginUser ='';
|
||||
hostsite ='';
|
||||
_error ='';
|
||||
_id= -1;
|
||||
_partnerId = -1;
|
||||
fatherList: Code[] =[];
|
||||
motherList: Code[] =[];
|
||||
sexList:Code[] =[];
|
||||
familyList: Person[] =[];
|
||||
file: File | null = null;
|
||||
fileName = '';
|
||||
isNew = false;
|
||||
validationPoints?: Code[];
|
||||
partners = signal<RelationShipView[]>([]);
|
||||
deletePartner: RelationShipView[] =[];
|
||||
msg="[Family Edit Component] ";
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private personService = inject(PersonService);
|
||||
private messageService = inject(MessageService);
|
||||
private confirmationService = inject(ConfirmationService);
|
||||
//for focus input
|
||||
// @ViewChild('mystaffid') mystaffNo!: MatInput;
|
||||
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
|
||||
private subscription:Subscription = new Subscription();
|
||||
subChanged$ = new Subject<boolean>();
|
||||
adminuserForm = this.formBuilder.group({
|
||||
id: [0], //Validators.required
|
||||
email: [''], //Validators.required
|
||||
firstname: ['',Validators.required], //Validators.required
|
||||
lastname: ['',Validators.required], //Validators.required
|
||||
phone: [''], //Validators.required
|
||||
address: [''],
|
||||
sex:['M',Validators.required],
|
||||
image: [''],
|
||||
dob: new FormControl<Date|null>(null),
|
||||
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,
|
||||
) {
|
||||
this.hostsite = this.appSetting.appSetting.attachment;
|
||||
}
|
||||
loadParentList( list: Person[], selfId: number): void {
|
||||
this.familyList = list;
|
||||
let i = 0;
|
||||
let item: Code;
|
||||
let family: Person;
|
||||
//const list = this.familyService.parentList;
|
||||
for (i = 0; i < list.length; i++)
|
||||
{
|
||||
family= list[i];
|
||||
if (family.id != selfId)
|
||||
{
|
||||
item = { id: family.id,
|
||||
name: family.firstName! + " " + family.lastName!,
|
||||
status: family.lastName!,
|
||||
active: family.alive!
|
||||
|
||||
};
|
||||
if (family.sex == 'F')
|
||||
this.motherList.push(item);
|
||||
else
|
||||
this.fatherList.push(item);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
getClassForRequire(prev:string,name:string){
|
||||
const notok = !this.adminuserForm.controls[name].valid &&
|
||||
this.adminuserForm.controls[name].touched;
|
||||
let str =prev;
|
||||
if (notok)
|
||||
str += " ng-invalid ng-dirty";
|
||||
return str;
|
||||
}
|
||||
|
||||
onFileSelected(event: any) {
|
||||
|
||||
const file: File = event.target.files[0];
|
||||
|
||||
if (file) {
|
||||
this.file = file;
|
||||
//this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded!' });
|
||||
// this.courseForm.patchValue({ attachmenFile: this.file.name });
|
||||
this.fileName = file.name;
|
||||
this.subChanged$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
deleteFile(): void {
|
||||
this.confirmationService.confirm({
|
||||
|
||||
message: 'Are you sure that you want to delete image?',
|
||||
header: 'Confirmation',
|
||||
closable: true,
|
||||
closeOnEscape: true,
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
rejectButtonProps: {
|
||||
label: 'Cancel',
|
||||
severity: 'secondary',
|
||||
outlined: true,
|
||||
},
|
||||
acceptButtonProps: {
|
||||
label: 'Delete',
|
||||
},
|
||||
accept: () => {
|
||||
this.doDeleteFile();
|
||||
},
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
doDeleteFile(): void {
|
||||
const fileName = this.adminuserForm.value.image;
|
||||
const familyId = this.adminuserForm.value.id;
|
||||
const data = { fileName, familyId };
|
||||
const delete$ = this.personService.deleteUploadFile(data);
|
||||
this.subscription.add(delete$.subscribe(
|
||||
{
|
||||
next: x => {
|
||||
if (x.statusCode == 1) {
|
||||
{
|
||||
this.adminuserForm.patchValue({ image: "" });
|
||||
this.cdr.detectChanges();
|
||||
this.messageService.add({ severity: 'success', summary: 'Delete image all ok', detail: 'file: ' + fileName });
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message });
|
||||
}
|
||||
},
|
||||
error: e => {
|
||||
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e });
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
|
||||
getFileToSave(file: File): FormData {
|
||||
|
||||
//const familyId = this.adminuserForm.value.id!;
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return formData;
|
||||
}
|
||||
// formData.append("familyId", familyId.toString());
|
||||
// console.log("upload file", familyId, file);
|
||||
saveFile(formData: FormData)
|
||||
{
|
||||
const upload$ = this.personService.uploadFile(formData);
|
||||
this.subscription.add(upload$.subscribe(
|
||||
{
|
||||
next: x => {
|
||||
if (x.statusCode == 1) {
|
||||
const filename = x.data;
|
||||
//this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded to server API: ' + x.data });
|
||||
this.adminuserForm.patchValue({ image: filename });
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
else
|
||||
this.messageService.add({ severity: 'error', summary: 'Error upload file', detail: 'Fail to upload file: ' + x.message });
|
||||
},
|
||||
error: e =>
|
||||
this.messageService.add({ severity: 'error', summary: 'Error upload file', detail: 'Fail to upload file: ' })
|
||||
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
populateSex(): void {
|
||||
let item: Code;
|
||||
item = {
|
||||
id:1,
|
||||
name:"Female",
|
||||
status:'F'
|
||||
};
|
||||
this.sexList.push(item);
|
||||
item = {
|
||||
id:2,
|
||||
name:"Male",
|
||||
status:'M'
|
||||
};
|
||||
this.sexList.push(item);
|
||||
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.populateSex();
|
||||
const id = this.config.data.id;
|
||||
console.log("family edit", this.config);
|
||||
this.loadParentList(this.config.data.familyList, id);
|
||||
//this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||
// const id = Number(this.route.snapshot.paramMap.get('id'));
|
||||
// now load thing up
|
||||
const user = Utils.getCurrentUser();
|
||||
// console.log(this.msg + "current login user ", user);
|
||||
if (user.username === '')
|
||||
alert("you are not login.");
|
||||
else
|
||||
this.loginUser = user.firstName;
|
||||
this._id = id;
|
||||
//console.log(this.msg + " " + id );
|
||||
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
|
||||
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
|
||||
if (id > 0)
|
||||
{
|
||||
this.isNew = false;
|
||||
this.subscription.add(
|
||||
this.personService.loadPersonById(id).subscribe({
|
||||
next: x => this.assignValue(x.data),
|
||||
error: e => console.log(e)
|
||||
})
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// this for disable submit button
|
||||
get isFieldsChange() {
|
||||
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
|
||||
//console.log(this.msg + 'is fields change', chan);
|
||||
return chan;
|
||||
}
|
||||
|
||||
assignValue(item:Person): void {
|
||||
this._id = item.id;
|
||||
this.fileName = item.image!;
|
||||
if (item.relationShips)
|
||||
this.populatePartner(item.relationShips, item.id);
|
||||
this.adminuserForm.patchValue({
|
||||
id: item.id,
|
||||
email: item.email,
|
||||
firstname: item.firstName,
|
||||
lastname: item.lastName,
|
||||
alive: item.alive,
|
||||
phone: item.phone,
|
||||
address: item.address,
|
||||
fatherId: item.fatherId,
|
||||
motherId: item.motherId,
|
||||
sex: item.sex,
|
||||
image: item.image
|
||||
});
|
||||
const dob = item.dob;
|
||||
if (dob)
|
||||
{
|
||||
this.adminuserForm.patchValue({
|
||||
dob: moment(dob).toDate()
|
||||
});
|
||||
}
|
||||
// disable use false//true for not disable.
|
||||
this.subChanged$.next(true);
|
||||
}
|
||||
|
||||
// convenience getter for easy access in form fields
|
||||
get f() { return this.adminuserForm.controls;}
|
||||
validate(adminuser:Person): boolean {
|
||||
let result = true;
|
||||
console.log("validate", adminuser);
|
||||
if (!adminuser.firstName)
|
||||
{
|
||||
this._error = 'firstname is blank or empty';
|
||||
result = false;
|
||||
}
|
||||
else if (adminuser.lastName == '')
|
||||
{
|
||||
this._error = 'lastname is blank or empty';
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getPartnerForSave(): RelationShip[] {
|
||||
let rlist: RelationShip[]= [];
|
||||
let i =0;
|
||||
let vitem: RelationShipView;
|
||||
let item: RelationShip;
|
||||
for (i = 0; i < this.deletePartner.length; i++)
|
||||
{
|
||||
vitem = this.deletePartner[i];
|
||||
item = {
|
||||
id: vitem.id,
|
||||
personId: vitem.personId,
|
||||
state: mState.Delete,
|
||||
relatePersonId: vitem.relatePersonId
|
||||
};
|
||||
rlist.push(item);
|
||||
console.log("get partner for save delete list ", vitem);
|
||||
}
|
||||
console.log("get partner for save before loop this.partners ", i);
|
||||
for (i = 0; i < this.partners().length; i++)
|
||||
{
|
||||
vitem = this.partners()[i];
|
||||
if (vitem.state == mState.New || vitem.state == mState.Modified)
|
||||
{
|
||||
item = {
|
||||
id: vitem.id,
|
||||
personId: vitem.personId,
|
||||
state: vitem.state,
|
||||
relatePersonId: vitem.relatePersonId
|
||||
};
|
||||
rlist.push(item);
|
||||
}
|
||||
console.log("get partner for save in partner list ", vitem);
|
||||
}
|
||||
|
||||
|
||||
return rlist;
|
||||
}
|
||||
|
||||
async onSubmit(e:Event): Promise<void> {
|
||||
e.preventDefault();
|
||||
// if form valid then go save
|
||||
//make sure
|
||||
const okToSave = this.adminuserForm.valid;
|
||||
if (okToSave)
|
||||
{
|
||||
let adminuserValue = this.adminuserForm.value;
|
||||
|
||||
let item:Person = {
|
||||
id: adminuserValue.id,
|
||||
email: adminuserValue.email,
|
||||
firstName: adminuserValue.firstname,
|
||||
lastName: adminuserValue.lastname,
|
||||
alive: adminuserValue.alive,
|
||||
phone: adminuserValue.phone,
|
||||
address: adminuserValue.address,
|
||||
image:adminuserValue.image,
|
||||
fatherId: adminuserValue.fatherId,
|
||||
motherId: adminuserValue.motherId,
|
||||
sex: adminuserValue.sex
|
||||
};
|
||||
|
||||
if (adminuserValue.dob)
|
||||
{
|
||||
item.dob = moment(adminuserValue.dob).format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
this._error ='';
|
||||
const allOK = this.validate(item);
|
||||
if (allOK)
|
||||
{
|
||||
let container:any = {
|
||||
person: item
|
||||
};
|
||||
if (this.file != null)
|
||||
{
|
||||
container.formData = await Utils.toBase64(this.file);
|
||||
container.fileName = this.file.name;
|
||||
console.log('image as base64 ', container);
|
||||
}
|
||||
container.person.relationShips = this.getPartnerForSave();
|
||||
this.subscription.add (
|
||||
this.personService.savePerson(container).subscribe({
|
||||
next: x => {
|
||||
if (x.statusCode >= 1)
|
||||
{
|
||||
item.id = x.data;
|
||||
// this.messageService.add({severity:'success', summary: 'Save user', detail: item.firstName + " " + item.lastName });
|
||||
//this.router.navigate([this.returnUrl]);
|
||||
console.log("the person after save", item);
|
||||
this.ref.close(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
|
||||
}
|
||||
},
|
||||
error: e => {
|
||||
const message = e.message;
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: message });
|
||||
console.error("error ", e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
else
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
|
||||
}
|
||||
}
|
||||
getFamilyById(id: number): Person | undefined {
|
||||
let result:Person |undefined;
|
||||
result = this.familyList.find(x => x.id == id);
|
||||
return result;
|
||||
}
|
||||
populatePartner(list: RelationShip[], personId: number): void {
|
||||
let item: RelationShip;
|
||||
let vitem: RelationShipView;
|
||||
let vlist: RelationShipView[] =[];
|
||||
let i = 0;
|
||||
let family:Person|undefined;
|
||||
for (i = 0; i < list.length; i++)
|
||||
{
|
||||
item = list[i];
|
||||
if (item.relatePersonId != personId)
|
||||
{
|
||||
family = this.getFamilyById(item.relatePersonId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
family = this.getFamilyById(item.personId);
|
||||
}
|
||||
if (family)
|
||||
{
|
||||
vitem = {
|
||||
id: item.id,
|
||||
personId: item.personId,
|
||||
relatePersonId: item.relatePersonId,
|
||||
pfirstName: family.firstName,
|
||||
plastName: family.lastName,
|
||||
sex: family.sex,
|
||||
state:mState.NoChange
|
||||
};
|
||||
vlist.push(vitem);
|
||||
}
|
||||
}
|
||||
if (vlist.length > 0)
|
||||
this.partners.set(vlist);
|
||||
}
|
||||
|
||||
|
||||
addPartner(): void {
|
||||
const title = "Partner";
|
||||
this.showPickPerson(title, (sfamily: Person) => {
|
||||
let item: RelationShipView = {
|
||||
id: this._partnerId--,
|
||||
state: mState.New,
|
||||
personId: this._id,
|
||||
relatePersonId: sfamily.id,
|
||||
pfirstName: sfamily.firstName,
|
||||
plastName: sfamily.lastName,
|
||||
sex: sfamily.sex,
|
||||
};
|
||||
const fm = this.partners();
|
||||
fm.push(item);
|
||||
this.partners.set(fm);
|
||||
this.subChanged$.next(false);
|
||||
console.log("after put in partners list", this.partners());
|
||||
});
|
||||
}
|
||||
|
||||
onDeletePartner(item:RelationShipView): void {
|
||||
this.confirmationService.confirm({
|
||||
|
||||
message: 'Are you sure that you want to delete partner?',
|
||||
header: 'Confirmation',
|
||||
closable: true,
|
||||
closeOnEscape: true,
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
rejectButtonProps: {
|
||||
label: 'Cancel',
|
||||
severity: 'secondary',
|
||||
outlined: true,
|
||||
},
|
||||
acceptButtonProps: {
|
||||
label: 'Delete',
|
||||
},
|
||||
accept: () => {
|
||||
this.doDeletePartner(item);
|
||||
},
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
doDeletePartner(item:RelationShipView): void {
|
||||
if (item.id > 0)
|
||||
{
|
||||
this.deletePartner.push(item);
|
||||
}
|
||||
const dlist = this.partners().filter(x => x.id != item.id);
|
||||
this.partners.set(dlist);
|
||||
console.log("after delete partners list", this.partners());
|
||||
this.subChanged$.next(false);
|
||||
}
|
||||
|
||||
showPickPerson(title:string, callback:(id: Person) => void) :void {
|
||||
const ref = this.dialogService.open(Pickperson, {
|
||||
data: {
|
||||
familyList: this.familyList,
|
||||
},
|
||||
header: title,
|
||||
width: '80%',
|
||||
|
||||
maximizable: true
|
||||
});
|
||||
|
||||
ref.onClose.subscribe((item: Person) => {
|
||||
if (item) {
|
||||
//console.log("after close ward edit", item);
|
||||
this.messageService.add({severity:'success', summary: 'Select', detail: item.firstName!});
|
||||
//update the current list
|
||||
callback(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cancel(e:Event):void {
|
||||
e.preventDefault();
|
||||
this.ref.close(null);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
|
||||
import { AppSettingService } from '../shares';
|
||||
import { TreeNode } from 'primeng/api';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PersonService {
|
||||
public searchCriteria: StaffSearch;
|
||||
public parentList: Person[] =[];
|
||||
constructor(private http: HttpClient,
|
||||
|
||||
private appSetting :AppSettingService
|
||||
) {
|
||||
this.searchCriteria = {
|
||||
email:'',
|
||||
firstName: '',
|
||||
lastName:''
|
||||
};
|
||||
|
||||
}
|
||||
searchPersons(criteria: StaffSearch): Observable<ResultModel<Person[]>> {
|
||||
let config = { headers : { 'Content-Type': 'application/json' } };
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson";
|
||||
return this.http.post<ResultModel<Person[]>>(baseUrl, criteria, config);
|
||||
}
|
||||
|
||||
loadPersonById(id:number): Observable<ResultModel<Person>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
|
||||
/*
|
||||
const params = new HttpParams().set("id", ""+id);
|
||||
const headers = new HttpHeaders().set('Content-Type', 'application/json');
|
||||
const options = {
|
||||
headers: headers,
|
||||
params: params
|
||||
};
|
||||
*/
|
||||
|
||||
return this.http.get<ResultModel<Person>>(baseUrl + "/GetById/" + id);
|
||||
}
|
||||
loadPersonFamily(id: number): Observable<ResultModel<TreeNode>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
|
||||
return this.http.get<ResultModel<TreeNode>>(baseUrl + "/GetByPersonFamily/" + id);
|
||||
}
|
||||
|
||||
loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable<ResultModel<TreeNode[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
|
||||
const data = {useFather,useMother};
|
||||
return this.http.post<ResultModel<TreeNode[]>>(baseUrl + "/GetFamilyTreeBy", data);
|
||||
}
|
||||
|
||||
loadChildrenById(fatherId:number, motherId: number): Observable<ResultModel<Person[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
|
||||
const data = {fatherId,motherId};
|
||||
|
||||
return this.http.post<ResultModel<Person[]>>(baseUrl + "/GetChildress/" ,data);
|
||||
}
|
||||
loadRelationshipById(personId:number): Observable<ResultModel<RelationShip[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl;
|
||||
return this.http.get<ResultModel<RelationShip[]>>(baseUrl + "/GetByPersonId/" +personId);
|
||||
}
|
||||
savePerson(data:PersonContainer): Observable<ResultModel<number>> { //insert Adminuser
|
||||
let config = { headers : { 'Content-Type': 'application/json' } };
|
||||
console.log("save family", data);
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson";
|
||||
return this.http.post<ResultModel<number>>(baseUrl, data, config);
|
||||
}
|
||||
|
||||
deletePerson(id:number): Observable<ResultModel<number>>{
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
|
||||
const data = {id};
|
||||
return this.http.post<ResultModel<number>>(baseUrl + "/DeleteById" ,data);
|
||||
}
|
||||
uploadFile(data: any): Observable<ResultModel<string>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
|
||||
return this.http.post<ResultModel<string>>(baseUrl, data);
|
||||
}
|
||||
deleteUploadFile(data: any): Observable<ResultModel<number>> {
|
||||
//data ={filename};
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
|
||||
return this.http.post<ResultModel<number>>(baseUrl, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<div class="flex-row w-full h-full">
|
||||
<p-table #dt2 [value]="familyList()" sortMode="multiple" styleClass="p-datatable-sm"
|
||||
styleClass="p-datatable-sm" responsiveLayout="stack" paginatorDropdownAppendTo="body"
|
||||
dataKey="id" selectionMode="single" [(selection)]="selectedPerson"
|
||||
[paginator]="true" [globalFilterFields]="['lastName', 'firstName', 'sex']"
|
||||
[rows]="10" [rowsPerPageOptions]="[5,10, 20,50]"
|
||||
[loading]="loading">
|
||||
<ng-template #caption>
|
||||
<div class="flex">
|
||||
<p-iconfield iconPosition="left" class="ml-auto">
|
||||
<p-inputicon>
|
||||
<i class="pi pi-search"></i>
|
||||
</p-inputicon>
|
||||
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
|
||||
</p-iconfield>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="sex">Sex<p-sortIcon field="sex"></p-sortIcon>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-user>
|
||||
<tr [pSelectableRow]="user">
|
||||
<td>{{user.lastName}}</td>
|
||||
<td>{{user.firstName}}</td>
|
||||
<td>{{user.sex}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
<div class="flex justify-end mr-2 mb-2 p-2">
|
||||
<button pButton type="submit" class="p-button-sm mr-2"
|
||||
icon="pi pi-check" iconPos="left" label="Select"
|
||||
(click)="select($event)" [disabled]="selectedPerson == null" ></button>
|
||||
<button pButton pRipple class="p-button-sm" type="button"
|
||||
icon="pi pi-times" iconPos="left"
|
||||
label="Cancel" (click)="cancel($event)"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Table, TableModule } from 'primeng/table';
|
||||
import { Person } from '../models';
|
||||
import { IconFieldModule } from 'primeng/iconfield';
|
||||
import { InputIconModule } from 'primeng/inputicon';
|
||||
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pickperson',
|
||||
imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule],
|
||||
templateUrl: './pickperson.html',
|
||||
styleUrl: './pickperson.css'
|
||||
})
|
||||
export class Pickperson implements OnInit, OnDestroy{
|
||||
@ViewChild(Table) dt2!: Table;
|
||||
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig)
|
||||
{
|
||||
|
||||
}
|
||||
loading = false;
|
||||
familyList = signal<Person[]>([]);
|
||||
selectedPerson!: Person;
|
||||
handleInput(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
this.dt2.filterGlobal(value, 'contains');
|
||||
}
|
||||
|
||||
edit(id: number): void {
|
||||
|
||||
}
|
||||
cancel(e:Event):void {
|
||||
e.preventDefault();
|
||||
this.ref.close(null);
|
||||
}
|
||||
select(e:Event):void {
|
||||
e.preventDefault();
|
||||
this.ref.close(this.selectedPerson);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
console.log("pick person the familyList", this.config);
|
||||
this.familyList.set(this.config.data.familyList);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||
import { Utils } from '../shares';
|
||||
import {AuthenticationService} from '../user-services/authentication.service';
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthGuard {
|
||||
msg ="[AuthGuard] ";
|
||||
constructor(private router: Router,
|
||||
private authService :AuthenticationService ) { }
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const roleAllowed = route.data['roleAllowed'];
|
||||
const user = Utils.getCurrentUser();
|
||||
const userRole = Utils.getUserRole(user);
|
||||
let hasLoggedIn = Utils.getIsAuth();
|
||||
//console.log(this.msg + " the userlogin ", user, hasLoggedIn);
|
||||
if (hasLoggedIn) {
|
||||
|
||||
if (roleAllowed != undefined)
|
||||
{
|
||||
//it number toString
|
||||
// console.log("[auth-guard] ther roleAllow ", roleAllowed);
|
||||
// console.log("[auth-guard] before roleAllow ", roleAllowed,userRole);
|
||||
if (roleAllowed.indexOf(userRole.toString()) != -1) {
|
||||
// console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
this.authService.logout();
|
||||
this.router.navigate(['/login']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else //if no role define just return hasLoggedIn
|
||||
{
|
||||
return hasLoggedIn;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this.authService.logout();
|
||||
this.router.navigate(['/login']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// not logged in so redirect to login page with the return url
|
||||
//this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
|
||||
this.authService.logout();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './auth.guard';
|
||||
@@ -0,0 +1,54 @@
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
|
||||
export class CommonUtilities {
|
||||
/** Display the blob */
|
||||
static displayFileInNewTab(response:any, defaultFileName:string) {
|
||||
const contentType = response.headers.get('content-type');
|
||||
const blob = new Blob([response.body], { type: contentType });
|
||||
const fileName = this.getFileName(response,defaultFileName);
|
||||
const nav = (window.navigator as any);
|
||||
if (nav.msSaveOrOpenBlob) {
|
||||
nav.msSaveOrOpenBlob(blob, fileName);
|
||||
} else {
|
||||
const data = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = data;
|
||||
link.target = '_blank';
|
||||
link.click();
|
||||
}
|
||||
}
|
||||
/*** Download the blob ***/
|
||||
static downloadFile(response:any, defaultFileName:string) {
|
||||
const contentType = response.headers.get('content-type');
|
||||
const blob = new Blob([response.body], { type: contentType });
|
||||
const fileName = this.getFileName(response,defaultFileName);
|
||||
const nav = (window.navigator as any);
|
||||
if (typeof nav.msSaveBlob === 'function')
|
||||
{
|
||||
console.log("download the report saveblob");
|
||||
nav.msSaveBlob(blob, fileName);
|
||||
console.log("download the report saveblob done");
|
||||
}
|
||||
else {
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
console.log("no msSaveBlob download the report saveblob done");
|
||||
//link.download? = fileName;
|
||||
link.download = fileName;
|
||||
link.click();
|
||||
}
|
||||
}
|
||||
/** Get the attachment file name */
|
||||
static getFileName(response:any, defaultFileName:string) {
|
||||
const contentDis = response.headers.get('content-disposition');
|
||||
let fileName =defaultFileName;
|
||||
if (contentDis && contentDis.indexOf('attachment') !== -1) {
|
||||
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
||||
const matches = filenameRegex.exec(contentDis);
|
||||
if (matches != null && matches[1]) {
|
||||
fileName = matches[1].replace(/['"]/g, '');
|
||||
}
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
export function initializeApp_old(appInitService: AppSettingService) {
|
||||
return (): Promise<any> => {
|
||||
return appInitService.loadAppSetting_p();
|
||||
}
|
||||
|
||||
};
|
||||
export function initializeApp() {
|
||||
return (): Promise<any> => {
|
||||
return inject(AppSettingService).loadAppSetting_p();
|
||||
}
|
||||
// return () => inject(AppSettingService).loadAppSetting();
|
||||
};
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AppSettingService {
|
||||
private _appsetting: any;
|
||||
constructor(private http: HttpClient
|
||||
) { }
|
||||
|
||||
public loadAppSetting_p(): Promise<any> {
|
||||
const source$ = this.http.get("config/appsetting.json")
|
||||
const rs$ = firstValueFrom(source$)
|
||||
.then(x => {
|
||||
this._appsetting = x;
|
||||
console.log("[AppSettingService] assign", x);
|
||||
});
|
||||
return rs$;
|
||||
}
|
||||
public loadAppSetting(): Observable<any> {
|
||||
const source$ = this.http.get("config/appsetting.json")
|
||||
return source$;
|
||||
}
|
||||
set appSetting(value: any) {
|
||||
this._appsetting = value;
|
||||
}
|
||||
get appSetting(): any {
|
||||
return this._appsetting;
|
||||
}
|
||||
|
||||
}
|
||||
/* angular 19 new one in app.config.ts
|
||||
provideAppInitializer(initializeApp()),
|
||||
|
||||
/* module put this
|
||||
to use it
|
||||
in app.module.ts
|
||||
add
|
||||
1)
|
||||
providers: [
|
||||
{ provide : APP_INITIALIZER, multi : true, deps : [AppSettingService],
|
||||
useFactory: initializeApp
|
||||
},
|
||||
]
|
||||
|
||||
2)
|
||||
other this one work too.
|
||||
providers: [
|
||||
{
|
||||
provide : APP_INITIALIZER,
|
||||
multi : true,
|
||||
deps : [AppSettingService],
|
||||
useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting()
|
||||
}
|
||||
]
|
||||
|
||||
to use it calling something like this
|
||||
export class TestComponent {
|
||||
public test1ServiceUrl: string;
|
||||
|
||||
constructor(public configService: AppConfigService) {
|
||||
this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
import { AuthenticationService } from '../user-services';
|
||||
//import { ToastrService } from 'ngx-toastr';
|
||||
import { NavigationExtras, Router } from '@angular/router';
|
||||
/*
|
||||
@Injectable()
|
||||
export class ErrorInterceptor implements HttpInterceptor {
|
||||
//private authenticationService: AuthenticationService;
|
||||
constructor(
|
||||
private router: Router,
|
||||
// private toastr: ToastrService,
|
||||
) { }
|
||||
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// console.log("I am intercept to catch error status = 401...", request);
|
||||
return next.handle(request).pipe(catchError(err => {
|
||||
/* no use yet
|
||||
if (err) {
|
||||
switch(err.status) {
|
||||
case 400:
|
||||
if (err.error.errors) {
|
||||
const modalStateErrors = [];
|
||||
for (const key in err.error.errors) {
|
||||
if (err.error.errors[key]) {
|
||||
modalStateErrors.push(err.error.errors[key]);
|
||||
}
|
||||
}
|
||||
throw modalStateErrors;
|
||||
} else {
|
||||
this.toastr.error(err.statusText, err.status);
|
||||
}
|
||||
break;
|
||||
case 401:
|
||||
this.toastr.error(err.statusText, err.status);
|
||||
break;
|
||||
case 404:
|
||||
this.router.navigateByUrl('/not-found');
|
||||
break;
|
||||
case 500:
|
||||
const navigationExtras: NavigationExtras = {state:{error:err.error}};
|
||||
this.router.navigateByUrl('/server-error', navigationExtras);
|
||||
break;
|
||||
default:
|
||||
this.toastr.error('Something unexpected went wrong');
|
||||
console.log("this error interceptor",err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if (err.status === 401) {
|
||||
// auto logout if 401 response returned from api Unauthorized
|
||||
// this.authenticationService.logout();
|
||||
//location.reload();
|
||||
const error = err.error.message || err.statusText;
|
||||
console.log("After I Get Error status = 401...", error, err);
|
||||
return throwError(error);
|
||||
}
|
||||
else {
|
||||
console.log("I Get Error .", err);
|
||||
return throwError(err);
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
//new one
|
||||
//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/
|
||||
////////////////
|
||||
export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
|
||||
const authenticationService = inject(AuthenticationService);
|
||||
|
||||
// logger.log(`Request is on its way to ${req.url}`);
|
||||
return next(req).pipe(catchError(err => {
|
||||
if (err.status === 401) {
|
||||
// auto logout if 401 response returned from api Unauthorized
|
||||
// this.authenticationService.logout();
|
||||
//location.reload();
|
||||
const error = err.error.message || err.statusText;
|
||||
// console.log("After I Get Error status = 401...", error, err);
|
||||
return throwError(() => error);
|
||||
}
|
||||
else {
|
||||
console.log("I Get Error .", err);
|
||||
return throwError(() => err);
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { ConfigureUrl } from '../models';
|
||||
import { AppSettingService } from './appsetting';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HttpUtility {
|
||||
private baseApiUrl: string = "";
|
||||
constructor(private http: HttpClient,
|
||||
private appSetting :AppSettingService,
|
||||
private router: Router) {
|
||||
}
|
||||
/**
|
||||
* getFile
|
||||
* @param url
|
||||
*/
|
||||
public getFileAsText(url: string): Observable<any> {
|
||||
return this.http
|
||||
.get(this.getApiUrl(url), { responseType: 'blob' as 'text' })
|
||||
.pipe(catchError(e => this.handleError(e)));
|
||||
}
|
||||
/**
|
||||
* getFile
|
||||
* @param url
|
||||
*/
|
||||
public getFile(url: string): Observable<any> {
|
||||
return this.http
|
||||
.get(this.getApiUrl(url), { responseType: 'blob' as 'json' })
|
||||
.pipe(catchError(e => this.handleError(e)));
|
||||
}
|
||||
|
||||
/**
|
||||
* handleError
|
||||
* @param response
|
||||
*/
|
||||
private handleError(errorResponse: HttpErrorResponse) {
|
||||
// in a real world app, we may send the server to some remote logging infrastructure
|
||||
// instead of just logging it to the console
|
||||
return throwError(errorResponse);
|
||||
}
|
||||
//get excel file zip file etc ..
|
||||
/*
|
||||
allowedToDisplay = [
|
||||
this.mimeConstant.png,
|
||||
this.mimeConstant.jpeg,
|
||||
this.mimeConstant.jpg,
|
||||
this.mimeConstant.gif,
|
||||
this.mimeConstant.txt,
|
||||
this.mimeConstant.pdf
|
||||
];
|
||||
*/
|
||||
/* how to use it
|
||||
this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe(
|
||||
catchError(err => {
|
||||
this.snackBar.error(err || this.constants.error_Getting_photo);
|
||||
return EMPTY;
|
||||
}),
|
||||
finalize(() => {
|
||||
this.spinner.hide();
|
||||
})).subscribe(response => {
|
||||
if (response) {
|
||||
if (isDownload) {
|
||||
CommonUtilities.downloadFile(response);
|
||||
} else {
|
||||
if (response.body && response.body.type) {
|
||||
if (this.allowedToDisplay.includes(response.body.type)) {
|
||||
CommonUtilities.displayFileInNewTab(response);
|
||||
} else {
|
||||
this.temporaryFile = response;
|
||||
this.downloadWarning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
public getFileResponse(url: string, data?: any): Observable<any> {
|
||||
if (data) {
|
||||
return this.http.post(this.getApiUrl(url),
|
||||
data,
|
||||
{ responseType: 'blob', observe: 'response' })
|
||||
.pipe(catchError(e => this.handleError(e)));
|
||||
|
||||
}
|
||||
return this.http
|
||||
.get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' })
|
||||
.pipe(catchError(e => this.handleError(e)));
|
||||
}
|
||||
|
||||
public getFileAsPDF(url: string, data: any): Observable<any> {
|
||||
let updateURL;
|
||||
if (data) {
|
||||
updateURL = url + data;
|
||||
} else {
|
||||
updateURL = url;
|
||||
}
|
||||
return this.http
|
||||
.get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' })
|
||||
.pipe(catchError(e => this.handleError(e)));
|
||||
}
|
||||
/**
|
||||
* getApiUrl
|
||||
* @param url
|
||||
*/
|
||||
private getApiUrl(url:string) {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl;
|
||||
return baseUrl + "/"+ url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export * from './utils';
|
||||
export {initializeApp, AppSettingService} from './appsetting';
|
||||
export * from './error.interceptor';
|
||||
export * from './jwt.interceptor';
|
||||
export * from './lookup.service';
|
||||
export * from './httpfile.service';
|
||||
export * from './CommonUtilities';
|
||||
export * from './timeinput';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Utils } from './utils';
|
||||
/*
|
||||
@Injectable()
|
||||
export class JwtInterceptor implements HttpInterceptor {
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// add authorization header with jwt token if available
|
||||
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
|
||||
console.log('I am interceptor debug for currentUser obj', currentUser);
|
||||
if (currentUser && currentUser.token) {
|
||||
//console.log('I am interceptor add authorization header with jwt token');
|
||||
request = request.clone({
|
||||
setHeaders: {
|
||||
Authorization: `Bearer ${currentUser.token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
return next.handle(request);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
new one
|
||||
////////////////
|
||||
*/
|
||||
export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
|
||||
//const logger = inject(Logger);
|
||||
//logger.log(`Request is on its way to ${req.url}`);
|
||||
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
|
||||
// console.log('I am interceptor debug for currentUser obj', currentUser);
|
||||
if (currentUser && currentUser.token && currentUser.token != "") {
|
||||
// console.log('I am interceptor add authorization header with jwt token');
|
||||
request = request.clone({
|
||||
setHeaders: {
|
||||
Authorization: `Bearer ${currentUser.token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
return next(request);
|
||||
}
|
||||
|
||||
////////////////
|
||||
/*
|
||||
providers: [provideHttpClient(withInterceptors([loggerInterceptor]))]
|
||||
*/
|
||||
@@ -0,0 +1,136 @@
|
||||
import { Injectable, Inject , Output} from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { ConfigureUrl,Lookup,LookupEdit,ResultModel
|
||||
} from '../models';
|
||||
import { Utils,AppSettingService } from '../shares';
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class LookupService {
|
||||
msg: string = "[lookupService] ";
|
||||
_lookupByStatus:any;
|
||||
_staffList:any;
|
||||
_clientList:any;
|
||||
_jobList:any;
|
||||
constructor(private http: HttpClient,
|
||||
private appSetting :AppSettingService
|
||||
) {
|
||||
// this default when load look for previous login information
|
||||
// if no need then comment out downthere
|
||||
this.setAllNull();
|
||||
}
|
||||
setAllNull() :void {
|
||||
// this._lhdList = undefined;
|
||||
this._lookupByStatus = {};
|
||||
this._staffList = null;
|
||||
this._clientList = null;
|
||||
this._jobList = null;
|
||||
}
|
||||
loadLookupEditBystatus(type:string): Observable<ResultModel<LookupEdit[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit";
|
||||
const params = new HttpParams().append('type', type);
|
||||
console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type);
|
||||
return this.http.get<ResultModel<LookupEdit[]>>(baseUrl, {params});
|
||||
}
|
||||
loadLookupById(id:number, type:string): Observable<ResultModel<LookupEdit>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
|
||||
const params = new HttpParams()
|
||||
.append('id', id)
|
||||
.append('type', type);
|
||||
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
|
||||
}
|
||||
saveLookup(item:LookupEdit): Observable<ResultModel<number>> { //insert Lookup
|
||||
this.setAllNull();
|
||||
let config = { headers : { 'Content-Type': 'application/json' } };
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
|
||||
return this.http.post<ResultModel<number>>(baseUrl, item, config);
|
||||
}
|
||||
deleteLookup(id:number): Observable<ResultModel<number>>{
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
|
||||
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id );
|
||||
}
|
||||
|
||||
loadLookupBystatus(type:string): Observable<ResultModel<Lookup[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs";
|
||||
console.log(this.msg + "loadLookupBystatus", this._lookupByStatus);
|
||||
if (this._lookupByStatus[type])
|
||||
{
|
||||
console.log(this.msg + "Not go server this LoadJobs is in cache ",type);
|
||||
return of(this._lookupByStatus[type]);
|
||||
}
|
||||
else
|
||||
{
|
||||
const params = new HttpParams().append('type', type);
|
||||
console.log(this.msg + "go server this loadLookupBystatus is in cache ", type);
|
||||
return this.http.get<ResultModel<Lookup[]>>(baseUrl, {params})
|
||||
.pipe(map(resultModel => {
|
||||
this._lookupByStatus[type] = resultModel;
|
||||
return resultModel;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
loadStaffs(): Observable<ResultModel<Lookup[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs";
|
||||
/* const params = new HttpParams()
|
||||
.append('id', id)
|
||||
.append('type', type);
|
||||
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
|
||||
*/
|
||||
if (this._staffList != null)
|
||||
{
|
||||
return of(this._staffList);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
|
||||
.pipe(map( x => {
|
||||
this._staffList = x;
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
}
|
||||
loadClients(): Observable<ResultModel<Lookup[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients";
|
||||
/* const params = new HttpParams()
|
||||
.append('id', id)
|
||||
.append('type', type);
|
||||
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
|
||||
*/
|
||||
if (this._clientList != null)
|
||||
{
|
||||
return of(this._clientList);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
|
||||
.pipe(map(resultModel => {
|
||||
this._clientList = resultModel;
|
||||
return resultModel;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
loadJobs(): Observable<ResultModel<Lookup[]>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs";
|
||||
/* const params = new HttpParams()
|
||||
.append('id', id)
|
||||
.append('type', type);
|
||||
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
|
||||
*/
|
||||
if (this._jobList != null)
|
||||
{
|
||||
return of(this._jobList);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
|
||||
.pipe(map(x => {
|
||||
this._jobList = x;
|
||||
return x;
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'time-input',
|
||||
imports:[ReactiveFormsModule,CommonModule],
|
||||
template: `
|
||||
<input
|
||||
type="text"
|
||||
[formControl]="timeControl"
|
||||
(input)="onInput($event)"
|
||||
(blur)="onBlur()"
|
||||
class="time-input"
|
||||
style="width: 80px; text-align: center;"
|
||||
/>
|
||||
`,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => TimeInputComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
|
||||
@Input() placeholder: string = 'HH:MM';
|
||||
@Input() initialValue: string = ''; // Added initialValue input
|
||||
@Input() disabled: boolean = false;
|
||||
|
||||
timeControl = new FormControl('', [
|
||||
Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
|
||||
]);
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
private _value: string = '';
|
||||
private onChange: (value: string) => void = () => {};
|
||||
private onTouched: () => void = () => {};
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
|
||||
this.onChange(value || ''); // Pass the value to the parent form
|
||||
});
|
||||
if (this.initialValue) {
|
||||
this.writeValue(this.initialValue);
|
||||
}
|
||||
this.timeControl.disable();
|
||||
if(!this.disabled){
|
||||
this.timeControl.enable();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(val: string) {
|
||||
this._value = val;
|
||||
this.onChange(val);
|
||||
this.onTouched();
|
||||
}
|
||||
|
||||
writeValue(value: string): void {
|
||||
this._value = value || '';
|
||||
this.formatInput(this._value); // Use the formatting function
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
if (isDisabled) {
|
||||
this.timeControl.disable();
|
||||
} else {
|
||||
this.timeControl.enable();
|
||||
}
|
||||
}
|
||||
|
||||
onInput(event: Event): void {
|
||||
let input = (event.target as HTMLInputElement).value;
|
||||
input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
|
||||
|
||||
if (input.length > 4) {
|
||||
input = input.slice(0, 4); // Limit to 4 digits
|
||||
}
|
||||
|
||||
let formatted = '';
|
||||
if (input.length > 2) {
|
||||
formatted = input.slice(0, 2) + ':' + input.slice(2);
|
||||
} else {
|
||||
formatted = input;
|
||||
}
|
||||
this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
|
||||
this.value = formatted;
|
||||
}
|
||||
|
||||
onBlur(): void {
|
||||
this.onTouched();
|
||||
if (this.timeControl.valid) {
|
||||
return;
|
||||
}
|
||||
if (this.timeControl.value?.length === 0) {
|
||||
this.timeControl.setValue('', {emitEvent: false});
|
||||
this.value = '';
|
||||
return
|
||||
}
|
||||
this.formatInput(this.timeControl.value);
|
||||
}
|
||||
|
||||
private formatInput(val: string | null): void {
|
||||
if (!val) {
|
||||
this.timeControl.setValue('', {emitEvent: false});
|
||||
this.value = '';
|
||||
return;
|
||||
}
|
||||
let numbersOnly = val.replace(/[^0-9]/g, '');
|
||||
let formatted = '';
|
||||
|
||||
if (numbersOnly.length > 2) {
|
||||
formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
|
||||
} else {
|
||||
formatted = numbersOnly;
|
||||
}
|
||||
if (formatted.length === 5 && this.timeControl.valid) {
|
||||
this.timeControl.setValue(formatted, {emitEvent: false});
|
||||
this.value = formatted;
|
||||
} else if (numbersOnly.length <= 2) {
|
||||
this.timeControl.setValue(formatted, {emitEvent: false});
|
||||
this.value = formatted;
|
||||
} else {
|
||||
this.timeControl.setValue('00:00', {emitEvent: false});
|
||||
this.value = '00:00'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
import moment from "moment";
|
||||
import { Person, User, userRole } from "../models"
|
||||
import { TreeNode } from "primeng/api";
|
||||
|
||||
type MyProName = "fatherId" | "motherId";
|
||||
|
||||
export class Utils {
|
||||
static toBase64 (file:any)
|
||||
{
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = reject;
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
static getBase64(file: any) {
|
||||
let result: any;
|
||||
var reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = function () {
|
||||
result = reader.result;
|
||||
console.log('tobase64 ', result);
|
||||
};
|
||||
reader.onerror = function (error) {
|
||||
console.log('Error: ', error);
|
||||
};
|
||||
}
|
||||
static getCurrentUser(): User {
|
||||
|
||||
let myUser = new User();
|
||||
myUser.username = "";
|
||||
const obj = sessionStorage.getItem('currentUser');
|
||||
//const obj = localStorage.getItem('currentUser');
|
||||
if (obj != undefined) {
|
||||
let user = JSON.parse(obj);
|
||||
myUser.firstName = user.firstName;
|
||||
myUser.id = user.id;
|
||||
myUser.lastName = user.lastName;
|
||||
myUser.role = user.role;
|
||||
myUser.username = user.username;
|
||||
myUser.email = user.email;
|
||||
myUser.token = user.token;
|
||||
myUser.phone = user.phone;
|
||||
myUser.department = user.department;
|
||||
myUser.position = user.position;
|
||||
myUser.managerEmail = user.managerEmail;
|
||||
|
||||
|
||||
|
||||
}
|
||||
// console.log("[util] getcurrentUser",myUser);
|
||||
return myUser;
|
||||
}
|
||||
static setLocalStore(user: User): void {
|
||||
if (user && user.token) {
|
||||
// store user details and jwt token in local storage to keep user logged in between page refreshes
|
||||
// localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
sessionStorage.setItem('currentUser', JSON.stringify(user));
|
||||
}
|
||||
}
|
||||
|
||||
static getLastMonth(): Date {
|
||||
let result = moment().subtract(1, 'months').toDate();
|
||||
result.setDate(1);
|
||||
return result;
|
||||
}
|
||||
static getHome(): string {
|
||||
let result ="";
|
||||
const user = this.getCurrentUser();
|
||||
const urole = user.role;
|
||||
if (urole == userRole.Accounting)
|
||||
{
|
||||
result = "staffworkw";
|
||||
}
|
||||
else if (urole == userRole.Admin)
|
||||
{
|
||||
result = "staff";
|
||||
}
|
||||
else if (urole == userRole.ServiceManager)
|
||||
{
|
||||
result = "servicetask";
|
||||
}
|
||||
else if (urole == userRole.WorkShop)
|
||||
{
|
||||
result = "staffwork";
|
||||
}
|
||||
else if (urole == userRole.Normal)
|
||||
{
|
||||
result = "staff";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static getTimeStr(val:Date): string {
|
||||
let result = "00:00";
|
||||
if (val)
|
||||
{
|
||||
const hh = val.getHours();
|
||||
const min = val.getMinutes();
|
||||
if (hh < 10)
|
||||
result = '0' + hh.toString();
|
||||
else
|
||||
result = hh.toString();
|
||||
if (min < 10)
|
||||
result = result + ":0" + min.toString();
|
||||
else
|
||||
result = result + ":" + min.toString();
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static canRunReport(role: number): boolean {
|
||||
let result = false;
|
||||
if (role == userRole.Admin)
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
static getUserRole(user: User): number {
|
||||
/*
|
||||
sysadmin
|
||||
request_manager
|
||||
ward_user
|
||||
*/
|
||||
let ret = 0;
|
||||
if (user) {
|
||||
// console.log("utils user", user);
|
||||
if (user.role)
|
||||
ret = user.role;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static getIsAuth(): boolean {
|
||||
const user = this.getCurrentUser();
|
||||
if (user.username != "") {
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
static getAdminRoleName(): number {
|
||||
return userRole.Admin;
|
||||
}
|
||||
static clearCurrentUser(): void {
|
||||
sessionStorage.clear();
|
||||
//localStorage.clear();
|
||||
}
|
||||
|
||||
static formatNode(item:Person): TreeNode
|
||||
{
|
||||
let label = item.lastName + " " + item.firstName;
|
||||
let key = item.id.toString();
|
||||
let node: TreeNode ={
|
||||
key,
|
||||
label,
|
||||
type: 'person',
|
||||
data: {title: item.title, name: label, image: item.image},
|
||||
icon: 'pi pi-user'
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] {
|
||||
let result: TreeNode[] =[];
|
||||
let tree_node_child: TreeNode[];
|
||||
let node:TreeNode;
|
||||
let item: Person;
|
||||
const children = childressNodes.filter(x => x[proName] == pid);
|
||||
for (let c = 0; c < children.length; c++)
|
||||
{
|
||||
item = children[c];
|
||||
node = this.formatNode(item);
|
||||
tree_node_child = this.getChildForParentId(proName,item.id, childressNodes);
|
||||
console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child);
|
||||
if (tree_node_child.length > 0)
|
||||
{
|
||||
node.expanded = true;
|
||||
node.children = tree_node_child;
|
||||
}
|
||||
result.push(node);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] {
|
||||
let familyTree: TreeNode[] = [];
|
||||
let node:TreeNode;
|
||||
let child_nodes:TreeNode[];
|
||||
|
||||
let childressNodes:Person[];
|
||||
|
||||
let item: Person;
|
||||
const topNodes = familyList.filter(x => x[proName]! < 1);
|
||||
childressNodes = familyList.filter(x => x[proName]! > 0);
|
||||
for(let i = 0; i < topNodes.length; i++)
|
||||
{
|
||||
item = topNodes[i];
|
||||
node = Utils.formatNode(item);
|
||||
child_nodes = this.getChildForParentId(proName, item.id,childressNodes);
|
||||
console.log("populate getchildrenForParentId", child_nodes);
|
||||
if (child_nodes.length > 0)
|
||||
{
|
||||
node.expanded = true;
|
||||
node.children = child_nodes;
|
||||
}
|
||||
familyTree.push(node);
|
||||
//childressNodes =
|
||||
}
|
||||
return familyTree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './staff.component';
|
||||
export * from './staff.edit.component';
|
||||
export * from './staff.service';
|
||||
export * from './staff.pass.component';
|
||||
@@ -0,0 +1,12 @@
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
td.mat-column-edit, .mat-column-delete {
|
||||
width: 35px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<div class="shadow-2xl rounded p-2 mt-2">
|
||||
<h3>Staff List</h3>
|
||||
<div>
|
||||
<form>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<div class="">
|
||||
<label for="login">Email</label>
|
||||
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="lastname1">Surname</label>
|
||||
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="firstname">First Name</label>
|
||||
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
|
||||
class="inputfield w-full p-inputtext-sm">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
|
||||
(click)="search()"></button>
|
||||
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" iconPos="left" label="New Staff"
|
||||
(click)="newUser()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<p-table [value]="userList" sortMode="multiple" styleClass="p-datatable-sm"
|
||||
styleClass="p-datatable-sm" responsiveLayout="stack" [loading]="loading">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="email">Email<p-sortIcon field="email"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="lastname">Surname <p-sortIcon field="lastname"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="firstname">First Name <p-sortIcon field="firstname"></p-sortIcon>
|
||||
</th>
|
||||
|
||||
<th >Active </th>
|
||||
<th>Edit</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-user>
|
||||
<tr>
|
||||
<td>{{user.email}}</td>
|
||||
<td>{{user.lastname}}</td>
|
||||
<td>{{user.firstname}}</td>
|
||||
<td>
|
||||
|
||||
<i class="pi" [ngClass]="{'true-icon pi-check-circle': user.active, 'false-icon pi-times-circle': !user.active}"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button pButton type="button" icon="pi pi-pencil" class="p-button-rounded p-button-text"
|
||||
(click)="edit(user.id)"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDetectorRef} from '@angular/core';
|
||||
|
||||
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 { take } from 'rxjs/operators';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { StaffService } from './staff.service';
|
||||
import { AuthenticationService } from '../user-services';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
|
||||
@Component({
|
||||
selector: 'staff-list',
|
||||
templateUrl: './staff.component.html',
|
||||
imports:[TableModule,FormsModule,CommonModule,ButtonModule,InputTextModule],
|
||||
styleUrls: ['./staff.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class StaffComponent implements OnInit, OnDestroy{
|
||||
|
||||
private subscription:Subscription = new Subscription();
|
||||
firstname = '';
|
||||
email = '';
|
||||
lastname = '';
|
||||
loading = false;
|
||||
userList:StaffView[] = [];
|
||||
private cd = inject(ChangeDetectorRef);
|
||||
msg ="[Staff component]";
|
||||
|
||||
/*
|
||||
private store: Store<appValidationState>
|
||||
*/
|
||||
constructor(
|
||||
private staffService: StaffService,
|
||||
private authenticationService: AuthenticationService,
|
||||
private router: Router
|
||||
) {}
|
||||
getSearchCiteria(): StaffSearch {
|
||||
let criteria:StaffSearch = {
|
||||
email: this.email,
|
||||
firstName: this.firstname,
|
||||
lastName: this.lastname,
|
||||
};
|
||||
|
||||
return criteria;
|
||||
|
||||
}
|
||||
canSearch():boolean {
|
||||
let result = false;
|
||||
result = this.email !== "";
|
||||
result = result || this.lastname !== "";
|
||||
result = result || this.firstname !== "";
|
||||
return result;
|
||||
}
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.authenticationService.isHome = false;
|
||||
this.authenticationService.isReport = false;
|
||||
const prev = this.staffService.searchCriteria;
|
||||
let goload = true;
|
||||
if (prev.lastName !== '')
|
||||
{
|
||||
this.lastname = prev.lastName;
|
||||
goload = true;
|
||||
}
|
||||
if (prev.firstName !== '')
|
||||
{
|
||||
this.firstname = prev.firstName;
|
||||
goload = true;
|
||||
}
|
||||
if (prev.email !== '')
|
||||
{
|
||||
this.email = prev.email;
|
||||
goload = true;
|
||||
}
|
||||
if (goload)
|
||||
{
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
getActive(active:boolean):string {
|
||||
let result = 'false-icon pi-times-circle';
|
||||
if (active)
|
||||
result = 'true-icon pi-check-circle';
|
||||
return result;
|
||||
}
|
||||
search():void {
|
||||
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);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
newUser():void {
|
||||
//console.log("add new employee");
|
||||
this.router.navigate( ['/staff/new'], { queryParams: {returnUrl:'/staff' } });
|
||||
}
|
||||
edit(id: number) : void {
|
||||
//console.log("edit staff", id);
|
||||
this.router.navigate( ['/staff/'+id], { queryParams: {returnUrl:'/staff' } });
|
||||
}
|
||||
|
||||
deleteItem(id: number): void {
|
||||
this.staffService.deleteStaff(id)
|
||||
.pipe(take(1))
|
||||
.subscribe({ next: result => {
|
||||
console.log(this.msg + " deleteItem success", result);
|
||||
//let data = this.dataSource.data;
|
||||
//let index: number = data.findIndex(d => d.id === id);
|
||||
// console.log(this.msg + "delete from datasource");
|
||||
//data.splice(index, 1)
|
||||
//this.dataSource.data = data;
|
||||
},
|
||||
error: e => console.error(e)
|
||||
});
|
||||
//console.log(this.msg + "click button to delete");
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<div class="shadow-2xl rounded mt-2">
|
||||
<div class="mt-2 mb-2 ml-2">
|
||||
{{getEditText()}}
|
||||
</div>
|
||||
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" >
|
||||
<div class="ml-2 grid grid-cols-2 gap-3 p-2">
|
||||
<div class="">
|
||||
<label for="lastname1">Surname<strong class="app-require">*</strong></label>
|
||||
<input id="lastname1" pInputText formControlName="lastname" type="text"
|
||||
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="firstname">First Name<strong class="app-require">*</strong></label>
|
||||
<input id="firstname" pInputText formControlName="firstname" type="text"
|
||||
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="login">Email<strong class="app-require">*</strong></label>
|
||||
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
|
||||
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','email')" type="text" class="inputfield w-full">
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="phone" class="w-full">Phone</label>
|
||||
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
|
||||
</div>
|
||||
<div class="">
|
||||
<label class="flex w-full">Role<strong class="app-require">*</strong></label>
|
||||
<p-select [options]="Roles" optionLabel="name" optionValue="id" placeholder="Select access Level"
|
||||
formControlName="roleType" styleClass="w-full p-inputtext-sm mr-1"></p-select >
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="passwd" class="flex w-full">Password</label>
|
||||
<input id="passwd" pInputText formControlName="password" type="password">
|
||||
</div>
|
||||
<div class="grid align-end">
|
||||
<div class="p-field-checkbox">
|
||||
<p-checkbox inputId="active" formControlName="active" [binary]="true"></p-checkbox>
|
||||
<label for="active" class="ml-2 w-full">Active</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end mr-2 mb-2 p-2">
|
||||
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" iconPos="left" label="Save" [disabled]="isFieldsChange"></button>
|
||||
<button pButton pRipple class="p-button-sm" type="button" icon="pi pi-times" iconPos="left" label="Cancel" (click)="goBack()"></button>
|
||||
</div>
|
||||
</form>
|
||||
<!--pre>{{ adminuserForm.value|json}}</pre-->
|
||||
</div>
|
||||
@@ -0,0 +1,211 @@
|
||||
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { Subject, Subscription} from 'rxjs';
|
||||
import { MessageService } from 'primeng/api';
|
||||
|
||||
import {Staff, Code, userRole} from '../models';
|
||||
import { LookupService, Utils } from '../shares';
|
||||
import { StaffService } from './staff.service';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { SelectModule } from 'primeng/select';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'staff.edit.component.html',
|
||||
selector: 'staff-edit',
|
||||
imports:[ButtonModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule],
|
||||
styleUrls: ['staff.edit.component.css']
|
||||
})
|
||||
export class StaffEditComponent implements OnInit, OnDestroy {
|
||||
returnUrl ='';
|
||||
loginUser ='';
|
||||
_error ='';
|
||||
_id= -1;
|
||||
Roles: Code[];
|
||||
isNew = false;
|
||||
validationPoints?: Code[];
|
||||
msg="[adminUser Component] ";
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
//for focus input
|
||||
// @ViewChild('mystaffid') mystaffNo!: MatInput;
|
||||
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
|
||||
private subscription:Subscription = new Subscription();
|
||||
subChanged$ = new Subject<boolean>();
|
||||
adminuserForm = this.formBuilder.group({
|
||||
id: [0], //Validators.required
|
||||
email: ['',Validators.required], //Validators.required
|
||||
firstname: ['',Validators.required], //Validators.required
|
||||
lastname: ['',Validators.required], //Validators.required
|
||||
phone: [''], //Validators.required
|
||||
password: [''],
|
||||
active: [false], //Validators.required
|
||||
|
||||
roleType: [0,[Validators.required, Validators.min(1)]],
|
||||
|
||||
});
|
||||
constructor(
|
||||
private staffService: StaffService,
|
||||
private messageService: MessageService,
|
||||
private lookupService: LookupService,
|
||||
private router: Router, private route: ActivatedRoute,
|
||||
) {
|
||||
|
||||
this.Roles = [];
|
||||
let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'};
|
||||
this.Roles.push(item);
|
||||
item = {id:userRole.Accounting, name: 'Accounting', status:'Accounting'};
|
||||
this.Roles.push(item);
|
||||
item = {id:userRole.Normal, name: 'Normal', status:'Normal'};
|
||||
this.Roles.push(item);
|
||||
item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'};
|
||||
this.Roles.push(item);
|
||||
|
||||
|
||||
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
|
||||
//this.Roles.push(item);
|
||||
}
|
||||
getClassForRequire(prev:string,name:string){
|
||||
const notok = !this.adminuserForm.controls[name].valid &&
|
||||
this.adminuserForm.controls[name].touched;
|
||||
let str =prev;
|
||||
if (notok)
|
||||
str += " ng-invalid ng-dirty";
|
||||
return str;
|
||||
}
|
||||
ngOnInit():void {
|
||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||
const id = Number(this.route.snapshot.paramMap.get('id'));
|
||||
// now load thing up
|
||||
const user = Utils.getCurrentUser();
|
||||
// console.log(this.msg + "current login user ", user);
|
||||
if (user.username === '')
|
||||
alert("you are not login.");
|
||||
else
|
||||
this.loginUser = user.firstName;
|
||||
this._id = id;
|
||||
//console.log(this.msg + " " + id );
|
||||
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
|
||||
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
|
||||
if (id > 0)
|
||||
{
|
||||
this.isNew = false;
|
||||
this.subscription.add(
|
||||
this.staffService.loadStaffById(id).subscribe({
|
||||
next: x => this.assignValue(x.data),
|
||||
error: e => console.log(e)
|
||||
})
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
}
|
||||
getEditText(): string {
|
||||
if (this._id > 0)
|
||||
return "Edit Staff";
|
||||
else
|
||||
return "New Staff";
|
||||
}
|
||||
// this for disable submit button
|
||||
get isFieldsChange() {
|
||||
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
|
||||
//console.log(this.msg + 'is fields change', chan);
|
||||
return chan;
|
||||
}
|
||||
|
||||
assignValue(adminuser:Staff): void {
|
||||
this.adminuserForm.patchValue({
|
||||
id: adminuser.id,
|
||||
email: adminuser.email,
|
||||
firstname: adminuser.firstname,
|
||||
lastname: adminuser.lastname,
|
||||
active: adminuser.active,
|
||||
phone: adminuser.phone,
|
||||
roleType: adminuser.roleType,
|
||||
|
||||
});
|
||||
// disable use false//true for not disable.
|
||||
this.subChanged$.next(true);
|
||||
}
|
||||
|
||||
// convenience getter for easy access in form fields
|
||||
get f() { return this.adminuserForm.controls;}
|
||||
validate(adminuser:Staff): boolean {
|
||||
let result = true;
|
||||
if (adminuser.email.trim() == '')
|
||||
{
|
||||
this._error = 'email is blank or empty';
|
||||
result = false;
|
||||
}
|
||||
else if (adminuser.firstname.trim() == '')
|
||||
{
|
||||
this._error = 'Firstname is blank or empty';
|
||||
result = false;
|
||||
}
|
||||
else if (adminuser.lastname.trim() == '')
|
||||
{
|
||||
this._error = 'lastname is blank or empty';
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
onSubmit(): void {
|
||||
// if form valid then go save
|
||||
//make sure
|
||||
const okToSave = this.adminuserForm.valid;
|
||||
if (okToSave)
|
||||
{
|
||||
let adminuserValue = this.adminuserForm.value;
|
||||
|
||||
let adminuser:Staff = {
|
||||
id: adminuserValue.id,
|
||||
email: adminuserValue.email,
|
||||
firstname: adminuserValue.firstname,
|
||||
lastname: adminuserValue.lastname,
|
||||
active: adminuserValue.active,
|
||||
roleType: adminuserValue.roleType,
|
||||
phone: adminuserValue.phone,
|
||||
password: adminuserValue.password,
|
||||
|
||||
type: 0
|
||||
};
|
||||
this._error ='';
|
||||
const allOK = this.validate(adminuser);
|
||||
if (allOK)
|
||||
{
|
||||
this.subscription.add (
|
||||
this.staffService.saveStaff(adminuser).subscribe({
|
||||
next: x => {
|
||||
if (x.statusCode >= 1)
|
||||
{
|
||||
this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.firstname + " " + adminuser.lastname });
|
||||
this.router.navigate([this.returnUrl]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
|
||||
}
|
||||
},
|
||||
error: e => {
|
||||
const message = e || e.message;
|
||||
// this.toastr.error(message);
|
||||
console.log("error ", e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
else
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
|
||||
}
|
||||
}
|
||||
goBack(): void {
|
||||
this.router.navigate([this.returnUrl]);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="shadow-2xl rounded mt-2">
|
||||
<div class="mt-2 mb-2 ml-2">
|
||||
{{getEditText()}}
|
||||
</div>
|
||||
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" >
|
||||
<div class="ml-2 formgrid grid">
|
||||
|
||||
<div class="field col-12 md:col-3">
|
||||
<label class="w-full">Password<strong class="app-require">*</strong></label>
|
||||
<input id="password" pInputText formControlName="password" type="password" >
|
||||
</div>
|
||||
<div class="field col-12 md:col-3">
|
||||
<label class="w-full">Password Again<strong class="app-require">*</strong></label>
|
||||
<input id="passwordA" pInputText formControlName="passwordAgain" type="password">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex justify-content-end mr-2 mb-2">
|
||||
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" iconPos="left" label="Save" [disabled]="isFieldsChange"></button>
|
||||
<button pButton pRipple class="p-button-sm" type="button" icon="pi pi-times" iconPos="left" label="Cancel" (click)="goBack()"></button>
|
||||
</div>
|
||||
</form>
|
||||
<!--pre>{{ adminuserForm.value|json}}</pre-->
|
||||
</div>
|
||||
@@ -0,0 +1,191 @@
|
||||
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { Subject, Subscription} from 'rxjs';
|
||||
import { MessageService } from 'primeng/api';
|
||||
|
||||
import {Staff, Code, userRole, ResetPassword} from '../models';
|
||||
import { LookupService, Utils } from '../shares';
|
||||
import { StaffService } from './staff.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'staff.pass.component.html',
|
||||
selector: 'staff-pass',
|
||||
imports:[ReactiveFormsModule,CommonModule,ButtonModule],
|
||||
styleUrls: ['staff.pass.component.css']
|
||||
})
|
||||
export class StaffPassComponent implements OnInit, OnDestroy {
|
||||
returnUrl ='';
|
||||
loginUser ='';
|
||||
_error ='';
|
||||
_id= -1;
|
||||
|
||||
isNew = false;
|
||||
validationPoints?: Code[];
|
||||
msg="[adminUser Component] ";
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
//for focus input
|
||||
// @ViewChild('mystaffid') mystaffNo!: MatInput;
|
||||
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
|
||||
private subscription:Subscription = new Subscription();
|
||||
subChanged$ = new Subject<boolean>();
|
||||
adminuserForm = this.formBuilder.group({
|
||||
id: [0], //Validators.required
|
||||
|
||||
password: ['',Validators.required], //Validators.required
|
||||
passwordAgain: ['',Validators.required], //Validators.required
|
||||
|
||||
|
||||
});
|
||||
constructor(
|
||||
private staffService: StaffService,
|
||||
private messageService: MessageService,
|
||||
private lookupService: LookupService,
|
||||
private router: Router, private route: ActivatedRoute,
|
||||
) {
|
||||
|
||||
|
||||
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
|
||||
//this.Roles.push(item);
|
||||
}
|
||||
getClassForRequire(prev:string,name:string){
|
||||
const notok = !this.adminuserForm.controls[name].valid &&
|
||||
this.adminuserForm.controls[name].touched;
|
||||
let str =prev;
|
||||
if (notok)
|
||||
str += " ng-invalid ng-dirty";
|
||||
return str;
|
||||
}
|
||||
ngOnInit():void {
|
||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||
const id = Number(this.route.snapshot.paramMap.get('id'));
|
||||
// now load thing up
|
||||
const user = Utils.getCurrentUser();
|
||||
// console.log(this.msg + "current login user ", user);
|
||||
if (user.username === '')
|
||||
alert("you are not login.");
|
||||
else
|
||||
this.loginUser = user.firstName;
|
||||
this._id = id;
|
||||
//console.log(this.msg + " " + id );
|
||||
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
|
||||
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
|
||||
|
||||
|
||||
}
|
||||
getEditText(): string {
|
||||
|
||||
return "Reset Password";
|
||||
|
||||
}
|
||||
// this for disable submit button
|
||||
get isFieldsChange() {
|
||||
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
|
||||
//console.log(this.msg + 'is fields change', chan);
|
||||
return chan;
|
||||
}
|
||||
searchUser(): void {
|
||||
const staffid = this.adminuserForm.controls["stafflinkNo"].value;
|
||||
// console.log(this.msg + " the staffid is ", staffid);
|
||||
if (staffid != '') {
|
||||
//this.form.controls['your form control name'].value
|
||||
this.staffService.loadStaffById(staffid).subscribe({
|
||||
next: x => {
|
||||
//console.log(this.msg + " this is stafflink no ", x);
|
||||
// now get current user.
|
||||
|
||||
if (x.statusCode == 1) {
|
||||
//this.error = true;
|
||||
const fname = x.data.firstname + " " + x.data.lastname;
|
||||
this.adminuserForm.patchValue({name:fname, addedBy:this.loginUser});
|
||||
|
||||
}
|
||||
else {
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
|
||||
}
|
||||
},
|
||||
error: e => {
|
||||
const message = e || e.message;
|
||||
//this.toastr.error(message);
|
||||
console.log("error ", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
//this.toastr.error("please enter staff link no");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convenience getter for easy access in form fields
|
||||
get f() { return this.adminuserForm.controls;}
|
||||
validate(adminuser:any, passwordAgain:string): boolean {
|
||||
let result = true;
|
||||
const pass = adminuser.password.trim();
|
||||
if (pass == '')
|
||||
{
|
||||
this._error = 'password is blank or empty';
|
||||
result = false;
|
||||
}
|
||||
if (pass !== passwordAgain)
|
||||
{
|
||||
this._error = 'password is not match';
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
onSubmit(): void {
|
||||
// if form valid then go save
|
||||
//make sure
|
||||
const okToSave = this.adminuserForm.valid;
|
||||
|
||||
if (okToSave)
|
||||
{
|
||||
let adminuserValue = this.adminuserForm.value;
|
||||
|
||||
let adminuser:ResetPassword = {
|
||||
id: this._id,
|
||||
password: adminuserValue.password,
|
||||
|
||||
};
|
||||
this._error ='';
|
||||
const again = adminuserValue.passwordAgain;
|
||||
const allOK = this.validate(adminuserValue, again);
|
||||
if (allOK)
|
||||
{
|
||||
this.subscription.add (
|
||||
this.staffService.saveResetPassword(adminuser).subscribe({
|
||||
next: x => {
|
||||
if (x.statusCode >= 1)
|
||||
{
|
||||
this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.id.toString() });
|
||||
this.router.navigate([this.returnUrl]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
|
||||
}
|
||||
},
|
||||
error: e => {
|
||||
const message = e || e.message;
|
||||
// this.toastr.error(message);
|
||||
console.log("error ", e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
else
|
||||
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
|
||||
}
|
||||
}
|
||||
goBack(): void {
|
||||
this.router.navigate([this.returnUrl]);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword } from '../models';
|
||||
import { AppSettingService } from '../shares';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class StaffService {
|
||||
public searchCriteria: StaffSearch;
|
||||
constructor(private http: HttpClient,
|
||||
private appSetting :AppSettingService
|
||||
) {
|
||||
this.searchCriteria = {
|
||||
email:'',
|
||||
firstName: '',
|
||||
lastName:''
|
||||
};
|
||||
|
||||
}
|
||||
searchStaffs(criteria: StaffSearch): Observable<ResultModel<StaffView[]>> {
|
||||
let config = { headers : { 'Content-Type': 'application/json' } };
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl + "/SearchStaff";
|
||||
return this.http.post<ResultModel<StaffView[]>>(baseUrl, criteria, config);
|
||||
}
|
||||
|
||||
loadStaffById(id:number): Observable<ResultModel<Staff>> {
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl;
|
||||
/*
|
||||
const params = new HttpParams().set("id", ""+id);
|
||||
const headers = new HttpHeaders().set('Content-Type', 'application/json');
|
||||
const options = {
|
||||
headers: headers,
|
||||
params: params
|
||||
};
|
||||
*/
|
||||
//return this.http.get<AdminUser>(this.baseUrl + 'api/Adminuser/LoadAdminuserById',options);
|
||||
return this.http.get<ResultModel<Staff>>(baseUrl + "/" + id);
|
||||
}
|
||||
saveStaff(data:Staff): Observable<ResultModel<number>> { //insert Adminuser
|
||||
let config = { headers : { 'Content-Type': 'application/json' } };
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/SaveStaff";
|
||||
return this.http.post<ResultModel<number>>(baseUrl, data, config);
|
||||
}
|
||||
saveResetPassword(data:ResetPassword): Observable<ResultModel<number>> { //insert Adminuser
|
||||
let config = { headers : { 'Content-Type': 'application/json' } };
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/ResetPassStaff";
|
||||
return this.http.post<ResultModel<number>>(baseUrl, data, config);
|
||||
}
|
||||
deleteStaff(id:number): Observable<ResultModel<number>>{
|
||||
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl;
|
||||
|
||||
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
@if (currentUserS() && isAuth)
|
||||
{
|
||||
<div class="flex flex-column justify-between md:flex-row rounded" style="background-color:white">
|
||||
<div style="margin-top:5px;padding-right:35px; cursor: pointer;width: 400px;">
|
||||
<img (click)="onHome()" src="images/application_image.png" alt="Banner" style="height:60px;width:100%" />
|
||||
</div>
|
||||
<div class="flex justify-end items-end flex-row">
|
||||
<div class="flex-column">
|
||||
<label class=" flex justify-end items-end mr-1 mb-2 myname">{{loginUser}}</label>
|
||||
<div>
|
||||
<span class="md:hidden flex justify-end"> <!--hidden on md screen show on small-->
|
||||
<button type="button" pButton icon="pi pi-align-justify" (click)="onemenu.toggle($event);"></button>
|
||||
<p-menu #onemenu [popup]="true" [model]="oneMenu"></p-menu>
|
||||
</span>
|
||||
<span class="hidden md:inline-flex"> <!--hidden when small screen-->
|
||||
<p-buttonGroup >
|
||||
<button type="button" pButton icon="pi pi-slack" label="Home" (click)="onHome()"
|
||||
class="p-button-sm " [attr.disabled]="isHome?true:null"></button>
|
||||
<!--button *ngIf="hasRoleAdmin" type="button" pButton icon="pi pi-file" label="Report" (click)="onReport()"
|
||||
[attr.disabled]="isReport?true:null" class="p-button-sm "></button-->
|
||||
|
||||
<p-menu #reportmenu [popup]="true" [model]="reportMenu"></p-menu>
|
||||
<p-menu #sysmenu [popup]="true" [model]="systemMenu"></p-menu>
|
||||
|
||||
@if (hasRoleAdmin)
|
||||
{
|
||||
<button type="button" pButton icon="pi pi-chevron-down" iconPos="right" label="System"
|
||||
(click)="sysmenu.toggle($event);" class="p-button-sm ">
|
||||
<!--i class="pi pi-cog mr-2" style="font-size: 1rem"></i-->
|
||||
</button>
|
||||
}
|
||||
<button pButton type="button" icon="pi pi-power-off" iconPos="left" label="Logout"
|
||||
(click)="logout()" class="p-button-sm p"></button>
|
||||
</p-buttonGroup>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { Component, OnInit, OnDestroy, effect, computed, Signal, } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MenuItem } from 'primeng/api';
|
||||
import { MenuModule } from 'primeng/menu';
|
||||
import { ButtonModule} from 'primeng/button';
|
||||
import { ButtonGroupModule } from 'primeng/buttongroup';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { AuthenticationService } from '../user-services';
|
||||
import { Utils } from '../shares';
|
||||
import { User } from '../models';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-toolbar',
|
||||
imports: [CommonModule, MenuModule, ButtonModule, ButtonGroupModule],
|
||||
templateUrl: './toolbar.component.html',
|
||||
styleUrl: './toolbar.component.css'
|
||||
})
|
||||
export class ToolbarComponent implements OnInit, OnDestroy {
|
||||
title = 'Safe Assessment Unit UI';
|
||||
isAuth = false;
|
||||
hasRoleAdmin = false;
|
||||
hasRoleReport = false;
|
||||
homeUrl = "/person";
|
||||
oneMenu: MenuItem[] = [];
|
||||
reportMenu: MenuItem[] = [];
|
||||
systemMenu: MenuItem[] = [];
|
||||
currentUserName = "";
|
||||
currentUserS: Signal<User> = computed (() => {
|
||||
const currentUser = this.authenticationService.authChange();
|
||||
this.isAuth =false;
|
||||
if (currentUser.username) {
|
||||
this.isAuth =true;
|
||||
this.assignRole(currentUser);
|
||||
console.log(`${this.title} on auth currentUser signal now ${this.isAuth}`);
|
||||
// add this to the one menu
|
||||
if (this.hasRoleAdmin) {
|
||||
|
||||
let i = 0;
|
||||
for (i = 0; i < this.systemMenu.length; i++) {
|
||||
this.oneMenu.push(this.systemMenu[i]);
|
||||
}
|
||||
|
||||
}
|
||||
//add the logout here
|
||||
this.oneMenu.push(
|
||||
{
|
||||
label: 'Logout', icon: 'pi pi-power-off', command: () => {
|
||||
this.logout()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return currentUser;
|
||||
});
|
||||
|
||||
private subscription: Subscription = new Subscription();
|
||||
constructor(
|
||||
private router: Router,
|
||||
private authenticationService: AuthenticationService
|
||||
) {
|
||||
this.authenticationService.isReport = false;
|
||||
this.authenticationService.isHome = true;
|
||||
}
|
||||
onHome(): void {
|
||||
this.authenticationService.isHome = true;
|
||||
this.router.navigate([this.homeUrl]);
|
||||
}
|
||||
get isHome(): boolean {
|
||||
return this.authenticationService.isHome;
|
||||
}
|
||||
get loginUser(): string {
|
||||
let result = "";
|
||||
let greeting = "Good ";
|
||||
const d = new Date();
|
||||
const hour = d.getHours();
|
||||
if (hour >= 5 && hour < 12)
|
||||
greeting = "Good morning, ";
|
||||
else if (hour >= 12 && hour < 17)
|
||||
greeting = "Good afternoon, ";
|
||||
else if (hour >= 17 && hour < 21)
|
||||
greeting = "Good evening, ";
|
||||
else
|
||||
greeting = "Good night, ";
|
||||
if (this.isAuth) {
|
||||
const user = Utils.getCurrentUser();
|
||||
result = user.firstName;
|
||||
}
|
||||
return greeting + result;
|
||||
}
|
||||
get isReport(): boolean {
|
||||
|
||||
return this.authenticationService.isReport;
|
||||
}
|
||||
initOneMenu(): void {
|
||||
this.oneMenu = [
|
||||
{
|
||||
label: 'Home', icon: 'pi pi-slack', command: () => {
|
||||
this.router.navigate([this.homeUrl]);
|
||||
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
initSystemMenu(): void {
|
||||
this.systemMenu = [
|
||||
/*
|
||||
{
|
||||
label: 'Lookup', icon: 'pi pi-bars', command: () => {
|
||||
this.router.navigate(['/lookup']);
|
||||
this.authenticationService.isReport = false;
|
||||
this.authenticationService.isHome = false;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Waiting List', icon: 'pi pi-map-marker', command: () => {
|
||||
this.router.navigate(['/waitinglist']);
|
||||
this.authenticationService.isReport = false;
|
||||
this.authenticationService.isHome = false;
|
||||
}
|
||||
},
|
||||
*/
|
||||
{
|
||||
label: 'Person', icon: 'pi pi-users',
|
||||
command: () => {
|
||||
this.router.navigate(['/person']);
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Family tree', icon: 'pi pi-users',
|
||||
command: () => {
|
||||
this.router.navigate(['/familytree']);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Staff', icon: 'pi pi-users',
|
||||
command: () => {
|
||||
this.router.navigate(['/staff']);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.initOneMenu();
|
||||
this.authenticationService.logout();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
assignRole(user: User): void {
|
||||
const userRole = Utils.getUserRole(user);
|
||||
this.hasRoleReport = Utils.canRunReport(userRole);
|
||||
this.currentUserName = user.firstName;
|
||||
//this.hasRoleAdmin = true;
|
||||
this.hasRoleAdmin = userRole == Utils.getAdminRoleName();
|
||||
//console.log(this.msg + " hasRoleAdmin ", this.hasRoleAdmin);
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.initOneMenu();
|
||||
this.initSystemMenu();
|
||||
console.log("ngOnInit toolbar called");
|
||||
// this.initStart();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Injectable, Inject, Output, signal, computed, Signal } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { ConfigureUrl, User } from '../models';
|
||||
import { Utils, AppSettingService, LookupService } from '../shares';
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthenticationService {
|
||||
//private authChange = new BehaviorSubject<User>(new User());
|
||||
// public currentUser: Observable<User>;
|
||||
public authChange = signal(new User());
|
||||
|
||||
private _isHome = false;
|
||||
private _isReport = false;
|
||||
private _selectDate: Date | null = null;
|
||||
baseUrl = ''; // this.appSettingService.appSetting.baseUrl + "/"+ ConfigureUrl.loginApiUrl;
|
||||
constructor(private http: HttpClient,
|
||||
private appSetting: AppSettingService,
|
||||
private lookupService: LookupService,
|
||||
|
||||
) {
|
||||
// this default when load look for previous login information
|
||||
// if no need then comment out downthere
|
||||
// this.currentUser = this.authChange.asObservable();
|
||||
const fname = this.getCurrentUser();
|
||||
if (fname) {
|
||||
|
||||
this.authChange.set(fname);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* select date */
|
||||
public get SelectDate(): Date | null {
|
||||
return this._selectDate;
|
||||
}
|
||||
public set SelectDate(value: Date | null) {
|
||||
this._selectDate = value;
|
||||
}
|
||||
|
||||
public get isHome(): boolean {
|
||||
return this._isHome;
|
||||
}
|
||||
public set isHome(value: boolean) {
|
||||
if (value) {
|
||||
this._isReport = false;
|
||||
}
|
||||
this._isHome = value;
|
||||
}
|
||||
|
||||
public get isReport(): boolean {
|
||||
return this._isReport;
|
||||
}
|
||||
public set isReport(value: boolean) {
|
||||
if (value) {
|
||||
this._isHome = false;
|
||||
}
|
||||
this._isReport = value;
|
||||
}
|
||||
|
||||
login(username: string, password: string): Observable<any> {
|
||||
this.lookupService.setAllNull();
|
||||
|
||||
const url = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.loginApiUrl;
|
||||
const epass = btoa(password);
|
||||
return this.http.post<any>(url, { username, password: epass })
|
||||
.pipe(map(resultModel => {
|
||||
// login successful if there's a jwt token in the response
|
||||
if (resultModel.statusCode == 1) {
|
||||
const user = resultModel.data;
|
||||
|
||||
Utils.setLocalStore(user);
|
||||
// const fName = user.firstName + " " + user.lastName;
|
||||
this.authChange.set(user);
|
||||
console.log("login from API result ", this.authChange());
|
||||
|
||||
}
|
||||
return resultModel;
|
||||
}));
|
||||
}
|
||||
|
||||
getCurrentUser(): User {
|
||||
let userObj: User;
|
||||
userObj = new User();
|
||||
let user = sessionStorage.getItem('currentUser');
|
||||
if (user) {
|
||||
userObj = JSON.parse(user);
|
||||
//fname = userObj.firstName + " " + userObj.lastName;
|
||||
// logged in so return true
|
||||
}
|
||||
return userObj;
|
||||
}
|
||||
|
||||
isAuth(): boolean {
|
||||
let val: boolean;
|
||||
val = false;
|
||||
if (sessionStorage.getItem('currentUser')) {
|
||||
// logged in so return true
|
||||
val = true;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
logout(): void {
|
||||
this.lookupService.setAllNull();
|
||||
|
||||
const url = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.logoutUrl;
|
||||
this.http.post<any>(url, {}).subscribe({
|
||||
next: x => {
|
||||
// console.log("logout return", x);
|
||||
sessionStorage.removeItem('currentUser');
|
||||
const user = new User();
|
||||
this.authChange.set(user);
|
||||
},
|
||||
error: e => {
|
||||
sessionStorage.removeItem('currentUser');
|
||||
this.authChange.set(new User());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './authentication.service';
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>FamilyTree</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { App } from './app/app';
|
||||
|
||||
bootstrapApplication(App, appConfig)
|
||||
.catch((err) => console.error(err));
|
||||
@@ -0,0 +1,60 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import "tailwindcss";
|
||||
@plugin "tailwindcss-primeui";
|
||||
@import "primeicons/primeicons.css";
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
/*padding-top: 5px; */
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-bottom: 0.3rem;
|
||||
background-color: var(--surface-b);
|
||||
font-family: var(--font-family);
|
||||
font-weight: 400;
|
||||
color: var(--text-color);
|
||||
|
||||
}
|
||||
.false-icon {
|
||||
color: #c63737;
|
||||
}
|
||||
|
||||
.true-icon {
|
||||
color: #15961e;
|
||||
}
|
||||
|
||||
.app-require {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.validateStar {
|
||||
color: red;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.p-datepicker table td>span {
|
||||
width: 1rem !important;
|
||||
height: 1rem !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.p-datepicker .p-timepicker>div {
|
||||
height: 50px !important;
|
||||
}
|
||||
|
||||
.p-datepicker .p-timepicker span {
|
||||
font-size: 0.9rem !important;
|
||||
}
|
||||
|
||||
.p-datatable .p-datatable-tbody>tr>td {
|
||||
|
||||
border: 1px solid #dee2e6;
|
||||
border-width: 1px 0 0 0;
|
||||
padding: 5px 5px !important;
|
||||
}
|
||||
@@ -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": []
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* 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": {
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* 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/**/*.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user