in all in one

This commit is contained in:
Kham
2026-04-22 08:48:04 +10:00
parent a994d72640
commit 12504e61f4
215 changed files with 21683 additions and 12 deletions
+16
View File
@@ -0,0 +1,16 @@
# 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
[*.md]
max_line_length = off
trim_trailing_whitespace = false
+3
View File
@@ -0,0 +1,3 @@
{
"angular.enable-strict-mode-prompt": false
}
+27
View File
@@ -0,0 +1,27 @@
# CarParkValidation
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.2.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
+149
View File
@@ -0,0 +1,149 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"CarParkValidation": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [
"moment"
],
"outputPath": "dist/CarParkValidation",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.css",
"node_modules/material-design-icons/iconfont/material-icons.css",
"node_modules/primeflex/primeflex.css",
"node_modules/primeng/resources/primeng.min.css",
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "3mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"buildTarget": "CarParkValidation:build"
},
"configurations": {
"production": {
"buildTarget": "CarParkValidation:build:production"
},
"development": {
"buildTarget": "CarParkValidation:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "CarParkValidation:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.css",
"node_modules/material-design-icons/iconfont/material-icons.css",
"node_modules/primeflex/primeflex.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "CarParkValidation:serve"
},
"configurations": {
"production": {
"devServerTarget": "CarParkValidation:serve:production"
}
}
}
}
}
},
"cli": {
"analytics": false
}
}
+36
View File
@@ -0,0 +1,36 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};
+23
View File
@@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('CarParkValidation app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
+11
View File
@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}
+14
View File
@@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
+32
View File
@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/CarParkValidation'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
+15185
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
{
"name": "car-park-validation",
"version": "1.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.4",
"@angular/cdk": "^17.3.4",
"@angular/common": "^17.3.4",
"@angular/compiler": "^17.3.4",
"@angular/core": "^17.3.4",
"@angular/forms": "^17.3.4",
"@angular/material": "^17.3.4",
"@angular/material-moment-adapter": "^17.3.4",
"@angular/platform-browser": "^17.3.4",
"@angular/platform-browser-dynamic": "^17.3.4",
"@angular/router": "^17.3.4",
"material-design-icons": "^3.0.1",
"moment": "^2.29.1",
"primeflex": "^3.3.1",
"primeicons": "^7.0.0",
"primeng": "^17.13.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"web-animations-js": "^2.3.2",
"zone.js": "~0.14.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.4",
"@angular/cli": "^17.3.4",
"@angular/compiler-cli": "^17.3.4",
"@types/jasmine": "~3.8.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.4.1",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.0.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.6.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~5.4.5"
}
}
@@ -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,61 @@
<div class="edit-content">
<h3>Application Administrators</h3>
<div class="addlink">
<a [routerLink]='["/home/adminuser", 0]' [queryParams]="{returnUrl:'/home/adminuser'}">
<i class="material-icons">playlist_add</i>
Add User
</a>
</div>
<mat-form-field>
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Name all column" #input>
</mat-form-field>
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- Desciption Column -->
<ng-container matColumnDef="stafflinkNo">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Stafflink Id</th>
<td mat-cell *matCellDef="let element"> {{element.stafflinkNo}} </td>
</ng-container>
<!-- name Column -->
<ng-container matColumnDef="name">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let element">{{element.name}} </td>
</ng-container>
<!-- roleType Column -->
<ng-container matColumnDef="roleType">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Role</th>
<td mat-cell *matCellDef="let element"> {{element.roleType}}
</td>
</ng-container>
<!-- ValidationPoint Column -->
<ng-container matColumnDef="validationPoint">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Validation Point</th>
<td mat-cell *matCellDef="let element"> {{element.validationPoint}}
</td>
</ng-container>
<ng-container matColumnDef="edit" stickyEnd>
<th mat-mdc-header-cell *matHeaderCellDef>Edit</th>
<td mat-cell *matCellDef="let element">
<a [routerLink]='["/home/adminuser", element.id]' [queryParams]="{returnUrl:'/home/adminuser'}">
<mat-icon>edit</mat-icon>
</a>
</td>
</ng-container>
<!-- active Column -->
<ng-container matColumnDef="active">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Active</th>
<td mat-cell *matCellDef="let element">
<mat-checkbox [checked]="element.active" name="active" color="primary">
</mat-checkbox>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="selection-pointer tr-mouseover"
(click)="showEdit(row)"></tr>
</table>
<mat-paginator [pageSize]="10" [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator>
</div>
@@ -0,0 +1,91 @@
import { Component, Inject, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { AdminUserView } from '../models';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { AdminUserService } from './adminuser.service';
import { MessageService } from 'primeng/api';
@Component({
selector: 'adminuser-list',
templateUrl: './adminuser.component.html',
styleUrls: ['./adminuser.component.css']
})
export class AdminUserComponent implements OnInit, AfterViewInit {
dataSource: MatTableDataSource<AdminUserView>;
private subscription: Subscription = new Subscription();
displayedColumns: string[] = ['stafflinkNo', 'name', 'roleType', 'validationPoint', 'active', 'edit'];
msg = "[AdminUser component]";
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatPaginator) paginator!: MatPaginator;
/*
private store: Store<appValidationState>
*/
constructor(
private adminUserService: AdminUserService,
private router: Router,
private messageService: MessageService,
) {
this.dataSource = new MatTableDataSource<AdminUserView>();
}
//KeyboardEvent
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
ngOnInit(): void {
this.subscription.add(
this.adminUserService.loadAdminusers().subscribe(result => {
// console.log(this.msg + "onInit load Data", result);
this.dataSource.data = result.data;
}, error => {
console.log("error ", error);
})
);
/*
this.store.dispatch(validationActions.LoadValidationpoints());
this.store.pipe(select(validationSelector.getAllValidations)).subscribe(result => this.dataSource.data = result);
*/
}
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
showEdit(item: AdminUserView) {
console.log(this.msg + "on row click", item);
this.router.navigate(['/home/adminuser/' + item.id], { queryParams: { returnUrl: '/home/adminuser' } });
}
deleteItem(id: number): void {
this.adminUserService.deleteAdminuser(id)
.pipe(take(1))
.subscribe(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 => console.error(error));
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,56 @@
<div class="edit-content">
<div class="title">
{{getEditText()}}
</div>
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" class="flex flex-column gap-2">
<div class="flex flex-column">
<mat-card>
<div>
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Stafflink Number<strong class="app-require">*</strong></mat-label>
<input matInput formControlName="stafflinkNo" (blur)="searchUser()" maxlength="10" name="stafflinkno"
placeholder="Stafflink Number" required #mystaffid>
</mat-form-field>
</div>
<div>
<mat-form-field class="w-full" appearance="outline">
<mat-label>Name</mat-label>
<input matInput formControlName="name" maxlength="100" name="name" placeholder="Name" readonly>
</mat-form-field>
</div>
<div>
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Role<strong class="app-require">*</strong></mat-label>
<mat-select formControlName="roleType" required>
<mat-option>--</mat-option>
<mat-option *ngFor="let role of Roles" [value]="role.id">
{{role.description}}
</mat-option>
</mat-select>
<mat-error *ngIf="f['roleType'].hasError('required')">Please choose Role</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Validation Point<strong class="app-require">*</strong></mat-label>
<mat-select formControlName="validationPointId" required>
<mat-option>--</mat-option>
<mat-option *ngFor="let validationPoint of validationPoints" [value]="validationPoint.id">
{{validationPoint.description}}
</mat-option>
</mat-select>
<mat-error *ngIf="f['validationPointId'].hasError('required')">Please choose validation Point</mat-error>
</mat-form-field>
</div>
<div>
<mat-checkbox formControlName="active" color="primary">Active</mat-checkbox>
</div>
</mat-card>
</div>
<div class="flex justify-content-end mt-2">
<button type="submit" class="mr-2" mat-raised-button color="primary" [disabled]="isFieldsChange">Submit</button>
<button type="button" mat-raised-button color="primary" (click)="goBack()">Cancel</button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
</div>
@@ -0,0 +1,178 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { UntypedFormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subject, pipe, Subscription } from 'rxjs';
import { AdminUser, Lookup, userRole } from '../models';
import { LookupService, Utils } from '../shares';
import { AdminUserService } from './adminuser.service';
import { MessageService } from 'primeng/api';
@Component({
templateUrl: 'adminuser.edit.component.html',
selector: 'adminuser-edit',
styleUrls: ['adminuser.edit.component.css']
})
export class AdminUserEditComponent implements OnInit, OnDestroy {
returnUrl = '';
loginUser = '';
_id = -1;
Roles?: Lookup[];
validationPoints?: Lookup[];
msg = "[adminUser Component] ";
//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
stafflinkNo: [''], //Validators.required
name: [''], //Validators.required
addedBy: [''], //Validators.required
addedOn: [], //Validators.required
active: [false], //Validators.required
roleType: [0, Validators.required],
validationPointId: [0, Validators.required]
});
constructor(private formBuilder: UntypedFormBuilder,
private adminUserService: AdminUserService,
private messageService: MessageService,
private lookupService: LookupService,
private router: Router, private route: ActivatedRoute,
) {
this.Roles = [];
let item: Lookup = { id: userRole.Admin, categoryId: userRole.Admin, description: 'Admin' };
this.Roles.push(item);
item = { id: userRole.Normal, categoryId: userRole.Normal, description: 'Normal' };
this.Roles.push(item);
this.subscription.add(this.lookupService.loadValidationPoint().subscribe(x => this.validationPoints = x.data,
error => console.error(error)));
}
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.subscription.add(this.adminUserService.loadAdminuserById(id).subscribe(x => this.assignValue(x.data), error =>
console.error(error.message)));
}
}
getEditText(): string {
if (this._id > 0)
return "Edit Admin User";
else
return "New Admin User";
}
// 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.adminUserService.getStaffLinkId(staffid).subscribe(x => {
//console.log(this.msg + " this is stafflink no ", x);
// now get current user.
if (x.data) {
//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: "can not find stafflinkNo:" + staffid });
// this.adminuserForm.patchValue({name:x.data.firstName + " " + x.data.lastName
}
},
error => {
console.log("error ", error);
}
);
}
else {
this.messageService.add({ severity: 'error', summary: 'Error', detail: "please enter staff link no" });
}
}
assignValue(adminuser: AdminUser): void {
this.adminuserForm.patchValue({
id: adminuser.id,
stafflinkNo: adminuser.stafflinkNo,
name: adminuser.name,
addedBy: adminuser.addedBy,
addedOn: adminuser.addedOn,
active: adminuser.active,
roleType: adminuser.roleType,
validationPointId: adminuser.validationPointId
});
// 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; }
onSubmit(): void {
// if form valid then go save
//make sure
const okToSave = this.adminuserForm.valid;
if (okToSave) {
let adminuserValue = this.adminuserForm.value;
let adminuser: AdminUser = {
id: adminuserValue.id,
stafflinkNo: adminuserValue.stafflinkNo,
name: adminuserValue.name,
addedBy: adminuserValue.addedBy,
addedOn: new Date(),
active: adminuserValue.active,
roleType: adminuserValue.roleType,
validationPointId: adminuserValue.validationPointId,
};
this.subscription.add(
this.adminUserService.saveAdminuser(adminuser).subscribe(x => {
if (x.statusCode >= 1) {
this.messageService.add({ severity: 'success', summary: 'Success', detail: "Save is OK" });
this.router.navigate([this.returnUrl]);
}
else {
this.messageService.add({ severity: 'error', summary: 'Error', detail: "Fail to save" });
}
},
error => {
console.log("error ", error);
}
)
);
}
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+51
View File
@@ -0,0 +1,51 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AdminUser, AdminUserView, ResultModel,ConfigureUrl,User } from '../models';
import { AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class AdminUserService {
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {}
loadAdminusers(): Observable<ResultModel<AdminUserView[]>> {
let config = { headers : { 'Content-Type': 'application/json' } };
//const baseUrl = ConfigureUrl.baseUrl + "/"+ ConfigureUrl.adminUserUrl;
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.adminUserUrl;
return this.http.get<ResultModel<AdminUserView[]>>(baseUrl);
}
getStaffLinkId(staffid:string) :Observable<ResultModel<User>>{
const userUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.userByStaffIdUrl;
let params = new HttpParams().set("stafflinkNo", ""+staffid);
let headers = new HttpHeaders().set('Content-Type', 'application/json');
let options = {
headers: headers,
params: params
};
return this.http.get<ResultModel<User>>(userUrl, options);
}
loadAdminuserById(id:number): Observable<ResultModel<AdminUser>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.adminUserUrl;
let params = new HttpParams().set("id", ""+id);
let headers = new HttpHeaders().set('Content-Type', 'application/json');
let options = {
headers: headers,
params: params
};
//return this.http.get<AdminUser>(this.baseUrl + 'api/Adminuser/LoadAdminuserById',options);
return this.http.get<ResultModel<AdminUser>>(baseUrl + "/" + id);
}
saveAdminuser(jsData:AdminUser): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.adminUserUrl;
return this.http.post<ResultModel<number>>(baseUrl, jsData, config);
}
deleteAdminuser(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.adminUserUrl;
let config = { headers : { 'Content-Type': 'application/json' } };
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id);
}
}
+4
View File
@@ -0,0 +1,4 @@
export * from './adminuser.component';
export * from './adminuser.edit.component';
export * from './adminuser.service';
+37
View File
@@ -0,0 +1,37 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminUserComponent, AdminUserEditComponent } from './adminuser';
import { ConcessionTypeComponent, ConcessiontypeEditComponent} from './concessiontype';
import { ConcessionValidationComponent, ConcessionValidationEditComponent } from './concessionValidation';
import { HomeComponent } from './home';
import { LoginComponent } from './login';
import { ValidationPointComponent, ValidationpointEditComponent } from './validationPoint';
import { AuthGuard } from './route-guard';
import {ConcessionvalidationReportComponent} from './concessionvalidationreport';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'home', component: HomeComponent ,
children: [
{ path: 'adminuser', component: AdminUserComponent, canActivate: [AuthGuard], data: { roleAllowed: 'Admin' }} ,
{ path: 'adminuser/:id', component: AdminUserEditComponent, canActivate: [AuthGuard], data: { roleAllowed: 'Admin' }},
{ path: 'validationpoint', component: ValidationPointComponent, canActivate: [AuthGuard], data: { roleAllowed: 'Admin' } },
{ path: 'validationpoint/:id', component: ValidationpointEditComponent,canActivate: [AuthGuard], data: { roleAllowed: 'Admin' } },
{ path: 'concessiontype', component:ConcessionTypeComponent, canActivate: [AuthGuard], data: { roleAllowed: 'Admin' }},
{ path: 'concessiontype/:id', component: ConcessiontypeEditComponent, canActivate: [AuthGuard], data: { roleAllowed: 'Admin' }},
{ path: 'concessionvalidation', component: ConcessionValidationComponent, canActivate: [AuthGuard]},
{ path: 'concessionvalidation/:id', component: ConcessionValidationEditComponent, canActivate: [AuthGuard]},
{ path: 'app-concessionvalidation-report', component: ConcessionvalidationReportComponent, canActivate: [AuthGuard], data: { roleAllowed: 'Admin' }},
]
},
// otherwise redirect to home
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: '**', redirectTo: 'login' },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
+24
View File
@@ -0,0 +1,24 @@
.container {
display:flex;
flex-direction: column;
justify-content: space-between;
height:100%;
}
.mat-sidenav-container,.mat-sidenav-content {
height: 100%;
}
.mat-sidenav{
width:250px;
}
.main-content {
padding-left:25px;
padding-right:15px;
justify-content:space-around;
}
.mat-toolbar {
justify-content: start;
}
+3
View File
@@ -0,0 +1,3 @@
<router-outlet></router-outlet>
<p-confirmDialog></p-confirmDialog>
<p-toast></p-toast>
+10
View File
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'CarParkValidation';
}
+113
View File
@@ -0,0 +1,113 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, APP_INITIALIZER } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppMaterialModule } from './material/app.material.module';
import { MAT_DATE_LOCALE } from '@angular/material/core';
import { HomeComponent } from './home';
import { LoginComponent } from './login';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { NavToolarComponent } from './nav-toolbar/nav-toolbar.component';
import { AuthenticationService } from './user-services/authentication.service';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
//here the custom components
import { SetupModuleModule } from './setup-module/setup-module.module';
import { ConcessionvalidationReportComponent } from './concessionvalidationreport';
import { AppSettingService, ErrorInterceptor, JwtInterceptor } from './shares';
import { ConcessionValidationService, ConcessionValidationEditComponent, ConcessionValidationComponent } from './concessionValidation';
import { NotFoundComponent } from './not-found/not-found.component';
import { ServerErrorComponent } from './server-error/server-error.component';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { ToastModule } from 'primeng/toast';
import { MessageService, ConfirmationService } from 'primeng/api'
import { CalendarModule } from 'primeng/calendar';
//and in the module providers
// { provide: NGX_MAT_DATE_FORMATS, useValue: CUSTOM_MOMENT_FORMATS }
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
LoginComponent,
NavToolarComponent,
ConcessionValidationEditComponent,
ConcessionValidationComponent,
ConcessionvalidationReportComponent,
NotFoundComponent,
ServerErrorComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule,
AppMaterialModule,
SetupModuleModule,
AppRoutingModule,
BrowserAnimationsModule,
ConfirmDialogModule,
ToastModule,
CalendarModule,
],
providers: [
MessageService,
ConfirmationService,
{
provide: APP_INITIALIZER,
multi: true,
deps: [AppSettingService],
useFactory: (appConfigService: AppSettingService) => () => appConfigService.loadAppSetting()
},
{ provide: MAT_DATE_LOCALE, useValue: 'en-GB', multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
],
bootstrap: [AppComponent]
})
export class AppModule { }
/*
make star * small
check start expiry make the
the banner is red.
#e02c2c
*/
/*
ng build --base-href "/CarParkValidation/"
ng build --base-href "/CarParkValidation/" -c production
*/
/*
downgrade angular
ng --version
npm uninstall -g @angular/cli
npm cache clean --force
npm install -g @angular/cli@10 or 11
ng --version
ng add @angular/material@10
npm install ngx-toastr
npm install ngx-ui-loader
npm install moment
npm i @angular/material-moment-adapter@10
npm i @angular/flex-layout@10.0.0-beta.32
npm install --save classlist.js
npm install --save web-animations-js
in the tsconfig.json
change "target": "es2015", to target: es5 for IE
*/
@@ -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,98 @@
<div class="edit-content">
<h3>Concession Validation</h3>
<div *ngIf="isAdmin">
<div class="flex flex-row gap-2 w-full">
<mat-form-field>
<mat-label>Choose a from date</mat-label>
<input matInput [(ngModel)]="fromDate" (focus)="pickerfrom.open()" [matDatepicker]="pickerfrom">
<mat-datepicker-toggle matSuffix [for]="pickerfrom"></mat-datepicker-toggle>
<mat-datepicker #pickerfrom></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>Choose a to date</mat-label>
<input matInput [(ngModel)]="toDate" (focus)="pickerto.open()" [matDatepicker]="pickerto">
<mat-datepicker-toggle matSuffix [for]="pickerto"></mat-datepicker-toggle>
<mat-datepicker #pickerto></mat-datepicker>
</mat-form-field>
<button mat-mini-fab color="primary" aria-label="loading record" (click)="onLoad()" matTooltip="Loading">
<span class="material-icons">
flash_on
</span>
</button>
</div>
</div>
<div class="addlink">
<a [routerLink]='["/home/concessionvalidation", 0]' [queryParams]="{returnUrl:'/home/concessionvalidation'}"
(click)="setDate()">
<span class="inline-icon material-icons">add_task</span>
Add Concession Validation
</a>
</div>
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- validateDate Column -->
<ng-container matColumnDef="validateDate">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Date </th>
<td mat-cell *matCellDef="let element"> {{element.validateDate | date: 'dd/MM/yyyy HH:mm'}} </td>
</ng-container>
<!-- concessionType Column -->
<ng-container matColumnDef="concessionType">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Concession Type </th>
<td mat-cell *matCellDef="let element"> {{element.concessionType}} </td>
</ng-container>
<!-- concessionCard Column -->
<ng-container matColumnDef="concessionCard">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Concession Card Number</th>
<td mat-cell *matCellDef="let element"> {{element.concessionCard}} </td>
</ng-container>
<!-- concessionHolder Column -->
<ng-container matColumnDef="concessionHolder">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Concession Holder Name</th>
<td mat-cell *matCellDef="let element"> {{element.concessionHolder}} </td>
</ng-container>
<!-- validationPoint Column -->
<ng-container matColumnDef="validationPoint">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Validation Point </th>
<td mat-cell *matCellDef="let element"> {{element.validationPoint}} </td>
</ng-container>
<!-- concessionExpiry Column -->
<ng-container matColumnDef="concessionExpiry">
<th mat-mdc-header-cell *matHeaderCellDef>Expiry date checked</th>
<td mat-cell *matCellDef="let element">
<mat-checkbox [checked]="element.concessionExpiry" name="concessionExpiry" color="primary">
</mat-checkbox>
</td>
</ng-container>
<div *ngIf="isAdmin">
<!-- staffFirstName Column -->
<ng-container matColumnDef="staffFirstName">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Staff Name</th>
<td mat-cell *matCellDef="let element">{{element.staffFirstName}} {{element.staffLastName}} </td>
</ng-container>
<ng-container matColumnDef="edit" stickyEnd>
<div *ngIf="isAdmin">
<th mat-mdc-header-cell *matHeaderCellDef>Edit</th>
<td mat-cell *matCellDef="let element">
<!--a [routerLink]='["/home/concessionvalidation", element.id]' [queryParams]="{returnUrl:'/home/concessionvalidation'}">
<mat-icon>edit</mat-icon>
</a-->
<button mat-icon-button (click)="showEdit(element)" matTooltip="Edit"><mat-icon>edit</mat-icon></button>
</td>
</div>
</ng-container>
<ng-container matColumnDef="delete" stickyEnd>
<th mat-mdc-header-cell *matHeaderCellDef>Delete</th>
<td mat-cell *matCellDef="let element">
<a (click)='deleteItem(element.id)'>
<mat-icon matTooltip="Delete Record">delete</mat-icon>
</a>
</td>
</ng-container>
</div>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="tr-mouseover"></tr>
</table>
<mat-paginator [pageSize]="10" [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator>
</div>
@@ -0,0 +1,166 @@
import { Component, OnInit, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { ConcessionValidation } from '../models';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Utils } from '../shares';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { ConcessionValidationService } from './concessionvalidation.service';
import { MatDialog } from '@angular/material/dialog';
import { ConfirmationService, MessageService } from 'primeng/api';
@Component({
selector: 'concessionvalidation-list',
templateUrl: './concessionvalidation.component.html',
styleUrls: ['./concessionvalidation.component.css']
})
export class ConcessionValidationComponent implements OnInit, AfterViewInit, OnDestroy {
dataSource: MatTableDataSource<ConcessionValidation>;
isAdmin = false;
isAllowFilter = true;
fromDate = new Date();
toDate = new Date();
username = '';
private subscription: Subscription = new Subscription();
displayedColumns: string[] =
[
'validateDate',
'concessionType',
'concessionCard',
'concessionHolder',
'concessionExpiry',
'validationPoint',
];
msg = "[ConcessionValidation component]";
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatPaginator) paginator!: MatPaginator;
/*
private store: Store<appValidationState>
*/
constructor(
private concessionValidationService: ConcessionValidationService,
private router: Router,
private messageService: MessageService,
private confirmationService: ConfirmationService,
public dialog: MatDialog,
) {
this.dataSource = new MatTableDataSource<ConcessionValidation>();
this.isAdmin = Utils.getIsAdmin();
if (this.isAdmin) {
this.displayedColumns =
[
'validateDate',
'concessionType',
'concessionCard',
'concessionHolder',
'concessionExpiry',
'validationPoint',
'staffFirstName',
'edit',
'delete'
];
}
}
//KeyboardEvent
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
ngOnInit(): void {
const user = Utils.getCurrentUser();
// console.log(this.msg + "current login user ", user);
if (user.username === "") {
user.username = "60249360";
this.messageService.add({ severity: 'error', summary: 'Error', detail: "you are not login! using my number " + user.username });
}
this.username = user.username;
if (this.isAdmin)
this.username = '';
const dateRange = this.concessionValidationService.getDate();
// console.log("the date range for now ", dateRange);
if (dateRange.fromDate != '')
this.fromDate = dateRange.fromDate;
if (dateRange.toDate != '')
this.toDate = dateRange.toDate;
this.onLoad();
/*
this.store.dispatch(validationActions.LoadValidationpoints());
this.store.pipe(select(validationSelector.getAllValidations)).subscribe(result => this.dataSource.data = result);
*/
}
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
onLoad(): void {
const cDate = this.fromDate;
const tDate = this.toDate;
this.subscription.add(
this.concessionValidationService.loadConcessionvalidationByStaffId(this.username, cDate, tDate).subscribe(result => {
//console.log(this.msg + "onInit load Data by staffId "+ this.username, result);
this.dataSource.data = result.data;
}, error => console.error(error.message))
);
//this.dataList$ = this.concessionValidationService.loadConcessionvalidationByStaffId(this.username, cDate, tDate)
// .pipe(catchError(err => {this.toastr.error(err.message); return EMPTY}));
}
setDate(): void {
this.concessionValidationService.setDateRange(this.fromDate, this.toDate);
}
showEdit(item: ConcessionValidation) {
// console.log(this.msg + "on row click", item);
this.setDate();
this.router.navigate(['/home/concessionvalidation/' + item.id], { queryParams: { returnUrl: '/home/concessionvalidation' } });
}
deleteItem(id: number): void {
const message = "Are you sure you want to delete this?";
const title = " Confirmation";
this.confirmationService.confirm({
message: message,
header: title,
icon: 'pi pi-exclamation-triangle',
accept: () => {
this.concessionValidationService.deleteConcessionvalidation(id)
.subscribe(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 => {
console.log("error ", error);
}
);
},
reject: () => {
console.log(this.msg + "click on No 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,69 @@
<div class="edit-content">
<div class="title">
{{getEditText()}}
</div>
<form [formGroup]="concessionvalidationForm" (ngSubmit)="onSubmit()">
<div class="flex flex-column">
<mat-card>
<div class="m-2">
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Concession Card Number<strong class="app-require">*</strong></mat-label>
<input matInput formControlName="concessionCard" maxlength="60" name="concessionCard" (blur)="searchCard()"
placeholder="Concession Card Number" required>
</mat-form-field>
</div>
<div *ngIf="isAdmin" class="m-2">
<label for="time" class="mr-2">Current Date</label>
<p-calendar formControlName="validateDate" [showTime]="true" inputId="time"
dateFormat="dd/mm/yy"></p-calendar>
</div>
<!--end-->
<div class="m-2">
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Concession Type<strong class="app-require">*</strong></mat-label>
<mat-select formControlName="concessionTypeId" required>
<mat-option>--</mat-option>
<mat-option *ngFor="let concessionType of concessionTypes" [value]="concessionType.id">
{{concessionType.description}}
</mat-option>
</mat-select>
<mat-error *ngIf="f['concessionTypeId'].hasError('required')">Please choose concession Type</mat-error>
</mat-form-field>
</div>
<div class="m-2">
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Concession Holder Name<strong class="app-require">*</strong></mat-label>
<input matInput formControlName="concessionHolder" maxlength="100" name=""
placeholder="Concession Holder Name" required>
</mat-form-field>
</div>
<div class="m-2">
<mat-checkbox formControlName="concessionExpiry" color="primary" required>Expiry date checked <strong
class="app-require">*</strong></mat-checkbox>
<div *ngIf="isAdmin">
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Validation Point<strong class="app-require">*</strong></mat-label>
<mat-select formControlName="validationPointId" required (selectionChange)="onSelectionChange($event)">
<mat-option>--</mat-option>
<mat-option *ngFor="let validationPoint of validationPoints" [value]="validationPoint.id">
{{validationPoint.description}}
</mat-option>
</mat-select>
<mat-error *ngIf="f['validationPointId'].hasError('required')">Please choose validation Point</mat-error>
</mat-form-field>
</div>
</div>
</mat-card>
</div>
<div class="flex justify-content-end mt-2">
<button class="mr-2" type="submit" mat-raised-button color="primary" [disabled]="isFieldsChange">Submit</button>
<button type="button" mat-raised-button color="primary" (click)="goBack()">Cancel</button>
</div>
</form>
<!--pre>{{ concessionvalidationForm.value|json}}</pre-->
</div>
@@ -0,0 +1,277 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { UntypedFormBuilder, FormGroup, Validators } from '@angular/forms';
import { ConcessionValidation, Lookup, ResultModel } from '../models';
import { Subject, Subscription } from 'rxjs';
import { LookupService } from '../shares';
import { ConcessionValidationService } from './concessionvalidation.service';
import { Utils } from '../shares';
import { MatSelectChange } from '@angular/material/select';
import * as moment from 'moment';
import { MatDialog } from '@angular/material/dialog';
import { ThemePalette } from '@angular/material/core';
import { ConfirmationService, MessageService } from 'primeng/api';
@Component({
templateUrl: 'concessionvalidation.edit.component.html',
selector: 'concessionvalidation-edit',
styleUrls: ['concessionvalidation.edit.component.css']
})
export class ConcessionValidationEditComponent implements OnInit, OnDestroy {
returnUrl = "";
msg = "[concessionvalidation edit Component] "
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
subChanged$ = new Subject<boolean>();
_id = -1;
isAdmin = false;
concessionTypes?: Lookup[];
validationPoints?: Lookup[];
public color: ThemePalette = 'primary';
public minDate: moment.Moment;
public maxDate: moment.Moment;
private subscription: Subscription = new Subscription();
concessionvalidationForm = this.formBuilder.group({
id: [0], //Validators.required
validateDate: [], //Validators.required
concessionCard: ['', Validators.required],
concessionHolder: ['', Validators.required],
concessionExpiry: [false, Validators.required], //Validators.required
concessionTypeId: [-1, Validators.min(1)],
stafflinkNo: [''], //Validators.required
validationPointId: [-1, Validators.min(1)],
validationPoint: [''], //Validators.required
staffFirstName: [''], //Validators.required
staffLastName: [''], //Validators.required
staffEmail: [''], //Validators.required
active: [false], //Validators.required
});
constructor(private formBuilder: UntypedFormBuilder,
private lookupService: LookupService,
private concessionValidationService: ConcessionValidationService,
private router: Router,
public dialog: MatDialog,
private confirmationService: ConfirmationService,
private messageService: MessageService,
private route: ActivatedRoute) {
/*
this.concessionTypes = [];
let item:Lookup = {id:1,categoryId:0, description:'Health Care Card'};
this.concessionTypes.push(item);
item = {id:3,categoryId:1, description:'Gold Veterans Affairs Card'};
this.concessionTypes.push(item);
item = {id:3,categoryId:1, description:'Pensioner Concession Card'};
this.concessionTypes.push(item);
*/
}
searchCard(): void {
const cardNo = this.concessionvalidationForm.controls["concessionCard"].value;
if (cardNo != "") {
const card$ = this.concessionValidationService.loadConcessionvalidationByCard(cardNo);
this.subscription.add(card$.subscribe({
next: x => {
if (x.statusCode == 1) {
const data = x.data;
this.concessionvalidationForm.patchValue({
concessionHolder: data.concessionHolder,
concessionTypeId: data.concessionTypeId,
});
}
}
})
);
}
}
ngOnInit(): void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
const id = Number(this.route.snapshot.paramMap.get('id'));
// now load thing up
this._id = id;
this.isAdmin = Utils.getIsAdmin();
this.subscription.add(this.lookupService.loadConcessionType().subscribe(x => this.concessionTypes = x.data,
error => console.error(error)));
this.subscription.add(this.lookupService.loadValidationPoint().subscribe(x => this.validationPoints = x.data,
error => console.error(error)));
this.subscription.add(this.concessionvalidationForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
const user = Utils.getCurrentUser();
console.log(this.msg + "current login user ", user);
if (user.username === "") {
user.username = "60249360";
this.messageService.add({ severity: 'error', summary: 'Error', detail: "you are not login! using my number " + user.username });
}
if (this._id < 1) {
this.concessionvalidationForm.patchValue({
validateDate: new Date(),
stafflinkNo: user.username,
staffFirstName: user.firstName,
staffLastName: user.lastName,
staffEmail: user.email,
validationPointId: user.validationPointId,
active: true
});
}
else {
this.subscription.add(
this.concessionValidationService.loadConcessionvalidationById(id)
.subscribe(x => this.assignValue(x.data), error => console.log(error.message)));
// console.log(this.msg + " load from db"+id);
}
}
get isFieldsChange() {
const chan = this.isChange || !this.concessionvalidationForm.valid; // this disable so need true valid = true not
const fvalid = this.concessionvalidationForm.valid;
// console.log(this.msg + "is fields change is valid: " + fvalid , chan,);
return chan;
}
getEditText(): string {
if (this._id > 0)
return "Edit Concession Validation";
else
return "New Concession Validation";
}
onChangeValidation($event: Event) {
//$event.target.value
//this.selectedCar = (event.target as HTMLSelectElement).value;
var val = ($event.target as HTMLSelectElement).value;
// console.log("the log for val " , val);
}
onSelectionChange($event: MatSelectChange) {
const text = $event.source.triggerValue;
// console.log("this is selectionchange "+ "'" + text + "'");
this.concessionvalidationForm.patchValue({ validationPoint: text });
}
assignValue(concessionvalidation: ConcessionValidation): void {
this.concessionvalidationForm.patchValue({
id: concessionvalidation.id,
validateDate: moment(concessionvalidation.validateDate).toDate(),
concessionCard: concessionvalidation.concessionCard,
concessionHolder: concessionvalidation.concessionHolder,
concessionExpiry: concessionvalidation.concessionExpiry,
concessionTypeId: concessionvalidation.concessionTypeId,
stafflinkNo: concessionvalidation.stafflinkNo,
staffFirstName: concessionvalidation.staffFirstName,
staffLastName: concessionvalidation.staffLastName,
staffEmail: concessionvalidation.staffEmail,
validationPointId: concessionvalidation.validationPointId,
active: concessionvalidation.active,
});
// disable use false//true for not disable.
this.subChanged$.next(true);
}
// convenience getter for easy access in form fields
get f() { return this.concessionvalidationForm.controls; }
onSubmit(): void {
const OkToSave = this.concessionvalidationForm.valid;
if (OkToSave) {
const concessionvalidationValue = this.concessionvalidationForm.value;
const concessionvalidation: ConcessionValidation = {
id: concessionvalidationValue.id,
validateDate: moment(concessionvalidationValue.validateDate).toDate(),
concessionCard: concessionvalidationValue.concessionCard,
concessionHolder: concessionvalidationValue.concessionHolder,
concessionExpiry: concessionvalidationValue.concessionExpiry,
concessionTypeId: concessionvalidationValue.concessionTypeId,
stafflinkNo: concessionvalidationValue.stafflinkNo,
staffFirstName: concessionvalidationValue.staffFirstName,
staffLastName: concessionvalidationValue.staffLastName,
staffEmail: concessionvalidationValue.staffEmail,
validationPointId: concessionvalidationValue.validationPointId,
active: concessionvalidationValue.active,
};
// console.log(this.msg + "this is OKTOsave", concessionvalidation);
//if edit mode just save it don't check
if (concessionvalidation.id > 0) {
this.saveConcession(concessionvalidation);
}
else {
//check first only if new
this.checkAndSave(concessionvalidation);
}
}
else {
// console.log(this.msg + "this is NOT OKTOsave");
this.messageService.add({ severity: 'error', summary: 'Error', detail: "please fill in the required fields" });
}
}
checkAndSave(concessionvalidation: ConcessionValidation): void {
const currentDate = moment(concessionvalidation.validateDate).format("YYYY-MM-DD");
const id = concessionvalidation.id;
this.subscription.add(
this.concessionValidationService.checkConcessionValidationByStaff(concessionvalidation.stafflinkNo,
concessionvalidation.concessionCard, currentDate, id)
.subscribe(x => {
let okTo = true;
if (x.data < 0) {
const message = "This card was used earlier today. Is it Ok to continue?";
const title = "Alert";
this.confirmationService.confirm({
message: message,
header: 'Confirmation',
icon: 'pi pi-exclamation-triangle',
accept: () => {
this.saveConcession(concessionvalidation);
},
reject: () => {
this.router.navigate([this.returnUrl]);
}
});
}
else {
this.saveConcession(concessionvalidation);
}
})
);
}
saveConcession(concessionvalidation: ConcessionValidation): void {
//console.log(this.msg + " Save Concession validation to server");
this.subscription.add(
this.concessionValidationService.saveConcessionvalidation(concessionvalidation)
.subscribe(x => {
if (x.statusCode >= 1) {
this.messageService.add({ severity: 'success', summary: 'Success', detail: "Save is OK" });
this.router.navigate([this.returnUrl]);
}
else
this.messageService.add({ severity: 'error', summary: 'Fail to Save', detail: x.message });
}, error => {
const message = error || error.message;
this.messageService.add({ severity: 'error', summary: 'Fail to Save', detail: message });
console.log("error ", error);
}
));
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
@@ -0,0 +1,102 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ConcessionValidation, ResultModel, ConfigureUrl } from '../models';
import * as moment from 'moment';
import { AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class ConcessionValidationService {
private dateRange: any;
constructor(private http: HttpClient,
private appSetting: AppSettingService
) {
this.dateRange = {};
this.dateRange.fromDate = '';
this.dateRange.toDate = '';
console.log("constructor concessionValidationService daterange", this.dateRange);
}
public setDateRange(fromDate: any, toDate: any) {
this.dateRange.fromDate = moment(fromDate).toDate();
this.dateRange.toDate = moment(toDate).toDate();
// console.log("concessionValidationService setdaterange", this.dateRange);
}
public getDate(): any {
// console.log("concessionValidationService getdaterange", this.dateRange);
return this.dateRange;
}
loadConcessionvalidations(): Observable<ResultModel<ConcessionValidation[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.concessionValidationUrl;
let config = { headers: { 'Content-Type': 'application/json' } };
// console.log("the concession", baseUrl);
return this.http.get<ResultModel<ConcessionValidation[]>>(baseUrl);
}
loadConcessionvalidationByStaffId(staffLinkId: string, fromDate: Date, toDate: Date): Observable<ResultModel<ConcessionValidation[]>> {
this.setDateRange(fromDate, toDate);
const strDate = moment(fromDate).format("YYYY-MM-DD");
const strtoDate = moment(toDate).format("YYYY-MM-DD");
const url = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.concessionValidationByIdUrl;
const params = new HttpParams().set("staffLinkNo", staffLinkId)
.set("fromDate", strDate)
.set("toDate", strtoDate);
const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = {
headers: headers,
params: params
};
//console.log("loadConcessionvalidationByStaffId url", url);
return this.http.get<ResultModel<ConcessionValidation[]>>(url, options);
}
loadConcessionvalidationById(id: number): Observable<ResultModel<ConcessionValidation>> {
let params = new HttpParams().set("id", "" + id);
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.concessionValidationUrl;
let headers = new HttpHeaders().set('Content-Type', 'application/json');
let options = {
headers: headers,
params: params
};
//return this.http.get<Concessionvalidation>(this.baseUrl + 'api/Concessionvalidation/LoadConcessionvalidationById',options);
return this.http.get<ResultModel<ConcessionValidation>>(baseUrl + "/" + id);
}
loadConcessionvalidationByCard(cardNo: string): Observable<ResultModel<ConcessionValidation>> {
const data = { cardNo };
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.concessionValidationUrl + "/GetConcessionCard";
/*
let params = new HttpParams().set("id", "" + id);
let headers = new HttpHeaders().set('Content-Type', 'application/json');
let options = {
headers: headers,
params: params
};
*/
//return this.http.get<Concessionvalidation>(this.baseUrl + 'api/Concessionvalidation/LoadConcessionvalidationById',options);
return this.http.post<ResultModel<ConcessionValidation>>(baseUrl, data);
}
checkConcessionValidationByStaff(staffLinkNo: string, concessionCard: string, currentDate: string, id: number): Observable<ResultModel<number>> {
let params = new HttpParams().set("staffLinkNo", staffLinkNo)
.set("concessionCard", concessionCard)
.set("currentDate", currentDate)
.set("id", "" + id);
let headers = new HttpHeaders().set('Content-Type', 'application/json');
let options = {
headers: headers,
params: params
};
const baseUrl = this.appSetting.appSetting.baseUrl;
const url = baseUrl + "/" + ConfigureUrl.CheckConcessionValidationByStaffUrl;
//return this.http.get<Concessionvalidation>(this.baseUrl + 'api/Concessionvalidation/LoadConcessionvalidationById',options);
return this.http.get<ResultModel<number>>(url, options);
}
saveConcessionvalidation(item: ConcessionValidation): Observable<ResultModel<number>> { //insert Concessionvalidation
let config = { headers: { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.concessionValidationUrl;
return this.http.post<ResultModel<number>>(baseUrl, item, config);
}
deleteConcessionvalidation(Id: number): Observable<ResultModel<number>> {
let config = { headers: { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.concessionValidationUrl;
return this.http.delete<ResultModel<number>>(baseUrl + "/" + Id);
}
}
+4
View File
@@ -0,0 +1,4 @@
export * from './concessionvalidation.component';
export * from './concessionvalidation.edit.component';
export * from './concessionvalidation.service';
@@ -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,41 @@
<div class="edit-content">
<h3>Concession Type</h3>
<div class="addlink">
<a [routerLink]='["/home/concessiontype", 0]' [queryParams]="{returnUrl:'/home/concessiontype'}">
<i class="material-icons">playlist_add</i>
Add Concession Type
</a>
</div>
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- Desciption Column -->
<ng-container matColumnDef="description">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Concession Type</th>
<td mat-cell *matCellDef="let element"> {{element.description}} </td>
</ng-container>
<!-- active Column -->
<ng-container matColumnDef="active">
<th mat-mdc-header-cell *matHeaderCellDef mat-sort-header>Active</th>
<td mat-cell *matCellDef="let element">
<mat-checkbox [checked]="element.active" name="active" color="primary">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="edit" stickyEnd>
<th mat-mdc-header-cell *matHeaderCellDef>Edit</th>
<td mat-cell *matCellDef="let element">
<a [routerLink]='["/home/concessiontype", element.id]' [queryParams]="{returnUrl:'/home/concessiontype'}">
<mat-icon>edit</mat-icon>
</a>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="selection-pointer tr-mouseover"
(click)="showEdit(row)"></tr>
</table>
<mat-paginator [pageSize]="10" [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons></mat-paginator>
</div>
@@ -0,0 +1,102 @@
import { Component, Inject, OnInit, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { ConcessionTypeService } from './concessiontype.service';
import { ConcessionType } from '../models';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
/* 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 { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { MessageService } from 'primeng/api';
@Component({
selector: 'concessiontype-list',
templateUrl: './concessiontype.component.html',
styleUrls: ['./concessiontype.component.css']
})
export class ConcessionTypeComponent implements OnInit, AfterViewInit, OnDestroy {
dataSource: MatTableDataSource<ConcessionType>;
private subscription: Subscription = new Subscription();
displayedColumns: string[] = ['description', 'active', 'edit'];
msg = "[concessionType.com]";
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatPaginator) paginator!: MatPaginator;
/*
private store: Store<appValidationState>
*/
constructor(
private concessionTypeService: ConcessionTypeService,
private messageService: MessageService,
private router: Router
) {
this.dataSource = new MatTableDataSource<ConcessionType>();
}
/*
applyFilter($event:any) {
const filterValue = $event.target.value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
*/
ngOnInit(): void {
this.subscription.add(
this.concessionTypeService.loadConcessiontypes().subscribe(result => {
// console.log(this.msg + "onInit", result);
this.dataSource.data = result.data;
}, error => {
console.log("error ", error);
}
)
);
/*
this.store.dispatch(validationActions.LoadValidationpoints());
this.store.pipe(select(validationSelector.getAllValidations)).subscribe(result => this.dataSource.data = result);
*/
}
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
showEdit(item: ConcessionType) {
console.log(this.msg + "on row click", item);
this.router.navigate(['/home/concessiontype/' + item.id], { queryParams: { returnUrl: '/home/concessiontype' } });
}
deleteItem(id: number): void {
this.concessionTypeService.deleteConcessiontype(id)
.pipe(take(1))
.subscribe(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 => {
console.log("error ", error);
}
);
}
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,26 @@
<div class="edit-content">
<div class="title"> {{getEditText()}} </div>
<form [formGroup]="concessiontypeForm" (ngSubmit)="onSubmit()">
<div class="flex flex-column">
<mat-card>
<input type="hidden" formControlName="id">
<div>
<mat-form-field class="w-full" appearance="outline" [hideRequiredMarker]="true">
<mat-label>Concession Type<strong class="app-require">*</strong></mat-label>
<input matInput formControlName="description" maxlength="80" name="Concession Type"
placeholder="description" required>
</mat-form-field>
</div>
<div>
<mat-checkbox formControlName="active" color="primary">Active</mat-checkbox>
</div>
</mat-card>
</div>
<div class="flex justify-content-end mt-2">
<button class="mr-2" type="submit" mat-raised-button color="primary"
[disabled]="isFieldsChange">Submit</button>
<button type="button" mat-raised-button color="primary" (click)="goBack()">Cancel</button>
</div>
</form>
<!--pre>{{ concessiontypeForm.value|json}}</pre-->
</div>
@@ -0,0 +1,114 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { UntypedFormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { ConcessionType } from '../models';
import { Subject, pipe, Subscription } from 'rxjs';
import { ConcessionTypeService } from './concessiontype.service';
import { MessageService } from 'primeng/api';
@Component({
templateUrl: 'concessiontype.edit.component.html',
selector: 'concessiontype-edit',
styleUrls: ['concessiontype.edit.component.css']
})
export class ConcessiontypeEditComponent implements OnInit {
returnUrl = '';
_id = -1;
msg = "[Concession type edit Component] ";
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>();
concessiontypeForm = this.formBuilder.group({
id: [0], //Validators.required
description: [''], //Validators.required
display: [''], //Validators.required
active: [false], //Validators.required
});
constructor(private formBuilder: UntypedFormBuilder,
private messageService: MessageService,
private concessionTypeService: ConcessionTypeService,
private route: ActivatedRoute, private router: Router) { }
ngOnInit(): void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
const id = Number(this.route.snapshot.paramMap.get('id'));
// now load thing up
this.subscription.add(this.concessiontypeForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
if (id > 0) {
this.subscription.add(
this.concessionTypeService.loadConcessiontypeById(id).subscribe(x => {
this.assignValue(x.data)
}, error => {
console.log("error ", error);
}
)
);
}
}
getEditText(): string {
if (this._id > 0)
return "Edit Concession Type";
else
return "New Concession Type";
}
// this for disable submit button
get isFieldsChange() {
const chan = this.isChange || !this.concessiontypeForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
assignValue(concessiontype: ConcessionType): void {
this.concessiontypeForm.patchValue({
id: concessiontype.id,
description: concessiontype.description,
display: concessiontype.display,
active: concessiontype.active,
});
// disable use false//true for not disable.
this.subChanged$.next(true);
}
// convenience getter for easy access in form fields
get f() { return this.concessiontypeForm.controls; }
onSubmit(): void {
// if form valid then go save
//make sure
const okToSave = this.concessiontypeForm.valid;
if (okToSave) {
let concessiontypeValue = this.concessiontypeForm.value;
let concessiontype: ConcessionType = {
id: concessiontypeValue.id,
description: concessiontypeValue.description,
display: concessiontypeValue.display,
active: concessiontypeValue.active,
};
this.subscription.add(this.concessionTypeService.saveConcessiontype(concessiontype).subscribe(x => {
//console.log("after save return is", x);
if (x.statusCode > 0) {
//alert("save ok");
this.messageService.add({ severity: 'success', summary: 'Success', detail: "Save is OK" });
this.router.navigate([this.returnUrl]);
//this.router.navigate(['/home/concessiontype'])
}
},
error => {
console.log("error ", error);
}
));
}
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
@@ -0,0 +1,40 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ConcessionType, ResultModel, ConfigureUrl } from '../models';
import { AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class ConcessionTypeService {
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {}
loadConcessiontypes(): Observable<ResultModel<ConcessionType[]> > {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" +ConfigureUrl.concessionTypeUrl;
let config = { headers : { 'Content-Type': 'application/json' } };
return this.http.get<ResultModel<ConcessionType[]>>(baseUrl);
}
loadConcessiontypeById(id:number): Observable<ResultModel<ConcessionType>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" +ConfigureUrl.concessionTypeUrl;
let params = new HttpParams().set("id", ""+id);
let headers = new HttpHeaders().set('Content-Type', 'application/json');
let options = {
headers: headers,
params: params
};
return this.http.get<ResultModel<ConcessionType>>(baseUrl +"/"+id);
}
saveConcessiontype(concessType:ConcessionType): Observable<ResultModel<number>> { //insert Concessiontype
const baseUrl = this.appSetting.appSetting.baseUrl + "/" +ConfigureUrl.concessionTypeUrl;
let config = { headers : { 'Content-Type': 'application/json' } };
return this.http.post<ResultModel<number>>(baseUrl , concessType, config);
}
deleteConcessiontype(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/" +ConfigureUrl.concessionTypeUrl;
let config = { headers : { 'Content-Type': 'application/json' } };
return this.http.delete<ResultModel<number>>(baseUrl +"/" + id);
}
}
+4
View File
@@ -0,0 +1,4 @@
export * from './concessiontype.component';
export * from './concessiontype.edit.component';
export * from './concessiontype.service';
@@ -0,0 +1,28 @@
<div class="edit-content">
<h3>Concession Validation Report</h3>
<form [formGroup]="concessionvalidationreportForm">
<div class="flex flex-row gap-2">
<mat-form-field appearance="outline">
<mat-label>Choose a from date</mat-label>
<input matInput formControlName="fromDate" (focus)="pickerfrom.open()" [matDatepicker]="pickerfrom">
<mat-datepicker-toggle matSuffix [for]="pickerfrom"></mat-datepicker-toggle>
<mat-datepicker #pickerfrom></mat-datepicker>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Choose a to date</mat-label>
<input matInput formControlName="toDate" (focus)="pickerto.open()" [matDatepicker]="pickerto">
<mat-datepicker-toggle matSuffix [for]="pickerto"></mat-datepicker-toggle>
<mat-datepicker #pickerto></mat-datepicker>
</mat-form-field>
</div>
<div>
<button type="button" mat-raised-button color="primary" (click)="onDownload()">Download</button>
</div>
<br />
<br />
<h3>User Report</h3>
<div>
<button type="button" mat-raised-button color="primary" (click)="onDownloadUser()">Download Users</button>
</div>
</form>
</div>
@@ -0,0 +1,92 @@
import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import * as moment from 'moment';
import { HttpUtility, CommonUtilities } from '../shares';
import { catchError, finalize } from 'rxjs/operators';
import { EMPTY } from 'rxjs';
import { HttpResponse } from '@angular/common/http';
import { ActivatedRoute, Router } from '@angular/router';
import { MessageService } from 'primeng/api';
@Component({
selector: 'app-concessionvalidation-report',
templateUrl: './concessionvalidationreport.component.html',
styleUrls: ['./concessionvalidationreport.component.css']
})
export class ConcessionvalidationReportComponent implements OnInit {
msg = "[concessionvalidation] ";
returnUrl = "";
concessionvalidationreportForm = this.formBuilder.group({
fromDate: [new Date()], //Validators.required
toDate: [new Date(), Validators.required],
});
constructor(
private formBuilder: UntypedFormBuilder,
private http: HttpUtility,
private router: Router,
private route: ActivatedRoute,
private messageService: MessageService
) {
}
onDownload(): void {
const constDate = this.concessionvalidationreportForm.value;
const fromDate = moment(constDate.fromDate).format("YYYY-MM-DD");
const toDate = moment(constDate.toDate).format("YYYY-MM-DD");
console.log(this.msg + "click on download", fromDate);
//GetReportFile
this.http.getFileResponse(`api/v1/ConcessionValidation/GetReportFile?fromDate=${fromDate}&toDate=${toDate}`).pipe(
catchError(err => {
return EMPTY;
}),
finalize(() => {
this.messageService.add({ severity: 'success', summary: 'Success', detail: "download completed ok to view now" });
})).subscribe((response: HttpResponse<Blob>) => {
if (response) {
console.log("the download report response", response);
CommonUtilities.downloadFile(response, 'report.xlsx');
}
});
}
onDownloadUser(): void {
//GetReportFile
this.http.getFileResponse(`api/v1/Users/GetReportFile`).pipe(
catchError(err => {
return EMPTY;
}),
finalize(() => {
this.messageService.add({ severity: 'success', summary: 'Success', detail: "download completed ok to view now" });
})).subscribe((response: HttpResponse<Blob>) => {
if (response) {
console.log("the download user report response", response);
CommonUtilities.downloadFile(response, 'UserReport.xlsx');
}
});
}
ngOnInit(): void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.assignValue();
}
assignValue(): void {
this.concessionvalidationreportForm.patchValue({
fromDate: new Date(),
toDate: moment(new Date()).add(30, 'd')
});
}
}
@@ -0,0 +1 @@
export * from './concessionvalidationreport.component';
+29
View File
@@ -0,0 +1,29 @@
.welcome {
text-align: center;
}
.container {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}
.mat-sidenav-container,
.mat-sidenav-content {
height: 100%;
}
.mat-sidenav {
width: 250px;
}
.main-content {
padding-left: 25px;
padding-right: 15px;
justify-content: space-around;
}
.mat-toolbar {
justify-content: space-between;
}
+17
View File
@@ -0,0 +1,17 @@
<div class="container">
<app-nav-toolbar (toggleEvent)="showSidenav($event)"></app-nav-toolbar>
<mat-sidenav-container>
<mat-sidenav #sidenav role="navigation" position='end'>
<app-nav-menu (closeMe)="onCloseSidenav($event)"></app-nav-menu>
</mat-sidenav>
<mat-sidenav-content>
<main class="main-content">
<router-outlet></router-outlet>
</main>
</mat-sidenav-content>
</mat-sidenav-container>
<mat-toolbar>
<p class="text-black text-sm mr-2">release 05.12.2022 v1.2</p>
<p class="text-blue-500 text-sm">power by angular {{ngVersion}}</p>
</mat-toolbar>
</div>
+23
View File
@@ -0,0 +1,23 @@
import { Component,ViewChild,VERSION } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent {
ngVersion = '';
openSidenav = false;
@ViewChild(MatSidenav) sidenav!: MatSidenav;
constructor() {
this.ngVersion = VERSION.full;
}
onCloseSidenav(e: boolean): void {
this.sidenav.close();
}
showSidenav(event: boolean): void {
this.sidenav.toggle();
}
}
+7
View File
@@ -0,0 +1,7 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./home.component"));
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;AAAA,sCAAiC"}
+2
View File
@@ -0,0 +1,2 @@
export * from './home.component';
+7
View File
@@ -0,0 +1,7 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./login.component"));
//# sourceMappingURL=index.js.map
+2
View File
@@ -0,0 +1,2 @@
export * from './login.component';
+5
View File
@@ -0,0 +1,5 @@
.btnloginRigh {
display: flex;
flex-direction:reverse;
justify-content:flex-end;
}
+44
View File
@@ -0,0 +1,44 @@
<div style="position:absolute;top:50%;left:50%;transform: translate(-50%, -50%);">
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<mat-card style="max-width:600px">
<img mat-card-image src="assets/images/application_image.png" alt="Nepean Car Park Validation">
<mat-card-content>
<p>Please Login to Continue</p>
<div class="flex flex-column">
<mat-form-field>
<input type="text" formControlName="username" matInput placeholder="StaffLinkNo/Employee no"
[ngClass]="{ 'is-invalid': submitted && f.username.errors }" />
</mat-form-field>
<div *ngIf="submitted && f.username.errors" class="invalid-feedback">
<div *ngIf="f.username.errors.required">StaffLinkNo is required</div>
</div>
<mat-form-field>
<input type="password" formControlName="password" matInput placeholder="password"
[ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
</mat-form-field>
<div *ngIf="submitted && f.password.errors" class="invalid-feedback">
<div *ngIf="f.password.errors.required">Password is required</div>
</div>
</div>
</mat-card-content>
<mat-card-actions class="btnloginRigh">
<button pButton type="submit" label="Login" class="p-button-sm" [loading]="loading"></button>
</mat-card-actions>
<div *ngIf="error" class="alert alert-danger">{{error}}</div>
</mat-card>
<div>
<mat-card style="max-width:600px;background-color:#e1e1e1; color:#222">
<mat-card-content style="text-align:center;font-size:13px;">
<p>
Please NOTE access to this database is restricted to approved employees for parking validations only.
If you require access to this database or further information, please email
</p>
<a href="mailto:{{adminEmail}}"> {{adminEmail}}
</a>
</mat-card-content>
</mat-card>
</div>
</form>
</div>
+76
View File
@@ -0,0 +1,76 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { UntypedFormBuilder, FormGroup, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';
import { AuthenticationService } from '../user-services';
import { AppSettingService } from '../shares';
@Component({
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
selector: 'login'
})
export class LoginComponent implements OnInit {
loginForm = this.formBuilder.group({
username: ['', Validators.required],
password: ['', Validators.required]
});
loading = false;
submitted = false;
adminEmail = "";
// get return url from route parameters or default to '/'
returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/home/concessionvalidation';
error = '';
constructor(
private formBuilder: UntypedFormBuilder,
private route: ActivatedRoute,
private router: Router,
private appSetting: AppSettingService,
private authenticationService: AuthenticationService) {
this.adminEmail = this.appSetting.appSetting.adminEmail;
}
ngOnInit() {
// reset login status
this.authenticationService.logout();
}
// convenience getter for easy access to form fields
get f() { return this.loginForm.controls; }
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
}
this.loading = true;
this.authenticationService.login(this.f.username.value, this.f.password.value)
.pipe(first())
.subscribe(
data => {
this.loading = false;
console.log("login result ", data);
if (data == 1)
this.router.navigate([this.returnUrl]);
else {
this.loginForm.patchValue({ password: '' });
alert("Invalid Username or Password. Please try again.");
}
},
er => {
console.log("error in login", er);
this.error = er.message;
this.loading = false;
});
}
}
@@ -0,0 +1,95 @@
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatNativeDateModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTreeModule } from '@angular/material/tree';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatMenuModule } from '@angular/material/menu';
import { MatExpansionModule } from '@angular/material/expansion';
import {
MatMomentDateModule
} from '@angular/material-moment-adapter'
@NgModule({
imports: [
MatIconModule,
MatButtonModule,
MatCheckboxModule,
MatInputModule,
MatDatepickerModule,
MatFormFieldModule,
MatTableModule,
MatSortModule,
MatPaginatorModule,
MatNativeDateModule,
MatMomentDateModule,
MatCardModule,
MatDialogModule,
MatSelectModule,
MatSidenavModule,
MatToolbarModule,
MatListModule,
MatTabsModule,
MatProgressSpinnerModule,
MatTreeModule,
MatTooltipModule,
MatSnackBarModule,
MatProgressBarModule,
MatMenuModule,
MatExpansionModule,
],
exports: [
MatIconModule,
MatButtonModule,
MatCheckboxModule,
MatInputModule,
MatDatepickerModule,
MatFormFieldModule,
MatTableModule,
MatSortModule,
MatPaginatorModule,
MatNativeDateModule,
MatMomentDateModule,
MatCardModule,
MatDialogModule,
MatSelectModule,
MatSidenavModule,
MatToolbarModule,
MatListModule,
MatTabsModule,
MatProgressSpinnerModule,
MatTreeModule,
MatSnackBarModule,
MatTooltipModule,
MatProgressBarModule,
MatMenuModule,
MatExpansionModule,
]
})
export class AppMaterialModule { }
+21
View File
@@ -0,0 +1,21 @@
export interface AdminUser {
id: number,
stafflinkNo: string,
name: string,
addedBy: string,
addedOn: Date,
active: boolean,
roleType: number,
validationPointId: number,
}
export interface AdminUserView {
id: number,
stafflinkNo: string,
name: string,
addedBy: string,
addedOn: Date,
active: boolean,
roleType: string,
validationPoint: string,
}
+6
View File
@@ -0,0 +1,6 @@
export interface ConcessionType {
id: number,
description: string,
display: string,
active: boolean,
}
+14
View File
@@ -0,0 +1,14 @@
export interface ConcessionValidation {
id: number,
validateDate: Date,
concessionCard: string,
concessionHolder: string,
concessionExpiry: boolean,
concessionTypeId: number,
stafflinkNo: string,
staffFirstName:string,
staffLastName:string,
staffEmail:string,
validationPointId: number,
active: boolean,
}
+17
View File
@@ -0,0 +1,17 @@
export enum ConfigureUrl
{
//baseUrl = "http://localhost:61744",
authenticateUrl = "api/v1/Users/Authenticate", //no use anymore
loginADUrl = "api/v1/Users/LoginAD",
loginApiUrl = "api/v1/Users/LoginApiAD",
validationPoint = "api/v1/ValidationPoint", //no "/" at the front
adminUserUrl = "api/v1/AdminUser",
concessionValidationUrl = "api/v1/ConcessionValidation",
concessionValidationByIdUrl = "api/v1/ConcessionValidation/ConcessionValidationByStaff",
CheckConcessionValidationByStaffUrl = "api/v1/ConcessionValidation/CheckConcessionValidationByStaff",
concessionTypeUrl = "api/v1/ConcessionType",
userByStaffIdUrl = "api/v1/Users/SearchApiStaff",
luConcessionTypeUrl = "api/v1/LookupCode/ConcessionTypes",
luValidationPointUrl = "api/v1/LookupCode/ValidationPoints",
}
+44
View File
@@ -0,0 +1,44 @@
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 PickListType {
Ok =0,
BodyPart = 16, // Body Part
Location = 17,
Equipment = 18,
LocationType = 19,
CheckList = 20,
Company = 21,
NonConformance = 22,
Occupation = 23,
IncidentType = 24,
InvestigationAgency = 25,
OutCome = 26,
Visibility = 27,
Surface = 28,
Lighting = 29,
Weather = 30,
CauseOfPrimaryInjury = 31,
ContributingFactor = 32,
HazardControlType = 33,
}
export enum userRole {
Normal = 1,
Admin = 2,
};
+9
View File
@@ -0,0 +1,9 @@
export * from './user';
export * from './validationpoint';
export * from './enum';
export * from './resultmodel';
export * from './adminuser';
export * from './configureUrl';
export * from './concessionvalidation';
export * from './lookup';
export * from './concessiontype';
+7
View File
@@ -0,0 +1,7 @@
export interface Lookup {
id: number;
categoryId: number;
description: string;
};
+44
View File
@@ -0,0 +1,44 @@
export interface treeLookupCode {
id: number;
code: string;
description: string;
canCheck: number; // 0 no 1 is yes
hasChild: number; // 0 no 1 is yes
}
export interface LookupCode {
id: number;
code: string;
description: string;
}
export interface LookupEnumContainer {
enumId: number;
list: LookupCode[];
}
export interface BusinessUnitLookup {
id: number;
abbreviation: string;
levelID: number;
name: string;
parentID: number;
}
export interface LookupBUData {
allowMultiSelect: boolean;
pickListTitle: string;
lookupList: BusinessUnitLookup[];
}
export interface LookupData {
allowMultiSelect:boolean;
pickListTitle: string;
lookupList: LookupCode[];
};
export interface LookupCodeContainer {
list: LookupCode[];
totalPages: number;
}
+7
View File
@@ -0,0 +1,7 @@
export interface PageQuery {
pageIndex: number;
pageSize: number;
search: string;
};
+80
View File
@@ -0,0 +1,80 @@
import { Lookup } from './lookup';
export interface SharePersonContainer {
list: PersonLite[];
totalPages: number;
}
export interface PersonLite {
id: number;
firstName: string;
surname: string;
referenceNo: string;
payrollNo: number;
email: string;
phoneNo: string;
}
export interface PersonLookup {
categorys: Lookup[];
title: Lookup[]; ///for source
positions: Lookup[]; ///for source
companys: Lookup[];
personStatus: Lookup[];
englishLevel: Lookup[];
yesNoPickList: Lookup[];
language: Lookup[];
residence: Lookup[];
nationality: Lookup[];
gender: Lookup[];
}
export interface PersonContainer {
person: Person;
}
export class Person {
id: number =0;
titleId: number =0;
firstName: string = '';
surname: string = '';
referenceNo: string = '';
workNo: string = '';
email: string = '';
categoryId: number = 0;
statusId: number = 0;
statusDate: string = '';
businessUnitId: number = 0;
businessUnitDesc: string = '';
}
export interface PersonDetail {
street: string;
postCode: string;
suburb: string;
state: string;
country: string;
home: string;
fax: string;
mobile: string;
work: string;
email: string;
mailStreetPostCode: string;
mailPostCode: string;
mailSuburb: string;
mailState: string;
mailCountry: string;
dateOfBirth: string;
genderId: number;
nationalityId: number;
residenceId: number;
englishLevelId: number;
interpretRequired: number;
languageSpokenId: string;
driverLicenceNo: string;
expiryDate: string;
}
+45
View File
@@ -0,0 +1,45 @@
import { BehaviorSubject } from 'rxjs';
import { treeLookupCode } from './lookupcode';
enum EnumLookup {
BodyPart = 16,
Location = 17,
Equipment = 18,
LocationType = 19,
CheckList = 20
}
export class DataNode {
loaded: boolean = false;
name: string ='';
id: number = 0;
children: DataNode[] =[];
}
export class UIDataNode {
allowMultiSelect: boolean = false;
pickListTitle: string = '';
enumPickType: number = 0;
children: treeLookupCode[] = [];
}
/** Nested node */
export class DataNode$ {
childrenChange = new BehaviorSubject<DataNode$[]>([]);
expanable = new BehaviorSubject<boolean>(true);
get children(): DataNode$[] {
return this.childrenChange.value;
}
get isExpanable(): boolean {
return this.expanable.value;
}
constructor(public id: number,
public name: string,
public canCheck: boolean,
public hasChildren: boolean,
public parentId: number,
public loadMoreParentItem: number | null = null) { }
}
+5
View File
@@ -0,0 +1,5 @@
export interface ResultModel<T> {
data: T;
message: string;
statusCode:number;
}
+10
View File
@@ -0,0 +1,10 @@
export class User {
id: number = 0;
username: string = '';
role: number = -1;
firstName: string = '';
lastName: string = '';
email:string = '';
token:string ='';
validationPointId:number = 0;
}
+6
View File
@@ -0,0 +1,6 @@
export interface ValidationPoint {
id: number,
description: string,
display: string,
active: boolean,
}
@@ -0,0 +1,15 @@
a {
text-decoration: none;
}
.mat-expansion-panel-header {
padding-left: 16px !important;
}
a:hover,
a:active {
color: lightgray;
}
.nav-caption{
display:inline-block;
padding-left: 10px;
}
@@ -0,0 +1,42 @@
<mat-nav-list>
<a *ngIf="isAuth" mat-list-item [routerLink]='["/home/concessionvalidation"]' (click)='collapse()'>
<mat-icon>assignment_turned_in</mat-icon><span class="nav-caption">Concession Validation</span>
</a>
<a *ngIf="isAdmin" mat-list-item [routerLink]='["/home/app-concessionvalidation-report"]' (click)='collapse()'>
<mat-icon>description</mat-icon> <span class="nav-caption">Report</span>
</a>
<!--a *ngIf="isAdmin" mat-list-item href="http://nephmdb-sql007/CarParkValidationReport/Reports/ConcessionValidation.aspx" target="_blank" (click)='collapse()'>
<mat-icon>description</mat-icon> <span class="nav-caption">Report 007</span>
</a>
<a *ngIf="isAdmin" mat-list-item href="http://virtapp-mdb048/CarParkValidationReport/Reports/ConcessionValidation.aspx" target="_blank" (click)='collapse()'>
<mat-icon>description</mat-icon> <span class="nav-caption">Report 048</span>
</a-->
<!--Admin menu-->
<mat-accordion *ngIf="isAdmin">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<mat-icon>settings</mat-icon> <span class="nav-caption">Settings</span>
</mat-panel-title>
</mat-expansion-panel-header>
<a mat-list-item [routerLink]='["/home/adminuser"]' (click)='collapse()'>
<mat-icon>manage_accounts</mat-icon><span class="nav-caption">Admin User</span>
</a>
<a mat-list-item [routerLink]='["/home/concessiontype"]' (click)='collapse()'>
<mat-icon>redeem</mat-icon> <span class="nav-caption">Concession Type</span>
</a>
<a mat-list-item [routerLink]='["/home/validationpoint"]' (click)='collapse()'>
<mat-icon>report_problem</mat-icon> <span class="nav-caption">Validation Point</span>
</a>
</mat-expansion-panel>
</mat-accordion>
<a *ngIf="!isAuth" mat-list-item [routerLink]='["/login"]' (click)='collapse()'>
<mat-icon>input</mat-icon><span class="nav-caption">Login</span>
</a>
<a *ngIf="isAuth" mat-list-item [routerLink]='["/login"]' (click)='onLogout()'>
<mat-icon>input</mat-icon><span class="nav-caption">Logout</span>
</a>
</mat-nav-list>
+46
View File
@@ -0,0 +1,46 @@
import { Component, EventEmitter, Output, OnInit, OnDestroy } from '@angular/core';
import { AuthenticationService } from '../user-services';
import { Subscription } from 'rxjs'
import { Router } from '@angular/router';
import { Utils } from '../shares';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent implements OnInit, OnDestroy{
isAuth = false;
isAdmin = false;
panelOpenState = false;
authSubscription!: Subscription;
@Output() closeMe: EventEmitter<boolean> = new EventEmitter();
constructor(private authService: AuthenticationService, private router: Router) { }
ngOnInit() {
this.authSubscription = this.authService.authChange.subscribe(currentUser => {
this.isAuth = Utils.getIsAuth();
this.isAdmin = Utils.getIsAdmin();
});
}
ngOnDestroy() {
if (this.authSubscription) {
this.authSubscription.unsubscribe();
}
}
collapse() {
this.closeMe.emit(true);
}
onLogout() {
console.log("onlogout click");
this.collapse();
this.authService.logout();
// this.router.navigate(['/login']);
}
}
@@ -0,0 +1,38 @@
a {
text-decoration: none;
color: white;
}
a:hover,
a:active {
color: lightgray;
}
/* may be is not use anymore */
li .glyphicon {
margin-right: 10px;
}
.navigation-items {
list-style: none;
padding: 0;
margin: 0;
}
/* Highlighting rules for nav menu items */
li.link-active a,
li.link-active a:hover,
li.link-active a:focus {
background-color: #4189C7;
color: white;
}
/* Keep the nav menu independent of scrolling and on top of other items */
.main-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1;
}
@@ -0,0 +1,20 @@
<mat-toolbar class="flex justify-content-between" style="background-color: #e12a40;">
<div style="margin-top: 5px;padding-right: 35px; cursor: pointer;height:70px;">
<img (click)="onHome()" src="assets/images/application_image.png" alt="Parking Concession Validation Register"
style="height:70px;width:320px" />
</div>
<div class="flex justify-content-end">
<ul class="navigation-items">
<li *ngIf="!isAuth">
<a [routerLink]='["/login"]'>
Login
</a>
</li>
</ul>
<div class="font-bold text-white">
{{currentUserName}}
</div>
<button (click)="onToggle()" mat-icon-button type="button"><mat-icon>arrow_drop_down</mat-icon></button>
</div>
</mat-toolbar>
@@ -0,0 +1,51 @@
import { AuthenticationService } from '../user-services';
import { Subscription } from 'rxjs'
import { Component, EventEmitter, Output, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { Utils } from '../shares';
@Component({
selector: 'app-nav-toolbar',
templateUrl: './nav-toolbar.component.html',
styleUrls: ['./nav-toolbar.component.css']
})
export class NavToolarComponent implements OnInit, OnDestroy {
isExpanded = false;
isAuth = false;
isAdmin = false;
currentUserName = "";
authSubscription: Subscription = new Subscription;
@Output() toggleEvent: EventEmitter<boolean> = new EventEmitter();
constructor(private authService: AuthenticationService,
private router: Router) { }
ngOnInit() {
this.authSubscription = this.authService.authChange.subscribe(currentUser => {
this.isAuth = Utils.getIsAuth();
this.isAdmin = Utils.getIsAdmin();
this.currentUserName = currentUser;
});
}
ngOnDestroy() {
if (this.authSubscription) {
this.authSubscription.unsubscribe();
}
}
onHome() {
this.router.navigate(['/home/concessionvalidation']);
}
onToggle() {
this.isExpanded = !this.isExpanded;
this.toggleEvent.emit(this.isExpanded);
}
onLogout() {
this.authService.logout();
this.router.navigate(['/login']);
}
}
@@ -0,0 +1,4 @@
<div class="container">
<h1>Not found</h1>
<button [routerLink]="['/']">Return to home page</button>
</div>
@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.css']
})
export class NotFoundComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
+6
View File
@@ -0,0 +1,6 @@
export * from './person.component';
export * from './person.list.component';
export * from './person.edit.component';
export * from './person.detail.edit.component';
export * from './person.service';
+10
View File
@@ -0,0 +1,10 @@
th.mat-sort-header-sorted {
color: black;
}
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
+73
View File
@@ -0,0 +1,73 @@
<h1>Person List</h1>
<p>This component demonstrates Person material table.</p>
<mat-form-field>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter">
</mat-form-field>
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- Id Column -->
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef> Id </th>
<td mat-cell *matCellDef="let element"> {{element.id}} </td>
</ng-container>
<!-- ReferenceNo Column -->
<ng-container matColumnDef="referenceNo">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ReferenceNo </th>
<td mat-cell *matCellDef="let element"> {{element.referenceNo}} </td>
</ng-container>
<!-- FirstName Column -->
<ng-container matColumnDef="firstName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> FirstName </th>
<td mat-cell *matCellDef="let element"> {{element.firstName}} </td>
</ng-container>
<!-- Surname Column -->
<ng-container matColumnDef="surname">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Surname </th>
<td mat-cell *matCellDef="let element"> {{element.surname}} </td>
</ng-container>
<ng-container matColumnDef="edit" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let element">
<a [routerLink]='["/person", element.id]' [queryParams]="{returnUrl:'/person'}">
<mat-icon>border_color</mat-icon>
</a>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSize]="5" [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons></mat-paginator>
<!--
<p *ngIf="!persons"><em>Loading...</em></p>
<table class='table' *ngIf="persons">
<thead>
<tr>
<th>Id</th>
<th>ReferenceNo</th>
<th>First Name</th>
<th>Surname</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of persons">
<td> person.id </td>
<td> person.referenceNo </td>
<td> person.firstName </td>
<td> person.surname </td>
<td>
<a [routerLink]='["/person", person.id]' [queryParams]="{returnUrl:'/person'}">
<span class='glyphicon glyphicon-user'></span> Edit
</a>
</td>
</tr>
</tbody>
</table>
-->
+50
View File
@@ -0,0 +1,50 @@
import { Component, Inject, OnInit, AfterViewInit , ViewChild} from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { HttpClient } from '@angular/common/http';
import { PersonService } from './person.service';
import { Person } from '../models';
@Component({
selector: 'person-data',
templateUrl: './person.component.html',
styleUrls: ['./person.component.css']
})
export class PersonComponent implements OnInit, AfterViewInit{
private _baseUrl: string;
dataSource: MatTableDataSource<Person>;
displayedColumns: string[] = ['id', 'referenceNo', 'firstName', 'surname', 'edit'];
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatPaginator) paginator!: MatPaginator;
constructor(private http: HttpClient,
private personService: PersonService,
@Inject('BASE_URL') baseUrl: string) {
this._baseUrl = baseUrl;
this.dataSource = new MatTableDataSource<Person>();
}
ngAfterViewInit(): void {
this.dataSource.sort = this.sort!;
this.dataSource.paginator = this.paginator!;
}
ngOnInit(): void
{
this.personService.loadPersons(10,1).subscribe(result => {
console.log('Persons', result);
this.dataSource.data = result;
}, error => console.error(error));
}
applyFilter($event:any) {
const filterValue = $event.target.value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
}
@@ -0,0 +1,6 @@
.mat-form-field {
font-size: 14px;
width: 95%;
}
@@ -0,0 +1,151 @@
<form [formGroup]="personDetailForm" >
<div fxLayout
fxLayout.xs="column"
fxLayoutAlign="none"
fxLayoutGap="10px"
fxLayoutGap.xs="0">
<div class="item1" fxFlex="30%" fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill>
<mat-form-field>
<input type="text" formControlName="home" matInput placeholder="Home">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="fax" matInput placeholder="Fax">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="mobile" matInput placeholder="Mobile">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="work" matInput placeholder="Work">
</mat-form-field>
<mat-form-field>
<input type="email" formControlName="email" matInput placeholder="Email">
</mat-form-field>
</mat-card>
</div>
<div class="item2" fxFlex="30%" fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill>
<mat-form-field>
<input type="text" formControlName="street" matInput placeholder="Street">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="postCode" matInput placeholder="Post Code">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="suburb" matInput placeholder="Suburb">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="state" matInput placeholder="State">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="country" matInput placeholder="Country">
</mat-form-field>
</mat-card>
</div>
<div class="item3" fxFlex fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill>
<mat-form-field>
<input type="text" formControlName="mailStreetPostCode" matInput placeholder="Street/PO Box">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="mailPostCode" matInput placeholder="Post Code">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="mailSuburb" matInput placeholder="Suburb">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="mailCountry" matInput placeholder="Country">
</mat-form-field>
</mat-card>
</div>
</div>
<!--second section this for space between two group.-->
<div fxLayout="column" fxLayoutGap="10px"><p></p></div>
<div
fxLayout
fxLayout.xs="column"
fxLayoutAlign="none"
fxLayoutGap="10px"
fxLayoutGap.xs="0">
<div fxFlex="50%" class="item1" fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill>
<mat-form-field>
<input formControlName="dateOfBirth" matInput [matDatepicker]="DOBOnPicker" placeholder="Date Of Birth">
<mat-datepicker-toggle matSuffix [for]="DOBOnPicker"></mat-datepicker-toggle>
<mat-datepicker #DOBOnPicker></mat-datepicker>
</mat-form-field>
<picklist-tree-comp [PickListDesc]="'Body Part'"
[EnumPickListType]="getEnumBodyPartId()"
[AllowMultiple]="true" [DialogTitle]="'Pick Body Part'"
[placeholder]="'Body Part'" formControlName="genderId"></picklist-tree-comp>
<mat-form-field>
<mat-select placeholder="Gender" formControlName="genderId">
<mat-option *ngFor="let cs of lookup?.gender" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Nationality" formControlName="nationalityId">
<mat-option *ngFor="let cs of lookup?.nationality" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Residence" formControlName="residenceId">
<mat-option *ngFor="let cs of lookup?.residence" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card>
</div>
<div class="item2" fxFlex="30%" fxLayoutAlign="none stretch">
<mat-card>
<mat-form-field>
<mat-select placeholder="English Level" formControlName="englishLevelId">
<mat-option *ngFor="let cs of lookup?.englishLevel" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Interpret Required" formControlName="interpretRequired">
<mat-option *ngFor="let cs of lookup?.yesNoPickList" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Language Spoken" formControlName="languageSpokenId">
<mat-option *ngFor="let cs of lookup?.language" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="driverLicenceNo" matInput placeholder="Driver Licence No">
</mat-form-field>
<mat-form-field>
<input formControlName="expiryDate" matInput [matDatepicker]="expiryDateOnPicker" placeholder="Expirt Date">
<mat-datepicker-toggle matSuffix [for]="expiryDateOnPicker"></mat-datepicker-toggle>
<mat-datepicker #expiryDateOnPicker></mat-datepicker>
</mat-form-field>
</mat-card>
</div>
</div>
</form>
@@ -0,0 +1,207 @@
import { Component, OnInit, Input , Output, EventEmitter} from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { PersonDetail, LookupCode, PersonLookup, BusinessUnitLookup, LookupBUData, PickListType } from '../models';
import { PersonService } from './person.service';
import { OverlayService } from '../overlay/overlay.module';
import { LookupService } from '../share/lookup.service';
import { LookupTreeComponent } from '../share/lookup.tree.component';
@Component({
templateUrl: 'person.detail.edit.component.html',
selector: 'person-detail-edit',
styleUrls: ['person.detail.edit.component.css']
})
export class PersonDetailEditComponent implements OnInit {
personDetailForm: FormGroup = this.formBuilder.group({
street: [''],
postCode: [''],
suburb: [''],
state: [''],
country: [''],
home: [''],
fax: [''],
mobile: [''],
work: [''],
email: [''],
mailStreetPostCode: [''],
mailPostCode: [''],
mailSuburb: [''],
mailState: [''],
mailCountry: [''],
dateOfBirth: [''],
genderId: [0],
nationalityId: [],
residenceId: [0],
englishLevelId: [0],
interpretRequired: [0],
languageSpokenId: [0],
driverLicenceNo: [''],
expiryDate: [''],
});
loading = false;
submitted = false;
returnUrl: string= '';
error = '';
lookup: PersonLookup |undefined;
@Input()
set PersonDetailObj(value: PersonDetail) {
this.assignValue(value);
}
@Output() detailFieldChangeEvent = new EventEmitter<any>();
set Lookup(value: PersonLookup) {
this.lookup = value;
}
constructor(
private previewProgressSpinner: OverlayService,
private lookupService: LookupService,
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private dialog: MatDialog,
private personService: PersonService
)
{
//this.lookup = { categorys: [], positions: [], companys: [], personStatus: [], englishLevel: [] };
}
getEnumBodyPartId(): number {
return PickListType.BodyPart;
}
ngOnInit() {
let id = this.route.snapshot.paramMap.get('id');
this.buildFormGroup();
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
console.log('the return Url is ', this.returnUrl);
}
buildFormGroup(): void {
this.personDetailForm.valueChanges.subscribe(x => {
console.log("the person detail edit component data fields are changes", x);
console.log(" emit to listener etc ....");
this.detailFieldChangeEvent.emit(true);
})
}
getPersonDetail() {
const isChange = this.personDetailForm.dirty;
if ( isChange) {
const person = this.personDetailForm.value;
const personDetail: PersonDetail = {
street: person.street,
postCode: person.postCode,
state: person.state,
suburb: person.suburb,
country: person.country,
home: person.home,
mobile: person.mobile,
work: person.work,
email: person.email,
fax: person.fax,
mailStreetPostCode: person.mailStreetPostCode,
mailPostCode: person.mailPostCode,
mailSuburb: person.mailSuburb,
mailState: person.mailState,
mailCountry: person.mailCountry,
dateOfBirth: person.dateOfBirth,
genderId: person.genderId,
nationalityId: person.nationalityId,
residenceId: person.residenceId,
englishLevelId: person.englishLevelId,
interpretRequired: person.interpretRequired,
languageSpokenId: person.languageSpokenId,
driverLicenceNo: person.driverLicenceNo,
expiryDate: person.expiryDate
}
return personDetail;
}
else
return null;
}
assignValue(person: PersonDetail):void {
console.log("assign value ", person);
this.personDetailForm.patchValue(
{
state: person.state,
country: person.country,
street: person.street,
postCode: person.postCode,
home: person.home,
mobile: person.mobile,
work: person.work,
email: person.email,
fax: person.fax,
mailStreetPostCode: person.mailStreetPostCode,
mailPostCode: person.mailPostCode,
mailSuburb: person.mailSuburb,
mailState: person.mailState,
mailCountry: person.mailCountry,
dateOfBirth: person.dateOfBirth,
genderId: person.genderId,
nationalityId: person.nationalityId,
residenceId: person.residenceId,
englishLevelId: person.englishLevelId,
interpretRequired: person.interpretRequired,
languageSpoken: person.languageSpokenId,
driverLicenceNo: person.driverLicenceNo,
expiryDate: person.expiryDate
});
}
// convenience getter for easy access to form fields
get f() { return this.personDetailForm.controls; }
businessLookup(): void {
console.log('lookup person Dialog click.');
this.previewProgressSpinner.showLoading();
this.lookupService.loadBusinessLookup(0).subscribe(result => {
console.log('lookup business', result);
this.previewProgressSpinner.close();
const dialogRef = this.openBULookup(result, true);
dialogRef.afterClosed().subscribe((result: string | any[]) => {
console.log('The dialog was closed patchValue', result);
if (result && result.length > 0) {
let fitem = result[0];
this.personDetailForm.patchValue(
{
businessUnitId: fitem.id,
businessUnitDesc : fitem.description
});
}
});
}, error => console.error(error));
}
openBULookup(data: BusinessUnitLookup[], allowMulti: boolean): any {
var lookup: LookupBUData = { allowMultiSelect: allowMulti, lookupList: data, pickListTitle: "Business Unit" };
const dialogRef = this.dialog.open(LookupTreeComponent, {
width: '600px',
data: lookup
});
return dialogRef;
}
}
@@ -0,0 +1,7 @@
.example-button-row button,
.example-button-row a {
margin-right: 2px;
}
form {
height: 100%;
}
@@ -0,0 +1,81 @@
<form [formGroup]="personForm" (ngSubmit)="onSubmit()">
<div >
<button type="submit" mat-icon-button color="primary" [disabled]="isFieldsChange">
<mat-icon title="Save" aria-label="Example icon-button with a heart icon">save</mat-icon>
</button>
</div>
<div fxLayout
fxLayout.xs="column"
fxLayoutAlign="none"
fxLayoutGap="10px"
fxLayoutGap.xs="0">
<div class="item1" fxFlex="20%" fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill>
<input type="hidden" formControlName="id">
<mat-form-field>
<mat-select placeholder="Title" formControlName="titleId">
<mat-option *ngFor="let title of lookup?.title" [value]="title.id">
{{title.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="referenceNo" matInput placeholder="Reference No">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="firstName" matInput placeholder="FirtName" [ngClass]="{ 'is-invalid': submitted && f.firstName.errors }">
</mat-form-field>
<div *ngIf="submitted && f.firstName.errors" class="invalid-feedback">
<div *ngIf="f.firstname.errors!.required">firstname is required</div>
</div>
<mat-form-field>
<input type="text" formControlName="surname" matInput placeholder="Surname" [ngClass]="{ 'is-invalid': submitted && f.surname.errors }">
</mat-form-field>
<div *ngIf="submitted && f.surname.errors" class="invalid-feedback">
<div *ngIf="f.password.errors!.required">surname is required</div>
</div>
<mat-form-field>
<input type="text" formControlName="workNo" matInput placeholder="Telephone No">
</mat-form-field>
<mat-form-field>
<input type="text" formControlName="email" matInput placeholder="Email">
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Category" formControlName="categoryId">
<mat-option *ngFor="let category of lookup?.categorys" [value]="category.id">
{{category.description}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card>
</div>
<div class="item2" fxFlex="35%" fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill fxLayoutAlign="none stretch">
<div fxLayout="column" fxLayoutAlign="none stretch">
<business-lookup-comp [businessDesc]="personForm.value.businessUnitDesc" [placeholder]="'Business'" formControlName="businessUnitId"></business-lookup-comp>
<div fxLayout="row" fxFill fxLayoutAlign="none stretch">
<mat-form-field>
<mat-select placeholder="Status" formControlName="statusId">
<mat-option *ngFor="let cs of lookup?.personStatus" [value]="cs.id">
{{cs.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Status Date" formControlName="statusDate" readonly>
</mat-form-field>
</div>
</div>
</mat-card>
</div>
</div>
<div>
<pre>{{ personForm.value|json}}</pre>
</div>
</form>
+209
View File
@@ -0,0 +1,209 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Subject, pipe } from 'rxjs';
import {Person, PersonContainer, PersonLookup, BusinessUnitLookup, LookupBUData, PersonDetail } from '../models';
import { PersonService } from './person.service';
import { OverlayService } from '../overlay/overlay.module';
import { LookupService } from '../share/lookup.service';
import { LookupTreeComponent } from '../share/lookup.tree.component';
//import { PersonDetailEditComponent } from './person.detail.edit.component';
/* NgRx */
import { Store, select } from '@ngrx/store';
import * as fromSelector from './store/selectors/person.selector';
import * as fromAppState from './store/reducers/person.app.state';
import * as fromActions from './store/actions/person.action';
@Component({
templateUrl: 'person.edit.component.html',
selector: 'person-edit',
styleUrls: ['person.edit.component.css']
})
export class PersonEditComponent implements OnInit {
personForm: FormGroup = this.formBuilder.group({
id: [0],
businessUnitId: [0],
titleId: [0],
statusId: [{ value: 0, disabled: true }],
categoryId: [0],
statusDate: [''],
email: [''],
businessUnitDesc: [''],
referenceNo: [''],
firstName: ['', Validators.required],
surname: ['', Validators.required],
workNo: [''],
});
loading = false;
submitted = false;
returnUrl: string = '';
isChange = true;
subChanged$ = new Subject<boolean>();
error = '';
lookup: any;
// @ViewChild(PersonDetailEditComponent, { static: true }) personDetailComp: PersonDetailEditComponent;
constructor(
private previewProgressSpinner: OverlayService,
private lookupService: LookupService,
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private store: Store<fromAppState.AppState>,
private dialog: MatDialog,
private personService: PersonService) {
this.lookup = {
categorys: [], title: [], personStatus: [], yesNoPickList: [],
};
}
ngOnInit() {
//let id = this.route.snapshot.paramMap.get('id');
const id = this.route.snapshot.params.id;
//subject track for change
this.personForm.valueChanges.subscribe(x => this.isChange = false);
this.subChanged$.subscribe(x => this.isChange = x);
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
console.log("the id:" + id);
console.log("person id: ", this.route.snapshot);
if (id) {
this.store.dispatch(fromActions.LoadPersonById({ id: +id }));
this.store.pipe(select(fromSelector.getPersonForEdit))
.subscribe(result => {
if (result != null) {
// this.personDetailComp.PersonDetailObj = result.personDetail;
this.assignValue(result.person);
console.log("here the get person for edit is select from store", result);
this.subChanged$.next(true); //reset it.
}
});
}
else
console.log("new Person empty from edit person");
//get lookup
this.personService.loadPersonPickList().subscribe(result => {
this.lookup = result;
// this.personDetailComp.Lookup = this.lookup;
console.log('the lookup for person ', this.lookup);
});
this.store.pipe(select(fromSelector.getPersonStatusCode)).subscribe(
/* go back to return url */
no => {
if (no < 0)
{
console.log('ERROR ');
}
else if (no == 1) {
console.log('close Edit it! save already: ' + this.returnUrl);
this.router.navigate([this.returnUrl]);
}
}
);
}
assignValue(person: Person) {
console.log("assign value ", person);
this.personForm.patchValue(
{
id: person.id,
firstName: person.firstName,
surname: person.surname,
titleId: person.titleId,
workNo: person.workNo,
referenceNo: person.referenceNo,
email: person.email,
statusId: person.statusId,
categoryId: person.categoryId,
statusDate: person.statusDate,
businessUnitId: person.businessUnitId,
businessUnitDesc: person.businessUnitDesc
});
}
// convenience getter for easy access to form fields
get f() { return this.personForm.controls; }
businessLookup(): void {
console.log('lookup person Dialog click.');
this.previewProgressSpinner.showLoading();
this.lookupService.loadBusinessLookup(0).subscribe(result => {
console.log('lookup business', result);
this.previewProgressSpinner.close();
const dialogRef = this.openBULookup(result, false);
dialogRef.afterClosed().subscribe((result: string | any[]) => {
console.log('The dialog was closed patchValue', result);
if (result && result.length > 0) {
const fitem = result[0];
this.personForm.patchValue(
{
businessUnitId: fitem.id,
businessUnitDesc : fitem.description
});
}
});
}, error => console.error(error));
}
openBULookup(data: BusinessUnitLookup[], allowMulti: boolean): any {
const lookup = { allowMultiSelect: allowMulti, lookupList: data, pickListTitle: "Business Unit" };
const dialogRef = this.dialog.open(LookupTreeComponent, {
width: '600px',
data: lookup
});
return dialogRef;
}
onDetailChange(isChange: boolean) {
console.log("<incidentReport Edit> Incident Detail Edit fire event ", isChange);
this.subChanged$.next(false);
}
get isFieldsChange() {
const chan = this.isChange;
console.log('is fields change', chan);
return chan;
}
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.personForm.invalid) {
return;
}
const personValue = this.personForm.value;
//console.log("save person Value", personValue);
this.loading = true;
const person: Person = {
id: +personValue.id,
firstName: personValue.firstName,
surname: personValue.surname,
titleId: personValue.titleId,
referenceNo: personValue.referenceNo,
workNo: personValue.workNo,
email: personValue.email,
statusId: personValue.statusId,
categoryId: personValue.categoryId,
statusDate: '',
businessUnitId: personValue.businessUnitId,
businessUnitDesc: '',
};
//const personDetail = this.personDetailComp.getPersonDetail();
const personContainer: PersonContainer = { person: person };
this.store.dispatch(fromActions.SavePerson({ personContainer }));
this.store.pipe(select(fromSelector.getPersonMessage)).subscribe(error => this.error = error);
}
}
@@ -0,0 +1,44 @@
th.mat-sort-header-sorted {
color: black;
}
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
.containerflex {
display: flex;
flex-flow: row;
flex-wrap: wrap;
}
.displayflex {
position: relative;
background: white;
flex-flow: column;
width: 30%;
margin: 10px;
}
.overlay {
width: 100%;
height: 100%;
opacity: 0;
position: absolute;
z-index: 20;
cursor: pointer;
color: black;
font-size: large;
background: rgba(0, 0, 0, 0.5); /* Black see-through */
}
.mat-card:hover .overlay {
opacity:0.8;
}
.myButton {
float: right;
background-color: aqua;
}
@@ -0,0 +1,38 @@
<h1>Person List</h1>
<button mat-button class="myButton" (click)="newPerson()">New</button>
<mat-form-field>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter">
</mat-form-field>
<div class="containerflex" *ngIf="(persons$ | async) as persons">
<div class="displayflex" *ngFor="let person of persons; let idx = index">
<mat-card fxLayout="column" fxLayoutAlign="center center" >
<div class="overlay" >
<button mat-button class="myButton" (click)="editPerson(person.id)">edit</button>
</div>
<mat-form-field>
<input type="text" matInput placeholder="Reference No" [value]="person?.referenceNo">
</mat-form-field>
<mat-form-field>
<input type="text" matInput placeholder="First Name" [value]="person.firstName">
</mat-form-field>
<mat-form-field>
<input type="text" matInput placeholder="Surname Name" [value]="person.surname">
</mat-form-field>
</mat-card>
</div>
</div>
<!--<div fxFlex="33%" fxLayoutAlign="none stretch">
<mat-card fxLayout="column" fxFill>
<mat-form-field>
<input type="text" matInput placeholder="Reference No" [ngModel]="person.referenceNo">
</mat-form-field>
<mat-form-field>
<input type="text" matInput placeholder="First Name" [value]="person.firstName">
</mat-form-field>
<mat-form-field>
<input type="text" matInput placeholder="Last Name" [value]="person.lastName">
</mat-form-field>
</mat-card>
</div>-->
@@ -0,0 +1,71 @@
import { Component, Inject, OnInit, AfterViewInit , ViewChild} from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { HttpClient } from '@angular/common/http';
import { PersonService } from './person.service';
import { Person } from '../models';
import { Router } from '@angular/router';
/* NgRx */
import { Store, select } from '@ngrx/store';
import * as fromSelector from './store/selectors/person.selector';
import * as fromAppState from './store/reducers/person.app.state';
import * as fromActions from './store/actions/person.action';
import { Observable } from 'rxjs';
@Component({
selector: 'person-list-data',
templateUrl: './person.list.component.html',
styleUrls: ['./person.list.component.css']
})
export class PersonListComponent implements OnInit, AfterViewInit{
private _baseUrl: string;
private _show: boolean;
// persons: any[];
_currentPos: number;
persons$!: Observable<Person[]> | null;
constructor(private http: HttpClient,
private router: Router,
private store: Store<fromAppState.AppState>,
private personService: PersonService,
@Inject('BASE_URL') baseUrl: string) {
this._baseUrl = baseUrl;
this._show = false;
this._currentPos = -1;
}
ngAfterViewInit(): void {
}
ngOnInit(): void
{
this.store.dispatch(fromActions.LoadPersons({ pageSize: 10, currentPage: 1 }));
this.persons$ = this.store.pipe(select(fromSelector.getAllPerson));
}
editPerson(id: number) {
console.log("edit person call " + id.toString());
this.router.navigate(['/person/' + id.toString()], { queryParams: { returnUrl: '/person' } } );
}
newPerson(): void {
this.router.navigate(['/person/new'], { queryParams: { returnUrl: '/person' } });
}
toogle(idx: number) {
this._show = !this._show;
this._currentPos = idx;
}
applyFilter($event:any) {
const filterValue = $event.target.value;
}
}
+79
View File
@@ -0,0 +1,79 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import { AppOverlayModule } from '../overlay/overlay.module';
import { PersonComponent } from './person.component';
import { PersonDetailEditComponent } from './person.detail.edit.component';
import { PersonEditComponent } from './person.edit.component';
import { PersonListComponent } from './person.list.component';
import { PersonService } from './person.service';
import { ShareLookUpModule } from '../share/share.lookup.module';
import { AppMaterialModule } from '../material/app.material.module';
import { CommonModule } from '@angular/common';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule, Routes } from '@angular/router';
/*NgRx*/
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { reducers } from './store/reducers';
import { effects } from './store/effects';
import { JwtInterceptor } from '../helpers';
import { AuthGuard } from '../helpers';
const personRoutes: Routes = [
{ path: '', component: PersonListComponent, canActivate: [AuthGuard] },
{
path: 'new', component: PersonEditComponent, canActivate: [AuthGuard] ,
data: {
action: 1
}
},
{ path: ':id', component: PersonEditComponent, canActivate: [AuthGuard] },
];
@NgModule({
declarations: [
PersonDetailEditComponent,
PersonComponent,
PersonListComponent,
PersonEditComponent,
],
exports: [
PersonDetailEditComponent,
PersonComponent,
PersonListComponent,
PersonEditComponent,
],
imports: [
CommonModule,
HttpClientModule,
ReactiveFormsModule,
FormsModule,
AppOverlayModule,
AppMaterialModule,
ShareLookUpModule,
RouterModule.forChild(personRoutes),
StoreModule.forFeature('persons', reducers),
EffectsModule.forFeature(effects),
],
providers: [
PersonService,
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
],
})
export class PersonModule { }
+71
View File
@@ -0,0 +1,71 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Person, PersonContainer, LookupCode, PersonLookup } from '../models';
import { Observable } from 'rxjs';
@Injectable()
export class PersonService {
private _baseUrl: string;
constructor(private http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
this._baseUrl = baseUrl;
}
savePerson(person: PersonContainer): Observable<number> {
let headers = new HttpHeaders().set('Content-Type', 'application/json');
//.set('authorization', 'Bearer ' + token);
let options = {
headers: headers
};
console.log('save Person in savePersons serivice', person);
return this.http.post<number>(this._baseUrl + 'api/Person/SavePerson', person, options);
}
deletePerson(personId: number): Observable<any> {
let headers = new HttpHeaders().set('Content-Type', 'application/json');
//.set('authorization', 'Bearer ' + token);
let options = {
headers: headers
};
console.log('deletePersons serivice', personId);
return this.http.post<any>(this._baseUrl + 'api/Person/DeletePerson', personId, options);
}
loadPersonById(id: number): Observable<PersonContainer> {
console.log('get Person in loadPersonById serivice');
let params = new HttpParams().set("personId", "" + id);
let headers = new HttpHeaders().set('Content-Type', 'application/json');
//.set('authorization', 'Bearer ' + token);
let options = {
headers: headers,
params: params
};
return this.http.get<PersonContainer>(this._baseUrl + 'api/Person/LoadPersonById', options);
}
loadPersonPickList(): Observable<PersonLookup> {
console.log('get loadPersonPickList serivice');
return this.http.get<PersonLookup>(this._baseUrl + 'api/Person/LoadPersonPickList');
}
loadPersons(pageSize: number, pageIndex: number ): Observable<Person[]>
{
console.log('get Person in loadPersons serivice');
const params = new HttpParams().set("pageSize", "" + pageSize)
.set("currentPage", "" + pageIndex);
// .set("searchText", searchText)
const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = {
headers: headers,
params: params
};
return this.http.get<Person[]>(this._baseUrl + 'api/Person/GetPersons', options);
}
loadCategory(): Observable<LookupCode[]> {
console.log('get Person in loadCategory serivice');
return this.http.get<LookupCode[]>(this._baseUrl + 'api/Person/LoadCategory');
}
}
@@ -0,0 +1,92 @@
// Section 1
import { Action, createAction, props } from '@ngrx/store'
import { Person, PersonContainer } from '../../../models';
// Section 2
export enum ActionTypes {
LOAD_PERSONS = '[PERSON] Loading Persons',
LOAD_PERSONS_FAIL = '[PERSON] Load Persons Fail',
LOAD_PERSONS_SUCCESS = '[PERSON] Load Persons Success',
LOAD_PERSON_BY_ID = '[PERSON] Loading Person By Id',
LOAD_PERSON_BY_ID_FAIL = '[PERSON] Load Person By Id Fail',
LOAD_PERSON_BY_ID_SUCCESS = '[PERSON] Load Person By Id Success',
SAVE_PERSON = '[PERSON] Starting Save',
SAVE_PERSON_FAIL = '[PERSON] Save Fail',
SAVE_PERSON_SUCCESS = '[PERSON] Save Success',
DELETE_PERSON = '[PERSON] Delete',
DELETE_PERSON_FAIL = '[PERSON] Delete Fail',
DELETE_PERSON_SUCCESS = '[PERSON] Delete Success'
}
export const LoadPersonById = createAction(
ActionTypes.LOAD_PERSON_BY_ID,
props<{ id:number }>()
);
export const LoadPersonByIdSuccess = createAction(
ActionTypes.LOAD_PERSON_BY_ID_SUCCESS,
props<{ personContainer: PersonContainer }>()
);
export const LoadPersonByIdFail = createAction(
ActionTypes.LOAD_PERSON_BY_ID_FAIL,
props<{msg: any}>()
);
// Section 3
export const SavePerson = createAction (
ActionTypes.SAVE_PERSON,
props< {personContainer: PersonContainer}> ()
);
export const SavePersonFail = createAction (
ActionTypes.SAVE_PERSON_FAIL,
props<{msg: any}>()
);
export const SavePersonSuccess = createAction (
ActionTypes.SAVE_PERSON_SUCCESS,
props< {person: Person , message: string}>()
);
export const DeletePerson = createAction (
ActionTypes.DELETE_PERSON,
props<{id: number}>()
);
export const DeletePersonSuccess = createAction (
ActionTypes.DELETE_PERSON_SUCCESS,
props<{ id: number, message: string}> ()
);
export const DeletePersonFail = createAction (
ActionTypes.DELETE_PERSON_FAIL,
props<{msg: any}>()
);
export const LoadPersons = createAction(
ActionTypes.LOAD_PERSONS,
props<{pageSize:number, currentPage: number}>()
);
// Succes
export const LoadPersonsSuccess = createAction (
ActionTypes.LOAD_PERSONS_SUCCESS,
props<{persons: Person[]}>()
);
// Fail
export const LoadPersonsFail = createAction (
ActionTypes.LOAD_PERSONS_FAIL,
props<{msg: any}>()
);
+5
View File
@@ -0,0 +1,5 @@
import { PersonEffects } from './person.effect';
export const effects: any[] = [PersonEffects];
export * from './person.effect';
@@ -0,0 +1,83 @@
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType, createEffect } from '@ngrx/effects';
import { map, switchMap, mergeMap, catchError } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import * as fromActions from '../actions/person.action';
import { PersonService } from '../../person.service';
@Injectable()
export class PersonEffects {
constructor(
private actions$: Actions,
private personService: PersonService
) { }
loadPersonById$ = createEffect(() => this.actions$.pipe(
ofType(fromActions.LoadPersonById),
switchMap((action) => {
return this.personService.loadPersonById(action.id).pipe(
map(person => fromActions.LoadPersonByIdSuccess({ personContainer: person })),
catchError(err => observableOf(fromActions.LoadPersonByIdFail({ msg: err })))
)
})
)
);
loadAllPerson$ = createEffect(() => this.actions$.pipe(
ofType(fromActions.LoadPersons),
switchMap((action) => {
return this.personService.loadPersons(action.pageSize, action.currentPage).pipe(
map(Persons => fromActions.LoadPersonsSuccess({ persons: Persons })),
catchError(err => observableOf(fromActions.LoadPersonsFail({ msg: err })))
)
})
)
);
savePerson$ = createEffect(() => this.actions$.pipe(
ofType(fromActions.SavePerson),
mergeMap(action => {
return this.personService.savePerson(action.personContainer)
.pipe(
map(robj => {
let rperson = { ...action.personContainer.person, id: robj };
rperson.id = robj;
console.log("success effect save person", rperson);
return fromActions.SavePersonSuccess({ person: rperson, message: "" });
}),
catchError(error => observableOf(fromActions.SavePersonFail({ msg: error })))
); //end pipe
})
)
);
deletePerson$ = createEffect(() => this.actions$.pipe(
ofType(fromActions.DeletePerson),
mergeMap(action => {
return this.personService
.deletePerson(action.id)
.pipe(
map(robj => fromActions.DeletePersonSuccess({id:robj, message:""})),
catchError(error => observableOf(fromActions.DeletePersonFail({msg:error})))
);
})
)
);
/*
@Effect()
searchArticleById$: Observable<Action> = this.actions$
pipe( ofType<fromActions.GetByIdAction>(fromActions.GET_BY_ID),
debounceTime(500),
map(action => action.payload),
switchMap(id =>
this.hazardService.getArticleById(id)
.map(res => new fromActions.GetByIdSuccessAction(res))
);
)
*/
}
+27
View File
@@ -0,0 +1,27 @@
import * as fromPerson from './person.reducer';
import { ActionReducerMap, createFeatureSelector, ActionReducer, MetaReducer } from '@ngrx/store';
import { appPersonState, AppState } from './person.app.state';
import * as fromAppReducer from '../../../state/app.reducers';
import { environment } from '../../../../environments/environment';
export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
return function (state, action): AppState {
console.log('state', state);
console.log('action', action);
return reducer(state, action);
};
}
export const metaReducers: MetaReducer<AppState>[] = !environment.production
? [logger]
: [];
export const reducers: ActionReducerMap<AppState> = {
persons: fromPerson.reducer,
currentUser: fromAppReducer.PersonReducer,
}
export * from './person.app.state';
export const getPersonState = createFeatureSelector<AppState>(
'persons'
);
@@ -0,0 +1,16 @@
import { Person, PersonContainer } from '../../../models';
import * as fromAppState from '../../../state/app.state'
export interface AppState extends fromAppState.AppState {
persons: appPersonState;
}
//status_code = -1 error
//status_code = 1 sucess
export interface appPersonState {
entities: { [id: number]: Person };
editPerson: PersonContainer | null;
loaded: boolean;
loading: boolean;
statusCode: number;
omessage: string;
}
@@ -0,0 +1,169 @@
import { Action, createReducer, on } from '@ngrx/store'
import { Person } from '../../../models';
import * as PersonActions from './../actions/person.action'
import { appPersonState } from './person.app.state';
// status_code
export const initialState: appPersonState = { entities: {}, editPerson: null, loaded: false, loading: false, omessage: "init", statusCode: 0};
// Section 2
const PersonReducer = createReducer(
initialState,
on(PersonActions.SavePersonSuccess, (state, action) =>
{
const pid = action.person.id;
console.log("The payload is SAVE_PERSON_SUCCESS ", pid);
const entities = {
...state.entities,
[pid]: action.person,
};
return {
...state,
entities,
editPerson: null,
statusCode: 1 //this is redirect to list component.
};
}),
on(PersonActions.SavePersonFail, (state, { msg }) =>
{
console.log("the payload is SAVE_PERSON_FAIL ", msg);
return {
...state,
loading: false,
loaded: false,
omessage: "ERROR " + msg,
statusCode: -1
};
}),
on(PersonActions.LoadPersonByIdSuccess, (state, action) => {
return {
...state,
editPerson: action.personContainer,
omessage: '',
statusCode: 0
};
}
),
on(PersonActions.LoadPersonByIdFail, (state, action) => {
return {
...state,
editPerson: null,
omessage: action.msg,
statusCode: 0
};
}),
on(PersonActions.LoadPersonByIdFail, (state, { msg }) =>
{
console.log("the load person by id fail ", msg);
return {
...state,
loading: false,
loaded: false,
omessage: "ERROR " + msg,
editPerson: null,
statusCode: -1
};
}),
on (PersonActions.LoadPersons, (state) =>
{
console.log("the load persons person.reducers");
return {
...state,
loading: true,
editPerson:null,
statusCode: 0
};
}),
on(PersonActions.LoadPersonsSuccess, (state, { persons } ) =>
{
const entities = persons.reduce(
(entities: { [id: number]: Person }, person: Person) => {
return {
...entities,
[person.id]: person,
};
},
{
...state.entities,
}
);
console.log('reducer PERSON load LOAD_PERSON_SUCCESS', persons);
return {
...state,
entities,
omessage: "",
loading: false,
loaded: true,
editPerson: null,
statusCode: 0,
};
}
),
on(PersonActions.LoadPersonsFail, (state, { msg }) =>
{
return {
...state,
loading: false,
loaded: false,
editPerson: null,
omessage: "ERROR " + msg,
statusCode: -1
};
}
),
on (PersonActions.DeletePerson, state =>
{
console.log('reducer Delete Person starting');
return {
...state,
omessage:""
};
} ),
on (PersonActions.DeletePersonSuccess , (state, action) =>
{
const pid = action.id;
const { [pid]: removed, ...entities } = state.entities;
return {
...state,
entities,
omessage: "Success",
statusCode: 1
};
}
),
on(PersonActions.DeletePersonFail, (state, { msg }) =>
{
return {
...state,
omessage: "ERROR " + msg,
statusCode: -1
};
}
)
);
export function reducer(state: appPersonState | undefined, action: Action)
{
return PersonReducer(state, action);
}
export const getPersonEntities = (state: appPersonState) => state.entities;
export const getPersonLoading = (state: appPersonState) => state.loading;
export const getPersonLoaded = (state: appPersonState) => state.loaded;
export const getPersonStatusCode = (state: appPersonState) => state.statusCode;
export const getPersonMessage = (state: appPersonState) => state.omessage;
export const getPersonForEdit = (state: appPersonState) => state.editPerson;

Some files were not shown because too many files have changed in this diff Show More