fixed the program.cs and run in VC code

This commit is contained in:
2026-07-30 17:23:48 +10:00
parent ce2b3ba8c9
commit 5d2e989ee9
112 changed files with 17313 additions and 17245 deletions
+75 -75
View File
@@ -1,75 +1,75 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
myZip/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Compiled output
dist/
bin/
obj/
tmp/
**/out-tsc
**/bazel-out
# Node
**/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
.vs/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
**/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
/API/FamilyTreeAPI/uploads
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
myZip/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Compiled output
dist/
bin/
obj/
tmp/
**/out-tsc
**/bazel-out
# Node
**/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
.vs/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
**/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
/API/FamilyTreeAPI/uploads
+24
View File
@@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C#: FamilyTreeAPI",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/FamilyTreeAPI/bin/Debug/net10.0/FamilyTreeAPI",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"serverReadyAction": {
"action": "openUrl",
"pattern": "Now listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html"
}
}
]
}
+2 -2
View File
@@ -3,7 +3,7 @@
# This stage is used when running from VS in fast mode (Default for Debug configuration)
#FROM mcr.microsoft.com/dotnet/aspnet:9::.0 AS base
ARG BUILD_CONFIGURATION=Release
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"]
RUN dotnet restore "./FamilyTreeAPI/FamilyTreeAPI.csproj"
@@ -31,7 +31,7 @@ FROM build AS publish
RUN dotnet publish "./FamilyTreeAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
ENV ASPNETCORE_HTTP_PORTS=8080
EXPOSE 8080
+9 -2
View File
@@ -7,10 +7,9 @@
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<PublishSingleFile>true</PublishSingleFile>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="16.0.0" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="16.0.0" />
@@ -31,4 +30,12 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
+24 -3
View File
@@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using DocumentFormat.OpenXml.Office2010.Drawing.Charts;
using FamilyTreeAPI.Authorization;
using FamilyTreeAPI.Entities;
using FamilyTreeAPI.GraphQL.Query;
@@ -14,6 +13,12 @@ using FamilyTreeAPI.Repository;
var builder = WebApplication.CreateBuilder(args);
// Explicitly register the base paths for Linux container execution
builder.Configuration
.SetBasePath(AppContext.BaseDirectory) // <-- Pulls from wherever the binary sits
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
// Add services to the container.
#region Services
var services = builder.Services;
@@ -23,8 +28,10 @@ services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
string skey = "Super Secret SIGN AND VERIFY JWT TOKENS";
var appSettingsSection = builder.Configuration.GetSection("AppSettings");
if (appSettingsSection != null)
if (appSettingsSection.Exists())
{
services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
@@ -32,10 +39,13 @@ if (appSettingsSection != null)
skey = appSettings.Secret;
else
{
skey = builder.Configuration.GetValue<string>("AppSettings:Secret");
string tkey = builder.Configuration.GetValue<string>("AppSettings:Secret") ?? string.Empty;
if (!string.IsNullOrEmpty(tkey))
skey = tkey;
}
}
var key = Encoding.ASCII.GetBytes(skey);
services.AddAuthentication(x =>
@@ -174,6 +184,17 @@ app.Run();
#endregion
/*
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
https://www.youtube.com/watch?v=WQFx2m5Ub9M
@@ -8,35 +8,11 @@
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5016",
"applicationUrl1": "http://192.168.8.188:5015"
"applicationUrl": "http://localhost:5016"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": false
}
},
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:4559",
"sslPort": 0
}
}
}
+6 -4
View File
@@ -2,11 +2,11 @@
"AppSettings": {
"Secret": "Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.",
"SQLConnectionString_i": "host=localhost;port=5432;database=FamilyTreeDB_prod;username=postgres;password=Positive~1;",
"iSQLConnectionString_i": "host=localhost;port=5432;database=FamilyTreeDB_prod;username=postgres;password=Positive~1;",
"SQLConnectionString": "host=192.168.1.240;port=5432;database=FamilyTreeDB_prod;username=postgres;password=Positive~1;",
"ImageFolder": "c:\\temp\\Family",
"ImportFolder": "c:\\temp"
"ImageFolder": "/temp/Family",
"ImportFolder": "/temp"
},
"Logging": {
@@ -17,11 +17,13 @@
}
},
"AllowedHosts": "*",
"Kestrel_not_use": {
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5015"
}
}
}
}
+17 -17
View File
@@ -1,17 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false
+43 -43
View File
@@ -1,43 +1,43 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
__screenshots__/
# System files
.DS_Store
Thumbs.db
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
__screenshots__/
# System files
.DS_Store
Thumbs.db
+4 -4
View File
@@ -1,5 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
{
"plugins": {
"@tailwindcss/postcss": {}
}
}
+4 -4
View File
@@ -1,4 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}
+20 -20
View File
@@ -1,20 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}
+42 -42
View File
@@ -1,42 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}
+31 -31
View File
@@ -1,32 +1,32 @@
FROM node:24.0.0 AS build
RUN mkdir -p /app
WORKDIR /app
COPY ["./*.json", "./"]
RUN npm install -g @angular/cli
COPY . .
# Run command in Virtual directory
RUN npm cache clean --force
RUN npm install
#RUN ng build --configuration=production
RUN ng build -c=production
FROM nginx:latest
#### copy nginx conf
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
#COPY --from=build app/dist/Familytree/browser /usr/share/nginx/html
#### copy artifact build from the 'build environment' old is not in browser folder
COPY --from=build /app/dist/Familytree/browser /usr/share/nginx/html
#CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
#docker build -t my_angular_app:latest .
#docker run --name carval -d -p 4200:80 varlidate_ui
FROM node:24.0.0 AS build
RUN mkdir -p /app
WORKDIR /app
COPY ["./*.json", "./"]
RUN npm install -g @angular/cli
COPY . .
# Run command in Virtual directory
RUN npm cache clean --force
RUN npm install
#RUN ng build --configuration=production
RUN ng build -c=production
FROM nginx:latest
#### copy nginx conf
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
#COPY --from=build app/dist/Familytree/browser /usr/share/nginx/html
#### copy artifact build from the 'build environment' old is not in browser folder
COPY --from=build /app/dist/Familytree/browser /usr/share/nginx/html
#CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
#docker build -t my_angular_app:latest .
#docker run --name carval -d -p 4200:80 varlidate_ui
#npm error code ENOENT
+59 -59
View File
@@ -1,59 +1,59 @@
# FamilyTreeUI
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.2.
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
# FamilyTreeUI
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.2.
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
+78 -78
View File
@@ -1,78 +1,78 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"packageManager": "npm",
"analytics": false
},
"newProjectRoot": "projects",
"projects": {
"FamilyTreeUI": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"allowedCommonJsDependencies": [
"moment", "file-saver", "xlsx"
],
"browser": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "5MB",
"maximumError": "6MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "FamilyTreeUI:build:production"
},
"development": {
"buildTarget": "FamilyTreeUI:build:development"
}
},
"defaultConfiguration": "development"
},
"test": {
"builder": "@angular/build:unit-test"
}
}
}
}
}
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"packageManager": "npm",
"analytics": false
},
"newProjectRoot": "projects",
"projects": {
"FamilyTreeUI": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"allowedCommonJsDependencies": [
"moment", "file-saver", "xlsx"
],
"browser": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "5MB",
"maximumError": "6MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "FamilyTreeUI:build:production"
},
"development": {
"buildTarget": "FamilyTreeUI:build:development"
}
},
"defaultConfiguration": "development"
},
"test": {
"builder": "@angular/build:unit-test"
}
}
}
}
}
+19 -19
View File
@@ -1,20 +1,20 @@
server {
listen 80;
sendfile on;
default_type application/octet-stream;
gzip on;
gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\.";
gzip_min_length 256;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 9;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html =404;
}
server {
listen 80;
sendfile on;
default_type application/octet-stream;
gzip on;
gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\.";
gzip_min_length 256;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 9;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html =404;
}
}
+9484 -9484
View File
File diff suppressed because it is too large Load Diff
+56 -56
View File
@@ -1,57 +1,57 @@
{
"name": "family-tree-ui",
"version": "1.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
},
"private": true,
"packageManager": "npm@11.6.4",
"dependencies": {
"@angular/common": "^21.0.0",
"@angular/compiler": "^21.0.0",
"@angular/core": "^21.0.0",
"@angular/forms": "^21.0.0",
"@angular/platform-browser": "^21.0.0",
"@angular/router": "^21.0.0",
"@ngrx/effects": "^21.0.1",
"@ngrx/store": "^21.0.1",
"@ngrx/store-devtools": "^21.0.1",
"@primeuix/themes": "^2.0.2",
"@tailwindcss/postcss": "^4.1.17",
"file-saver": "^2.0.5",
"moment": "^2.30.1",
"postcss": "^8.5.6",
"primeicons": "^7.0.0",
"primeng": "^21.0.1",
"rxjs": "~7.8.0",
"tailwindcss": "^4.1.17",
"tailwindcss-primeui": "^0.6.1",
"tslib": "^2.3.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@angular/build": "^21.2.7",
"@angular/cli": "^21.2.7",
"@angular/compiler-cli": "^21.0.0",
"@types/file-saver": "^2.0.7",
"jsdom": "^27.1.0",
"typescript": "~5.9.2",
"vitest": "^4.0.8"
}
{
"name": "family-tree-ui",
"version": "1.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
},
"private": true,
"packageManager": "npm@11.6.4",
"dependencies": {
"@angular/common": "^21.0.0",
"@angular/compiler": "^21.0.0",
"@angular/core": "^21.0.0",
"@angular/forms": "^21.0.0",
"@angular/platform-browser": "^21.0.0",
"@angular/router": "^21.0.0",
"@ngrx/effects": "^21.0.1",
"@ngrx/store": "^21.0.1",
"@ngrx/store-devtools": "^21.0.1",
"@primeuix/themes": "^2.0.2",
"@tailwindcss/postcss": "^4.1.17",
"file-saver": "^2.0.5",
"moment": "^2.30.1",
"postcss": "^8.5.6",
"primeicons": "^7.0.0",
"primeng": "^21.0.1",
"rxjs": "~7.8.0",
"tailwindcss": "^4.1.17",
"tailwindcss-primeui": "^0.6.1",
"tslib": "^2.3.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@angular/build": "^21.2.7",
"@angular/cli": "^21.2.7",
"@angular/compiler-cli": "^21.0.0",
"@types/file-saver": "^2.0.7",
"jsdom": "^27.1.0",
"typescript": "~5.9.2",
"vitest": "^4.0.8"
}
}
+7 -7
View File
@@ -1,8 +1,8 @@
{
"baseUrl1": "http://192.168.8.188:5015",
"baseUrl": "http://localhost:5016",
"baseUrl_docker": "http://localhost:8080",
"attachment": "http://localhost/document/family",
"username": "kham.vilaythong@gmail.com",
"prefill_ps": "passwor"
{
"baseUrl1": "http://192.168.8.188:5015",
"baseUrl": "http://localhost:5016",
"baseUrl_docker": "http://localhost:8080",
"attachment": "http://localhost/document/family",
"username": "kham.vilaythong@gmail.com",
"prefill_ps": "passwor"
}
+18 -18
View File
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="./index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="./index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
+66 -66
View File
@@ -1,67 +1,67 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners ,
provideAppInitializer} from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { providePrimeNG } from 'primeng/config';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ConfirmationService, MessageService } from 'primeng/api';
import { routes } from './app.routes';
import MyPreset from './mythem';
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
import { provideStore } from '@ngrx/store';
import { provideStoreDevtools } from '@ngrx/store-devtools';
import { isDevMode } from '@angular/core';
import { provideEffects } from '@ngrx/effects';
import { staffEffects,staffsReducer } from './state';
export const appConfig: ApplicationConfig = {
providers: [
provideStore({
myStaffs: staffsReducer,
}),
provideEffects([staffEffects]),
provideStoreDevtools({
maxAge: 30,
logOnly: !isDevMode()
}),
provideBrowserGlobalErrorListeners(),
MessageService, ConfirmationService,
provideAppInitializer(initializeApp()),
provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
provideRouter(routes, withComponentInputBinding()),
providePrimeNG({
theme: {
preset: MyPreset,
options: {
cssLayer: {
name: 'primeng',
order: 'theme, base, primeng'
}
}
}
}),
]
};
/*
$) ng build --base-href "/familytreeui/" -c production
*/
/*
import { Store } from '@ngrx/store';
private readonly store = inject(Store);
protected books = this.store.selectSignal(selectBooks);
protected onAdd(id: number) {
this.store.dispatch(StaffsActions.addStaff({ id }));
}
protected onRemove(id: number) {
this.store.dispatch(StaffsActions.removeStaff({ id }));
}
on ngOnInit() {
use it normal get from API first
and subscribe( (x) => this.store.dispatch(StaffApiAction.LoadStaffList({x})))
}
import { ApplicationConfig, provideBrowserGlobalErrorListeners ,
provideAppInitializer} from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { providePrimeNG } from 'primeng/config';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ConfirmationService, MessageService } from 'primeng/api';
import { routes } from './app.routes';
import MyPreset from './mythem';
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
import { provideStore } from '@ngrx/store';
import { provideStoreDevtools } from '@ngrx/store-devtools';
import { isDevMode } from '@angular/core';
import { provideEffects } from '@ngrx/effects';
import { staffEffects,staffsReducer } from './state';
export const appConfig: ApplicationConfig = {
providers: [
provideStore({
myStaffs: staffsReducer,
}),
provideEffects([staffEffects]),
provideStoreDevtools({
maxAge: 30,
logOnly: !isDevMode()
}),
provideBrowserGlobalErrorListeners(),
MessageService, ConfirmationService,
provideAppInitializer(initializeApp()),
provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
provideRouter(routes, withComponentInputBinding()),
providePrimeNG({
theme: {
preset: MyPreset,
options: {
cssLayer: {
name: 'primeng',
order: 'theme, base, primeng'
}
}
}
}),
]
};
/*
$) ng build --base-href "/familytreeui/" -c production
*/
/*
import { Store } from '@ngrx/store';
private readonly store = inject(Store);
protected books = this.store.selectSignal(selectBooks);
protected onAdd(id: number) {
this.store.dispatch(StaffsActions.addStaff({ id }));
}
protected onRemove(id: number) {
this.store.dispatch(StaffsActions.removeStaff({ id }));
}
on ngOnInit() {
use it normal get from API first
and subscribe( (x) => this.store.dispatch(StaffApiAction.LoadStaffList({x})))
}
*/
+4 -4
View File
@@ -1,5 +1,5 @@
<p-toast position="bottom-center" />
<app-toolbar> </app-toolbar>
<router-outlet />
<p-confirmDialog [style]="{width: '50vw'}"
<p-toast position="bottom-center" />
<app-toolbar> </app-toolbar>
<router-outlet />
<p-confirmDialog [style]="{width: '50vw'}"
rejectButtonStyleClass="p-button-text"> </p-confirmDialog>
+19 -19
View File
@@ -1,19 +1,19 @@
import { Routes } from '@angular/router';
import { Login } from './login';
import { StaffComponent, StaffEditComponent } from './staff';
import { AuthGuard } from './route-guard';
import { FamilyTree, FamilyList} from './person';
import { ImportCom } from './import.com/import.com';
import { ImageDisplayComponent } from './pickperson/image.display';
export const routes: Routes = [
{ path: '', redirectTo: 'login', pathMatch: 'full' },
// { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},},
{ path: 'login', component: Login},
{ path: 'staff', component: StaffComponent},
{ path: 'person', component: FamilyList},
{ path: 'imagedisplay/:id', component: ImageDisplayComponent},
{ path: 'import', component: ImportCom},
{ path: 'familytree', component: FamilyTree},
{ path: 'staff/:id', component: StaffEditComponent},
];
import { Routes } from '@angular/router';
import { Login } from './login';
import { StaffComponent, StaffEditComponent } from './staff';
import { AuthGuard } from './route-guard';
import { FamilyTree, FamilyList} from './person';
import { ImportCom } from './import.com/import.com';
import { ImageDisplayComponent } from './pickperson/image.display';
export const routes: Routes = [
{ path: '', redirectTo: 'login', pathMatch: 'full' },
// { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},},
{ path: 'login', component: Login},
{ path: 'staff', component: StaffComponent},
{ path: 'person', component: FamilyList},
{ path: 'imagedisplay/:id', component: ImageDisplayComponent},
{ path: 'import', component: ImportCom},
{ path: 'familytree', component: FamilyTree},
{ path: 'staff/:id', component: StaffEditComponent},
];
+15 -15
View File
@@ -1,15 +1,15 @@
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ToolbarComponent } from './toolbar/toolbar.component';
import { ToastModule } from 'primeng/toast';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
@Component({
selector: 'app-root',
imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
protected readonly title = signal('FamilyTree');
}
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ToolbarComponent } from './toolbar/toolbar.component';
import { ToastModule } from 'primeng/toast';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
@Component({
selector: 'app-root',
imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
protected readonly title = signal('FamilyTree');
}
+20 -20
View File
@@ -1,20 +1,20 @@
<div class="card gap-2">
<div class="mt-4">
<input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload multiple>
<div class="file-upload">
<label>{{fileName || "Photo upload:"}}</label>
<button pButton type="button" icon="pi pi-paperclip"
class="p-button-rounded p-button-text p-button-raised ml-2" pTooltip="attach file"
(click)="fileUpload.click()"></button>
</div>
<div class="flex items-end justify-end mt-4">
@if (files)
{
<button pButton pRipple type="button" icon="pi pi-upload" label="OK" class="p-button-sm mr-2"
(click)="upload($event)"></button>
}
<button pButton pRipple type="button" icon="pi pi-times" label="Cancel" class="p-button-sm"
(click)="cancel($event)"></button>
</div>
</div>
</div>
<div class="card gap-2">
<div class="mt-4">
<input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload multiple>
<div class="file-upload">
<label>{{fileName || "Photo upload:"}}</label>
<button pButton type="button" icon="pi pi-paperclip"
class="p-button-rounded p-button-text p-button-raised ml-2" pTooltip="attach file"
(click)="fileUpload.click()"></button>
</div>
<div class="flex items-end justify-end mt-4">
@if (files)
{
<button pButton pRipple type="button" icon="pi pi-upload" label="OK" class="p-button-sm mr-2"
(click)="upload($event)"></button>
}
<button pButton pRipple type="button" icon="pi pi-times" label="Cancel" class="p-button-sm"
(click)="cancel($event)"></button>
</div>
</div>
</div>
+178 -178
View File
@@ -1,178 +1,178 @@
import { ChangeDetectorRef, Component, inject, Inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DynamicDialogModule, DynamicDialogRef ,DynamicDialogConfig} from 'primeng/dynamicdialog';
import { LookupEdit } from '../models';
import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { Subject, Subscription } from 'rxjs';
import { ConfirmationService, MessageService } from 'primeng/api';
import { LookupService } from '../shares';
import { CommonModule } from '@angular/common';
import { CheckboxModule } from 'primeng/checkbox';
import { InputTextModule } from 'primeng/inputtext';
import { ButtonModule } from 'primeng/button';
@Component({
selector: 'add-photo',
imports: [CommonModule, ReactiveFormsModule,
ButtonModule, InputTextModule,
DynamicDialogModule, CheckboxModule],
templateUrl: './add.photo.html',
styleUrls: ['./add.photo.css'],
providers: [ConfirmationService]
})
export class AddPhoto implements OnInit, OnDestroy {
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
selType = "";
_error = "";
downloadFile = signal(false);
files: File[] = [];
fileName = "";
disabled: boolean = true;
subChanged$ = new Subject<boolean>();
private formBuilder = inject(UntypedFormBuilder);
private cdr = inject(ChangeDetectorRef);
private subscription: Subscription = new Subscription();
lookupForm = this.formBuilder.group({
id: [0], //Validators.required
description: ['', [Validators.required, Validators.maxLength(50)]],
codeId: ['', [Validators.required]],
parentId: [0],
active: [false], //Validators.required
});
constructor(
private lookupService: LookupService,
private messageService: MessageService,
private confirmationService: ConfirmationService,
public ref: DynamicDialogRef, public config: DynamicDialogConfig) { }
ngOnInit(): void {
const id = this.config.data.id;
this.selType = this.config.data.type;
const maxcodeId = this.config.data.maxCodeId;
if (id > 0) {
this.lookupService.loadLookupById(id, this.selType).subscribe({
next: x => {
this.assignValue(x.data);
this.cdr.markForCheck();
this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
},
error: e => {
console.error("error", e);
this.messageService.add({ severity: 'error', summary: 'Error on load cleaner ', detail: e.message });
}
});
}
else {
//new photo
const ward:LookupEdit = {
id, description: '', codeId: maxcodeId, type: this.selType, active: true,
};
this.assignValue(ward);
this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}
}
getClassForRequire(prev: string, name: string) {
const notok = !this.lookupForm.controls[name].valid &&
this.lookupForm.controls[name].touched;
let str = prev;
if (notok)
str += " ng-invalid ng-dirty";
return str;
}
get isFieldsChange() {
const chan = this.isChange || !this.lookupForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
assignValue(item: LookupEdit): void {
this.lookupForm.patchValue({
id: item.id,
codeId: item.codeId,
description: item.description,
active: item.active,
});
// disable use false//true for not disable.
this.subChanged$.next(true);
}
validate(item: LookupEdit): boolean {
let result = true;
if (item.description!.trim() == "") {
this._error = "Description is empty or blank";
result = false;
}
return result;
}
deleteFile() : void {
//const filename = this.requestForm.value.attachmentFile;
// const data = { filename };
this.fileName ="";
//this.requestForm.patchValue({ attachmentFile: "" });
/*
const delete$ = this.tradePersonService.deleteUploadFile(data);
this.subscription.add(delete$.subscribe(
{
next: x => {
if (x.statusCode == 1) {
{
this.requestForm.patchValue({ attachmentFile: "" });
}
}
else {
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message });
}
},
error: e => {
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e });
}
}
));
*/
}
onFileSelected(event: any) {
let i =0;
for (i = 0; i < event.target.files.length; i++)
{
const file: File = event.target.files[i];
this.files.push(file);
if (this.fileName != "")
this.fileName = this.fileName + "," + file.name;
else
this.fileName = file.name;
this.subChanged$.next(false);
}
}
upload(e: Event): void {
e.preventDefault();
if (this.files.length > 0)
{
this.ref.close(this.files);
}
else
{
this.messageService.add({severity: 'error', summary: 'No File to upload file', detail: 'select file first then upload file'});
}
}
cancel(e: Event): void {
e.preventDefault();
this.ref.close(null);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
import { ChangeDetectorRef, Component, inject, Inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DynamicDialogModule, DynamicDialogRef ,DynamicDialogConfig} from 'primeng/dynamicdialog';
import { LookupEdit } from '../models';
import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { Subject, Subscription } from 'rxjs';
import { ConfirmationService, MessageService } from 'primeng/api';
import { LookupService } from '../shares';
import { CommonModule } from '@angular/common';
import { CheckboxModule } from 'primeng/checkbox';
import { InputTextModule } from 'primeng/inputtext';
import { ButtonModule } from 'primeng/button';
@Component({
selector: 'add-photo',
imports: [CommonModule, ReactiveFormsModule,
ButtonModule, InputTextModule,
DynamicDialogModule, CheckboxModule],
templateUrl: './add.photo.html',
styleUrls: ['./add.photo.css'],
providers: [ConfirmationService]
})
export class AddPhoto implements OnInit, OnDestroy {
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
selType = "";
_error = "";
downloadFile = signal(false);
files: File[] = [];
fileName = "";
disabled: boolean = true;
subChanged$ = new Subject<boolean>();
private formBuilder = inject(UntypedFormBuilder);
private cdr = inject(ChangeDetectorRef);
private subscription: Subscription = new Subscription();
lookupForm = this.formBuilder.group({
id: [0], //Validators.required
description: ['', [Validators.required, Validators.maxLength(50)]],
codeId: ['', [Validators.required]],
parentId: [0],
active: [false], //Validators.required
});
constructor(
private lookupService: LookupService,
private messageService: MessageService,
private confirmationService: ConfirmationService,
public ref: DynamicDialogRef, public config: DynamicDialogConfig) { }
ngOnInit(): void {
const id = this.config.data.id;
this.selType = this.config.data.type;
const maxcodeId = this.config.data.maxCodeId;
if (id > 0) {
this.lookupService.loadLookupById(id, this.selType).subscribe({
next: x => {
this.assignValue(x.data);
this.cdr.markForCheck();
this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
},
error: e => {
console.error("error", e);
this.messageService.add({ severity: 'error', summary: 'Error on load cleaner ', detail: e.message });
}
});
}
else {
//new photo
const ward:LookupEdit = {
id, description: '', codeId: maxcodeId, type: this.selType, active: true,
};
this.assignValue(ward);
this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}
}
getClassForRequire(prev: string, name: string) {
const notok = !this.lookupForm.controls[name].valid &&
this.lookupForm.controls[name].touched;
let str = prev;
if (notok)
str += " ng-invalid ng-dirty";
return str;
}
get isFieldsChange() {
const chan = this.isChange || !this.lookupForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
assignValue(item: LookupEdit): void {
this.lookupForm.patchValue({
id: item.id,
codeId: item.codeId,
description: item.description,
active: item.active,
});
// disable use false//true for not disable.
this.subChanged$.next(true);
}
validate(item: LookupEdit): boolean {
let result = true;
if (item.description!.trim() == "") {
this._error = "Description is empty or blank";
result = false;
}
return result;
}
deleteFile() : void {
//const filename = this.requestForm.value.attachmentFile;
// const data = { filename };
this.fileName ="";
//this.requestForm.patchValue({ attachmentFile: "" });
/*
const delete$ = this.tradePersonService.deleteUploadFile(data);
this.subscription.add(delete$.subscribe(
{
next: x => {
if (x.statusCode == 1) {
{
this.requestForm.patchValue({ attachmentFile: "" });
}
}
else {
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message });
}
},
error: e => {
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e });
}
}
));
*/
}
onFileSelected(event: any) {
let i =0;
for (i = 0; i < event.target.files.length; i++)
{
const file: File = event.target.files[i];
this.files.push(file);
if (this.fileName != "")
this.fileName = this.fileName + "," + file.name;
else
this.fileName = file.name;
this.subChanged$.next(false);
}
}
upload(e: Event): void {
e.preventDefault();
if (this.files.length > 0)
{
this.ref.close(this.files);
}
else
{
this.messageService.add({severity: 'error', summary: 'No File to upload file', detail: 'select file first then upload file'});
}
}
cancel(e: Event): void {
e.preventDefault();
this.ref.close(null);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
export * from './photolist';
export * from './photolist';
export * from './add.photo';
+48 -48
View File
@@ -1,49 +1,49 @@
<div class="shadow-xl rounded-md p-2">
<div class="flex fex-row justify-between mb-2">
<p> please click save at the end</p>
<div class="flex items-end self-end">
<button pButton pRipple type="button" icon="pi pi-file" label="Attach Photo" class="p-button-sm"
(click)="newSupportDoc($event)"></button>
</div>
</div>
<div class="p-4">
<p-table [value]="FileList" sortMode="multiple" class="p-datatable-sm"
[loading]="loading">
<ng-template pTemplate="header">
<tr>
<th>Id</th>
<th pSortableColumn="description">Description<p-sortIcon field="description"></p-sortIcon>
</th>
<th>Action</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td>{{item.id}}</td>
<td >{{item.photo}}</td>
<td>
<button pButton type="button" icon="pi pi-times"
pTooltip="Delete" class="p-button-rounded text-red-500 p-button-text p-button-raised mr-2"
(click)="remove(item.id)"></button>
@if (item.id > 0) {
<button pButton type="button" icon="pi pi-file"
pTooltip="Download Photo"
class="p-button-rounded p-button-text p-button-raised"
(click)="downloadAttachment(item.id)"></button>
}
</td>
</tr>
</ng-template>
</p-table>
<div class="flex items-end justify-end mt-4">
<button pButton pRipple type="button" icon="pi pi-times" label="Close" class="p-button-sm"
(click)="onClose($event)"></button>
</div>
<div class="shadow-xl rounded-md p-2">
<div class="flex fex-row justify-between mb-2">
<p> please click save at the end</p>
<div class="flex items-end self-end">
<button pButton pRipple type="button" icon="pi pi-file" label="Attach Photo" class="p-button-sm"
(click)="newSupportDoc($event)"></button>
</div>
</div>
<div class="p-4">
<p-table [value]="FileList" sortMode="multiple" class="p-datatable-sm"
[loading]="loading">
<ng-template pTemplate="header">
<tr>
<th>Id</th>
<th pSortableColumn="description">Description<p-sortIcon field="description"></p-sortIcon>
</th>
<th>Action</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td>{{item.id}}</td>
<td >{{item.photo}}</td>
<td>
<button pButton type="button" icon="pi pi-times"
pTooltip="Delete" class="p-button-rounded text-red-500 p-button-text p-button-raised mr-2"
(click)="remove(item.id)"></button>
@if (item.id > 0) {
<button pButton type="button" icon="pi pi-file"
pTooltip="Download Photo"
class="p-button-rounded p-button-text p-button-raised"
(click)="downloadAttachment(item.id)"></button>
}
</td>
</tr>
</ng-template>
</p-table>
<div class="flex items-end justify-end mt-4">
<button pButton pRipple type="button" icon="pi pi-times" label="Close" class="p-button-sm"
(click)="onClose($event)"></button>
</div>
</div>
+183 -183
View File
@@ -1,183 +1,183 @@
//import { Component, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core';
import { catchError, EMPTY, finalize, Subscription } from 'rxjs';
import { CommonUtilities, HttpUtility, LookupService } from '../shares';
import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from 'primeng/dynamicdialog';
import { ConfirmationService, MessageService } from 'primeng/api';
import { AddPhoto } from './add.photo';
import { PersonPhotoDto } from '../models';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { TooltipModule } from 'primeng/tooltip';
import { AuthenticationService } from '../user-services';
import { HttpResponse } from '@angular/common/http';
import { PersonService } from '../person';
@Component({
selector: 'photo-list',
imports: [CommonModule, FormsModule, DynamicDialogModule,TooltipModule,
ButtonModule, TableModule],
templateUrl: './photolist.html',
styleUrls: ['./photolist.css'],
providers: [DialogService]
})
export class PhotoList implements OnInit, OnDestroy {
FileList: PersonPhotoDto[] =[]
loading = false;
_id = -1;
deletePersonPhoto: number[] = [];
private authenticationService = inject(AuthenticationService);
private cdr = inject(ChangeDetectorRef);
private confirmationService = inject(ConfirmationService);
private messageService = inject(MessageService);
private subscription: Subscription = new Subscription();
downloadFile = signal(false);
constructor(
private http: HttpUtility,
private personService: PersonService,
public ref: DynamicDialogRef, public config: DynamicDialogConfig,
public dialogService: DialogService
) { }
ngOnInit(): void {
const id = this.config.data.id;
const olist = this.config.data.personPhotos;
if (olist && olist.length > 0)
{
this.FileList = olist;
this.assignFileId(olist);
}
}
assignFileId(files: any[]): void {
let i = 0;
let id = -1;
let mx = 1;
for (i = 0; i < files.length; i++)
{
id = files[i].id;
if (id < mx)
{
mx = id;
}
}
this._id = -1 * mx;
}
downloadAttachment(id: number): void {
//GetReportFile
let typeofCall = "personId_" + id.toString();
let criteria:any ={id, fileName:''};
this.downloadFile.set(false);
this.http.getFileResponse("api/FileUpload/downloadPersonPhoto", criteria).pipe(
catchError(err => {
console.error(err.message);
return EMPTY;
}),
finalize(() => {
// this.toastr.success("download completed ok to view now");
this.downloadFile.set(false);
})).subscribe((response: HttpResponse<Blob>) => {
if (response) {
console.log("the download report response", response);
CommonUtilities.downloadFile(response, typeofCall);
}
});
}
onClose(event: Event): void {
const nlist = this.FileList.filter( x => x.id < 1);
this.ref.close({list: nlist, deleteIds: this.deletePersonPhoto});
}
newSupportDoc(event: Event): void {
this.showEdit(this._id--);
}
remove(id: number): void {
this.confirmationService.confirm({
message: 'Do you want to delete this record?',
header: 'Confirmation Delete',
icon: 'pi pi-info-circle',
rejectLabel: 'Cancel',
rejectButtonProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true,
},
acceptButtonProps: {
label: 'Delete',
severity: 'danger',
},
accept: () => {
this.doRemove(id);
}
});
}
doRemove(id: number): void {
if (id < 0)
{
const nlist = this.FileList.filter(x => x.id != id);
this.FileList = [...nlist];
this.cdr.markForCheck();
}
else
{
const deletepersonPhoto$ = this.personService.deletePersonPhotoFile(id);
this.subscription.add(deletepersonPhoto$.subscribe(
{
next: x => {
if (x.statusCode == 1)
{
const nlist = this.FileList.filter(x => x.id != id);
this.FileList = [...nlist];
this.deletePersonPhoto.push(id);
this.cdr.markForCheck();
this.messageService.add({severity:'success', summary: 'delete person photo', detail: "person photo Id " + id });
}
},
error:e => console.error("error in client delete person photo", e)
}
));
}
}
showEdit(id: number) {
const ref = this.dialogService.open(AddPhoto, {
data: {
id,
},
header: 'Add File',
width: '70%',
maximizable: true,
draggable: true
});
if (ref)
{
ref.onClose.subscribe((list: File[]) => {
if (list) {
//console.log("after close ward edit", item);
this.messageService.add({ severity: 'success', summary: 'Attact File', detail: list.length.toString() });
let it = id;
for (let i = 0; i < list.length; i++)
{
const item = list[i];
this.FileList.push({id: it--, photo: item.name, photoType:'', file: item});
}
this.cdr.markForCheck();
}
});
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
//import { Component, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core';
import { catchError, EMPTY, finalize, Subscription } from 'rxjs';
import { CommonUtilities, HttpUtility, LookupService } from '../shares';
import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from 'primeng/dynamicdialog';
import { ConfirmationService, MessageService } from 'primeng/api';
import { AddPhoto } from './add.photo';
import { PersonPhotoDto } from '../models';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { TooltipModule } from 'primeng/tooltip';
import { AuthenticationService } from '../user-services';
import { HttpResponse } from '@angular/common/http';
import { PersonService } from '../person';
@Component({
selector: 'photo-list',
imports: [CommonModule, FormsModule, DynamicDialogModule,TooltipModule,
ButtonModule, TableModule],
templateUrl: './photolist.html',
styleUrls: ['./photolist.css'],
providers: [DialogService]
})
export class PhotoList implements OnInit, OnDestroy {
FileList: PersonPhotoDto[] =[]
loading = false;
_id = -1;
deletePersonPhoto: number[] = [];
private authenticationService = inject(AuthenticationService);
private cdr = inject(ChangeDetectorRef);
private confirmationService = inject(ConfirmationService);
private messageService = inject(MessageService);
private subscription: Subscription = new Subscription();
downloadFile = signal(false);
constructor(
private http: HttpUtility,
private personService: PersonService,
public ref: DynamicDialogRef, public config: DynamicDialogConfig,
public dialogService: DialogService
) { }
ngOnInit(): void {
const id = this.config.data.id;
const olist = this.config.data.personPhotos;
if (olist && olist.length > 0)
{
this.FileList = olist;
this.assignFileId(olist);
}
}
assignFileId(files: any[]): void {
let i = 0;
let id = -1;
let mx = 1;
for (i = 0; i < files.length; i++)
{
id = files[i].id;
if (id < mx)
{
mx = id;
}
}
this._id = -1 * mx;
}
downloadAttachment(id: number): void {
//GetReportFile
let typeofCall = "personId_" + id.toString();
let criteria:any ={id, fileName:''};
this.downloadFile.set(false);
this.http.getFileResponse("api/FileUpload/downloadPersonPhoto", criteria).pipe(
catchError(err => {
console.error(err.message);
return EMPTY;
}),
finalize(() => {
// this.toastr.success("download completed ok to view now");
this.downloadFile.set(false);
})).subscribe((response: HttpResponse<Blob>) => {
if (response) {
console.log("the download report response", response);
CommonUtilities.downloadFile(response, typeofCall);
}
});
}
onClose(event: Event): void {
const nlist = this.FileList.filter( x => x.id < 1);
this.ref.close({list: nlist, deleteIds: this.deletePersonPhoto});
}
newSupportDoc(event: Event): void {
this.showEdit(this._id--);
}
remove(id: number): void {
this.confirmationService.confirm({
message: 'Do you want to delete this record?',
header: 'Confirmation Delete',
icon: 'pi pi-info-circle',
rejectLabel: 'Cancel',
rejectButtonProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true,
},
acceptButtonProps: {
label: 'Delete',
severity: 'danger',
},
accept: () => {
this.doRemove(id);
}
});
}
doRemove(id: number): void {
if (id < 0)
{
const nlist = this.FileList.filter(x => x.id != id);
this.FileList = [...nlist];
this.cdr.markForCheck();
}
else
{
const deletepersonPhoto$ = this.personService.deletePersonPhotoFile(id);
this.subscription.add(deletepersonPhoto$.subscribe(
{
next: x => {
if (x.statusCode == 1)
{
const nlist = this.FileList.filter(x => x.id != id);
this.FileList = [...nlist];
this.deletePersonPhoto.push(id);
this.cdr.markForCheck();
this.messageService.add({severity:'success', summary: 'delete person photo', detail: "person photo Id " + id });
}
},
error:e => console.error("error in client delete person photo", e)
}
));
}
}
showEdit(id: number) {
const ref = this.dialogService.open(AddPhoto, {
data: {
id,
},
header: 'Add File',
width: '70%',
maximizable: true,
draggable: true
});
if (ref)
{
ref.onClose.subscribe((list: File[]) => {
if (list) {
//console.log("after close ward edit", item);
this.messageService.add({ severity: 'success', summary: 'Attact File', detail: list.length.toString() });
let it = id;
for (let i = 0; i < list.length; i++)
{
const item = list[i];
this.FileList.push({id: it--, photo: item.name, photoType:'', file: item});
}
this.cdr.markForCheck();
}
});
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+10 -10
View File
@@ -1,10 +1,10 @@
<div>
<label class="flex mb-2"> Import Excel File</label>
<p-fileupload #fu mode="basic" chooseLabel="Choose"
chooseIcon="pi pi-upload" name="file"
url='{{baseUrl}}'
maxFileSize="10000000000" (onUpload)="onUpload($event)" />
<p-button styleClass="mt-4" label="Upload" (onClick)="fu.upload()" severity="primary" />
</div>
<div>
<label class="flex mb-2"> Import Excel File</label>
<p-fileupload #fu mode="basic" chooseLabel="Choose"
chooseIcon="pi pi-upload" name="file"
url='{{baseUrl}}'
maxFileSize="10000000000" (onUpload)="onUpload($event)" />
<p-button styleClass="mt-4" label="Upload" (onClick)="fu.upload()" severity="primary" />
</div>
+38 -38
View File
@@ -1,38 +1,38 @@
import { Component, inject, OnInit } from '@angular/core';
import { ButtonModule } from 'primeng/button';
import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload';
import { MessageService } from 'primeng/api';
import { FileUpload } from 'primeng/fileupload';
import { ToastModule } from 'primeng/toast';
import { AppSettingService } from '../shares';
/*
interface UploadEvent {
originalEvent: Event;
files: File[];
}*/
@Component({
selector: 'app-import.com',
imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload],
templateUrl: './import.com.html',
styleUrl: './import.com.css'
})
export class ImportCom implements OnInit {
uploadedFiles: any[] = [];
baseUrl = "";
private appSetting = inject(AppSettingService);
constructor(private messageService: MessageService) {}
ngOnInit(): void
{
const cbaseUrl = this.appSetting.appSetting.baseUrl;
//http://localhost:5015/api/FileUpload/UploadFile"
this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile";
}
onUpload(event:FileUploadEvent) {
for(let file of event.files) {
this.uploadedFiles.push(file);
}
this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''});
}
}
import { Component, inject, OnInit } from '@angular/core';
import { ButtonModule } from 'primeng/button';
import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload';
import { MessageService } from 'primeng/api';
import { FileUpload } from 'primeng/fileupload';
import { ToastModule } from 'primeng/toast';
import { AppSettingService } from '../shares';
/*
interface UploadEvent {
originalEvent: Event;
files: File[];
}*/
@Component({
selector: 'app-import.com',
imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload],
templateUrl: './import.com.html',
styleUrl: './import.com.css'
})
export class ImportCom implements OnInit {
uploadedFiles: any[] = [];
baseUrl = "";
private appSetting = inject(AppSettingService);
constructor(private messageService: MessageService) {}
ngOnInit(): void
{
const cbaseUrl = this.appSetting.appSetting.baseUrl;
//http://localhost:5015/api/FileUpload/UploadFile"
this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile";
}
onUpload(event:FileUploadEvent) {
for(let file of event.files) {
this.uploadedFiles.push(file);
}
this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''});
}
}
+12 -12
View File
@@ -1,12 +1,12 @@
<form runat="server">
<input accept="image/*" type='file' id="imgInp" />
<img id="blah" src="#" alt="your image" />
</form>
imgInp.onchange = evt => {
const [file] = imgInp.files
if (file) {
blah.src = URL.createObjectURL(file)
}
}
<form runat="server">
<input accept="image/*" type='file' id="imgInp" />
<img id="blah" src="#" alt="your image" />
</form>
imgInp.onchange = evt => {
const [file] = imgInp.files
if (file) {
blah.src = URL.createObjectURL(file)
}
}
+2 -2
View File
@@ -1,2 +1,2 @@
export * from './login';
export * from './login';
+9 -9
View File
@@ -1,10 +1,10 @@
.btnloginRigh {
display: flex;
flex-direction:reverse;
justify-content:flex-end;
width: 100%;
}
.myerror {
background-color: red;
color: white;
.btnloginRigh {
display: flex;
flex-direction:reverse;
justify-content:flex-end;
width: 100%;
}
.myerror {
background-color: red;
color: white;
}
+44 -44
View File
@@ -1,45 +1,45 @@
<div class="flex justify-center items-center h-full">
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" style="max-width: 600px;min-width: 300px;">
<p-card>
<div>
<img class="border-round" style="width:100%;height:100px;" alt="Logo" src="images/application_image_login.png">
</div>
<div class="mt-4">
<div class="flex flex-col">
<label for="email" class="w-full">Email<strong class="validateStar">*</strong></label>
<input type="text" pInputText formControlName="username" name="email"
[ngClass]="{ 'is-invalid': submitted && f.username.errors }" class="p-inputtext-sm inputfield">
</div>
<div class="flex flex-col">
<label for="ppassword" class="w-full">Password <strong class="validateStar">*</strong></label>
<input type="password" formControlName="password" name="ppassword" pInputText
[ngClass]="{ 'is-invalid': submitted && f.password.errors }" class="p-inputtext-sm inputfield">
</div>
</div>
<!--p-footer -->
<div class="btnloginRigh">
<div class="mt-4 pt-2">
<button pButton type="submit" label="Login" class="p-button-sm" [disabled]="isFieldsChange"
[loading]="loading"></button>
</div>
</div>
<!--/p-footer-->
</p-card>
<div class="myerror">{{error}}</div>
<div style="padding-top: 10px;"></div>
<!--div
style="text-align: center;display:inline-block;border-radius: var(--border-radius) !important;background-color:#e1e1e1; color:#222;font-size:13px;width:100%; margin-left:5px;margin-right: 5px;">
<p >
For management access to this application please email
</p>
<a href="mailto:NBMLHD-fleetteamreceipt@health.nsw.gov.au">NBMLHD-fleetteamreceipt@health.nsw.gov.au
</a>
</div-->
<!--pre> {{loginForm.value|json}} </pre-->
</form>
<div class="flex justify-center items-center h-full">
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" style="max-width: 600px;min-width: 300px;">
<p-card>
<div>
<img class="border-round" style="width:100%;height:100px;" alt="Logo" src="images/application_image_login.png">
</div>
<div class="mt-4">
<div class="flex flex-col">
<label for="email" class="w-full">Email<strong class="validateStar">*</strong></label>
<input type="text" pInputText formControlName="username" name="email"
[ngClass]="{ 'is-invalid': submitted && f.username.errors }" class="p-inputtext-sm inputfield">
</div>
<div class="flex flex-col">
<label for="ppassword" class="w-full">Password <strong class="validateStar">*</strong></label>
<input type="password" formControlName="password" name="ppassword" pInputText
[ngClass]="{ 'is-invalid': submitted && f.password.errors }" class="p-inputtext-sm inputfield">
</div>
</div>
<!--p-footer -->
<div class="btnloginRigh">
<div class="mt-4 pt-2">
<button pButton type="submit" label="Login" class="p-button-sm" [disabled]="isFieldsChange"
[loading]="loading"></button>
</div>
</div>
<!--/p-footer-->
</p-card>
<div class="myerror">{{error}}</div>
<div style="padding-top: 10px;"></div>
<!--div
style="text-align: center;display:inline-block;border-radius: var(--border-radius) !important;background-color:#e1e1e1; color:#222;font-size:13px;width:100%; margin-left:5px;margin-right: 5px;">
<p >
For management access to this application please email
</p>
<a href="mailto:NBMLHD-fleetteamreceipt@health.nsw.gov.au">NBMLHD-fleetteamreceipt@health.nsw.gov.au
</a>
</div-->
<!--pre> {{loginForm.value|json}} </pre-->
</form>
</div>
+137 -137
View File
@@ -1,137 +1,137 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { Router, ActivatedRoute, RouterModule } from '@angular/router';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { first } from 'rxjs/operators';
import { AuthenticationService } from '../user-services';
import { ConfirmationService } from 'primeng/api';
import { Subject, Subscription } from 'rxjs';
import { CommonModule } from '@angular/common';
import { CardModule } from 'primeng/card';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { AppSettingService, Utils } from '../shares';
@Component({
templateUrl: './login.html',
styleUrl: './login.css',
selector: 'login',
imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule,
RouterModule, CommonModule, CardModule],
})
export class Login implements OnInit, OnDestroy {
loginForm: FormGroup;
confirmationService = inject(ConfirmationService);
route = inject(ActivatedRoute);
router = inject(Router);
authenticationService = inject(AuthenticationService);
formBuilder = inject(FormBuilder);
private appSetting =inject(AppSettingService);
loading = false;
homeUrl = "/person";
submitted = false;
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
subChanged$ = new Subject<boolean>();
// get return url from route parameters or default to '/'
returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl;
error = '';
private subscription: Subscription = new Subscription();
constructor() {
const default_us = this.appSetting.appSetting.username;
const pass = this.appSetting.appSetting.prefill_ps;
this.loginForm = this.formBuilder.group({
username: [default_us, Validators.required],
password: [pass, Validators.required],
});
}
ngOnInit() {
// reset login status
this.loading = false;
this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}
updateData($event: Event) {
console.log("onChange click", $event);
}
// convenience getter for easy access to form fields
get f() { return this.loginForm.value; }
get isFieldsChange() {
const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
onSubmit() {
//this.playAudio();
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
}
const fvalue = this.loginForm.value;
this.loading = true;
this.error = "";
let username = "";
let password = "";
if (fvalue.username != null)
username = fvalue.username;
if (fvalue.password != null)
password = fvalue.password;
this.authenticationService.login(username, password)
.pipe(first())
.subscribe({
next: (x: any) => {
this.loading = false;
console.log("login result ", x.data, this.returnUrl);
if (x.statusCode == 1) {
// if (x.data.role == 1) {
// this.homeService.reportDate = Utils.getLastMonth();
// this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } });
// }
// else if (x.data.role == 3) {
// this.homeService.reportDate = Utils.getLastMonth();
// this.router.navigate(['/approval/' + x.data.id], { queryParams: { returnUrl: '/login' } });
//}
// else
this.router.navigate([this.homeUrl]);
}
else {
this.loginForm.patchValue({ password: '' });
// alert("Invalid Username or Password. Please try again.");
console.error("error in login", x);
this.confirmationService.confirm({
message: 'Error in Login: Invalid username or password. May be your password is expired',
header: 'Error Login',
icon: 'pi pi-info-circle',
rejectVisible: false,
acceptLabel: 'OK',
});
}
},
error: er => {
console.log("error in login", er);
this.error = er.message;
this.loading = false;
}
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
this.loading = false;
}
}
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { Router, ActivatedRoute, RouterModule } from '@angular/router';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { first } from 'rxjs/operators';
import { AuthenticationService } from '../user-services';
import { ConfirmationService } from 'primeng/api';
import { Subject, Subscription } from 'rxjs';
import { CommonModule } from '@angular/common';
import { CardModule } from 'primeng/card';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { AppSettingService, Utils } from '../shares';
@Component({
templateUrl: './login.html',
styleUrl: './login.css',
selector: 'login',
imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule,
RouterModule, CommonModule, CardModule],
})
export class Login implements OnInit, OnDestroy {
loginForm: FormGroup;
confirmationService = inject(ConfirmationService);
route = inject(ActivatedRoute);
router = inject(Router);
authenticationService = inject(AuthenticationService);
formBuilder = inject(FormBuilder);
private appSetting =inject(AppSettingService);
loading = false;
homeUrl = "/person";
submitted = false;
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
subChanged$ = new Subject<boolean>();
// get return url from route parameters or default to '/'
returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl;
error = '';
private subscription: Subscription = new Subscription();
constructor() {
const default_us = this.appSetting.appSetting.username;
const pass = this.appSetting.appSetting.prefill_ps;
this.loginForm = this.formBuilder.group({
username: [default_us, Validators.required],
password: [pass, Validators.required],
});
}
ngOnInit() {
// reset login status
this.loading = false;
this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}
updateData($event: Event) {
console.log("onChange click", $event);
}
// convenience getter for easy access to form fields
get f() { return this.loginForm.value; }
get isFieldsChange() {
const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
onSubmit() {
//this.playAudio();
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
}
const fvalue = this.loginForm.value;
this.loading = true;
this.error = "";
let username = "";
let password = "";
if (fvalue.username != null)
username = fvalue.username;
if (fvalue.password != null)
password = fvalue.password;
this.authenticationService.login(username, password)
.pipe(first())
.subscribe({
next: (x: any) => {
this.loading = false;
console.log("login result ", x.data, this.returnUrl);
if (x.statusCode == 1) {
// if (x.data.role == 1) {
// this.homeService.reportDate = Utils.getLastMonth();
// this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } });
// }
// else if (x.data.role == 3) {
// this.homeService.reportDate = Utils.getLastMonth();
// this.router.navigate(['/approval/' + x.data.id], { queryParams: { returnUrl: '/login' } });
//}
// else
this.router.navigate([this.homeUrl]);
}
else {
this.loginForm.patchValue({ password: '' });
// alert("Invalid Username or Password. Please try again.");
console.error("error in login", x);
this.confirmationService.confirm({
message: 'Error in Login: Invalid username or password. May be your password is expired',
header: 'Error Login',
icon: 'pi pi-info-circle',
rejectVisible: false,
acceptLabel: 'OK',
});
}
},
error: er => {
console.log("error in login", er);
this.error = er.message;
this.loading = false;
}
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
this.loading = false;
}
}
+6 -6
View File
@@ -1,6 +1,6 @@
export interface Code {
id:number;
name:string;
status?:string;
active?:boolean;
}
export interface Code {
id:number;
name:string;
status?:string;
active?:boolean;
}
+16 -16
View File
@@ -1,17 +1,17 @@
export enum ConfigureUrl
{
//baseUrl = "http://localhost:61744",
loginApiUrl = "api/Users/Login",
searchStaffUrl = "api/Users/SearchADStaff",
staffUrl = "api/Staff",
personUrl = "api/Person",
staffWorkUrl = "api/StaffWork",
jobUrl ="api/Job",
clientUrl ="api/Client",
relationShipUrl ="api/RelationShip",
logoutUrl ="api/Users/Logout",
adminUserUrl = "api/Staff",
lookupUrl = "api/Lookup",
FileUploadUrl = "api/FileUpload",
export enum ConfigureUrl
{
//baseUrl = "http://localhost:61744",
loginApiUrl = "api/Users/Login",
searchStaffUrl = "api/Users/SearchADStaff",
staffUrl = "api/Staff",
personUrl = "api/Person",
staffWorkUrl = "api/StaffWork",
jobUrl ="api/Job",
clientUrl ="api/Client",
relationShipUrl ="api/RelationShip",
logoutUrl ="api/Users/Logout",
adminUserUrl = "api/Staff",
lookupUrl = "api/Lookup",
FileUploadUrl = "api/FileUpload",
}
+37 -37
View File
@@ -1,37 +1,37 @@
export const MIMEType = {
png: 'image/png',
jpg: 'image/jpg',
jpeg: 'image/jpeg',
gif: 'image/gif',
txt: 'text/plain',
pdf: 'application/pdf'
};
//for status of object 0 no change, 1 changed , -1 is deleted.
export enum mState {
Delete = -1,
NoChange = 0,
Modified = 1,
New =2,
};
export enum userRole {
Normal = 1,
Admin = 2,
ServiceManager =3,
Accounting = 4,
WorkShop = 5,
};
export enum enumReqStatus {
Canel = -1,
Decline = -2,
NewRequest = 0,
Allocate = 1,
Arrival = 2,
Completed = 3,
OnHold = 4,
OffHold = 5
};
export const MIMEType = {
png: 'image/png',
jpg: 'image/jpg',
jpeg: 'image/jpeg',
gif: 'image/gif',
txt: 'text/plain',
pdf: 'application/pdf'
};
//for status of object 0 no change, 1 changed , -1 is deleted.
export enum mState {
Delete = -1,
NoChange = 0,
Modified = 1,
New =2,
};
export enum userRole {
Normal = 1,
Admin = 2,
ServiceManager =3,
Accounting = 4,
WorkShop = 5,
};
export enum enumReqStatus {
Canel = -1,
Decline = -2,
NewRequest = 0,
Allocate = 1,
Arrival = 2,
Completed = 3,
OnHold = 4,
OffHold = 5
};
+10 -10
View File
@@ -1,11 +1,11 @@
export * from './code';
export * from './configureUrl';
export * from './user';
export * from './resultmodel';
export * from './enum';
export * from './lookup';
export * from './staff';
export * from './person';
export * from './job';
export * from './relationship';
export * from './code';
export * from './configureUrl';
export * from './user';
export * from './resultmodel';
export * from './enum';
export * from './lookup';
export * from './staff';
export * from './person';
export * from './job';
export * from './relationship';
export * from './personphotodto';
+12 -12
View File
@@ -1,12 +1,12 @@
export interface JobSearch {
code:string;
description:string;
}
export interface Job {
id:number;
code:string|null|undefined;
description:string |null|undefined;
active:boolean |null|undefined;
}
export interface JobSearch {
code:string;
description:string;
}
export interface Job {
id:number;
code:string|null|undefined;
description:string |null|undefined;
active:boolean |null|undefined;
}
+15 -15
View File
@@ -1,16 +1,16 @@
// using for priority and infectionType
export interface Lookup {
id:number;
codeId:string;
description:string;
}
export interface LookupEdit {
id:number;
codeId:string;
description:string;
active:boolean;
type:string;
// using for priority and infectionType
export interface Lookup {
id:number;
codeId:string;
description:string;
}
export interface LookupEdit {
id:number;
codeId:string;
description:string;
active:boolean;
type:string;
}
+6 -6
View File
@@ -1,7 +1,7 @@
export interface MyDetail{
id:number,
login:string,
firstname:string,
surname:string,
jobTitle:string,
export interface MyDetail{
id:number,
login:string,
firstname:string,
surname:string,
jobTitle:string,
}
+37 -37
View File
@@ -1,37 +1,37 @@
import { PersonPhotoDto } from "./personphotodto";
import { RelationShip } from "./relationship";
export interface FamilySearch {
email:string|null;
phone:string |null;
clientname:string |null;
}
export interface Person {
id: number;
title?:string| null| undefined;
firstName: string | null|undefined;
lastName: string | null|undefined;
email: string | null|undefined;
phone: string | null|undefined;
address?: string | null|undefined;
alive: boolean | null|undefined;
dob? : string | null|undefined;
fatherId?: number| null|undefined;
motherId?: number| null|undefined;
image?: string|null|undefined;
sex?: string|null|undefined;
fatherName?:string |null;
motherName?:string |null;
relationShips?: RelationShip[];
personPhotos?: PersonPhotoDto[];
}
export interface PersonContainer
{
person: Person;
formData?:FormData;
}
import { PersonPhotoDto } from "./personphotodto";
import { RelationShip } from "./relationship";
export interface FamilySearch {
email:string|null;
phone:string |null;
clientname:string |null;
}
export interface Person {
id: number;
title?:string| null| undefined;
firstName: string | null|undefined;
lastName: string | null|undefined;
email: string | null|undefined;
phone: string | null|undefined;
address?: string | null|undefined;
alive: boolean | null|undefined;
dob? : string | null|undefined;
fatherId?: number| null|undefined;
motherId?: number| null|undefined;
image?: string|null|undefined;
sex?: string|null|undefined;
fatherName?:string |null;
motherName?:string |null;
relationShips?: RelationShip[];
personPhotos?: PersonPhotoDto[];
}
export interface PersonContainer
{
person: Person;
formData?:FormData;
}
+5 -5
View File
@@ -1,6 +1,6 @@
export interface PersonPhotoDto {
id: number;
photo:string|null | undefined;
photoType:string |null | undefined;
file: File | null | undefined;
export interface PersonPhotoDto {
id: number;
photo:string|null | undefined;
photoType:string |null | undefined;
file: File | null | undefined;
}
+26 -26
View File
@@ -1,27 +1,27 @@
import { mState } from "./enum";
export interface RelationShip {
id: number;
relatePersonId: number;
personId: number;
state: mState;
}
//relationship id this relatePersonId person has relation with other person
// also other person has also has relation with you. look in two way
// person id = 1 has to relation id = 90
// that means when you edit person 90 their relation ship also be there.
// so personid = 1 and relation id = 90 we need to show other way around.
//example first name = Smith has partner Jennifer
// when we edit Jennifer it should show Smith as her partner too.
export interface RelationShipView {
id: number;
relatePersonId:number;
personId : number;
pfirstName: string |null |undefined;
plastName: string |null |undefined;
sex:string |null |undefined;
state: mState;
import { mState } from "./enum";
export interface RelationShip {
id: number;
relatePersonId: number;
personId: number;
state: mState;
}
//relationship id this relatePersonId person has relation with other person
// also other person has also has relation with you. look in two way
// person id = 1 has to relation id = 90
// that means when you edit person 90 their relation ship also be there.
// so personid = 1 and relation id = 90 we need to show other way around.
//example first name = Smith has partner Jennifer
// when we edit Jennifer it should show Smith as her partner too.
export interface RelationShipView {
id: number;
relatePersonId:number;
personId : number;
pfirstName: string |null |undefined;
plastName: string |null |undefined;
sex:string |null |undefined;
state: mState;
}
+4 -4
View File
@@ -1,5 +1,5 @@
export interface ResultModel<T> {
data: T;
message: string;
statusCode:number;
export interface ResultModel<T> {
data: T;
message: string;
statusCode:number;
}
+39 -39
View File
@@ -1,39 +1,39 @@
export interface StaffView {
id:number,
email:string,
firstname:string,
lastname:string,
active:boolean,
}
export interface ResetPassword{
id:number;
password:string;
}
export interface Staff {
id:number,
email:string,
firstname:string,
lastname:string,
phone:string,
type:number;
active:boolean;
roleType: number;
password?:string,
}
export interface StaffSearch {
email:string;
firstName:string;
lastName:string;
}
//this use for new user only.
export interface AdminUserNew {
loginId:number;
user: Staff;// first get userid from this table and
}
export interface StaffView {
id:number,
email:string,
firstname:string,
lastname:string,
active:boolean,
}
export interface ResetPassword{
id:number;
password:string;
}
export interface Staff {
id:number,
email:string,
firstname:string,
lastname:string,
phone:string,
type:number;
active:boolean;
roleType: number;
password?:string,
}
export interface StaffSearch {
email:string;
firstName:string;
lastName:string;
}
//this use for new user only.
export interface AdminUserNew {
loginId:number;
user: Staff;// first get userid from this table and
}
+36 -36
View File
@@ -1,36 +1,36 @@
export class User {
id: number = 0;
username: string = '';
role: number = -1;
firstName: string = '';
lastName: string = '';
email:string = '';
token:string ='';
position: string ='';
department: string = '';
managerEmail:string ='';
phone:string='';
}
export interface UserAD
{
id: number,
firstName: string,
lastName: string,
email: string,
username: string,
department: string,
position: string,
role: number,
phone:string,
managerEmail:string
}
export class SecAccessLevel {
accessName:string='';
request:number=0;
report:number=0;
admin:number=0;
allocation:number=0;
}
export class User {
id: number = 0;
username: string = '';
role: number = -1;
firstName: string = '';
lastName: string = '';
email:string = '';
token:string ='';
position: string ='';
department: string = '';
managerEmail:string ='';
phone:string='';
}
export interface UserAD
{
id: number,
firstName: string,
lastName: string,
email: string,
username: string,
department: string,
position: string,
role: number,
phone:string,
managerEmail:string
}
export class SecAccessLevel {
accessName:string='';
request:number=0;
report:number=0;
admin:number=0;
allocation:number=0;
}
+71 -71
View File
@@ -1,71 +1,71 @@
//mypreset.ts
import { definePreset } from '@primeuix/themes';
import Aura from '@primeuix/themes/aura';
import { primitive } from '@primeuix/themes/aura/base';
const MyPreset = definePreset(Aura, {
semantic: {
colorScheme: {
primitive: {
cyan: {
50: '{cyan.50}',
100: '{cyan.100}',
200: '{cyan.200}',
300: '{cyan.300}',
400: '{cyan.400}',
500: '{cyan.500}',
}
},
primary: {
50: '{zinc.50}',
100: '{zinc.100}',
200: '{zinc.200}',
300: '{zinc.300}',
400: '{zinc.400}',
500: '{zinc.500}',
600: '{zinc.600}',
700: '{zinc.700}',
800: '{zinc.800}',
900: '{zinc.900}',
950: '{zinc.950}'
},
light: {
surface: {
0: '#ffffff',
50: '{zinc.50}',
100: '{zinc.100}',
200: '{zinc.200}',
300: '{zinc.300}',
400: '{zinc.400}',
500: '{zinc.500}',
600: '{zinc.600}',
700: '{zinc.700}',
800: '{zinc.800}',
900: '{zinc.900}',
950: '{zinc.950}'
}
},
dark: {
surface: {
0: '#ffffff',
50: '{slate.50}',
100: '{slate.100}',
200: '{slate.200}',
300: '{slate.300}',
400: '{slate.400}',
500: '{slate.500}',
600: '{slate.600}',
700: '{slate.700}',
800: '{slate.800}',
900: '{slate.900}',
950: '{slate.950}'
}
}
}
}
});
export default MyPreset;
//mypreset.ts
import { definePreset } from '@primeuix/themes';
import Aura from '@primeuix/themes/aura';
import { primitive } from '@primeuix/themes/aura/base';
const MyPreset = definePreset(Aura, {
semantic: {
colorScheme: {
primitive: {
cyan: {
50: '{cyan.50}',
100: '{cyan.100}',
200: '{cyan.200}',
300: '{cyan.300}',
400: '{cyan.400}',
500: '{cyan.500}',
}
},
primary: {
50: '{zinc.50}',
100: '{zinc.100}',
200: '{zinc.200}',
300: '{zinc.300}',
400: '{zinc.400}',
500: '{zinc.500}',
600: '{zinc.600}',
700: '{zinc.700}',
800: '{zinc.800}',
900: '{zinc.900}',
950: '{zinc.950}'
},
light: {
surface: {
0: '#ffffff',
50: '{zinc.50}',
100: '{zinc.100}',
200: '{zinc.200}',
300: '{zinc.300}',
400: '{zinc.400}',
500: '{zinc.500}',
600: '{zinc.600}',
700: '{zinc.700}',
800: '{zinc.800}',
900: '{zinc.900}',
950: '{zinc.950}'
}
},
dark: {
surface: {
0: '#ffffff',
50: '{slate.50}',
100: '{slate.100}',
200: '{slate.200}',
300: '{slate.300}',
400: '{slate.400}',
500: '{slate.500}',
600: '{slate.600}',
700: '{slate.700}',
800: '{slate.800}',
900: '{slate.900}',
950: '{slate.950}'
}
}
}
}
});
export default MyPreset;
+12 -12
View File
@@ -1,12 +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;
}
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
td.mat-column-edit, .mat-column-delete {
width: 35px;
padding-right: 2px;
}
+39 -39
View File
@@ -1,39 +1,39 @@
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Family Tree </h3>
<div>
<label>link from partner and Father and Mother Id</label>
<div >
<!--this styleclass all logic in api one not here. background color -->
<!--the treeNode.Data has treeData image and sex-->
<!--image is 64 string so to show 'data:image/png;base64,'imageStr' here -->
<!-- selectionMode="multiple" [(selection)]="selectedNodes" -->
<p-organization-chart [value]="familyTree()" [collapsible]="true">
<ng-template let-node pTemplate="default">
<div #targetEl class="flex flex-col items-center">
@if (node.data.image != "")
{
<img (mouseover)="op.show($event, targetEl)" [src]= "node.data.image" class="mb-2 w-12 h-12 rounded-full " />
}
<!--img src="https://primefaces.org/cdn/primeng/images/flag/flag_placeholder.png" [alt]="node.label" [class]="'flag' + ' flag-' + node.data" width="32" /-->
<div class="mt-2 font-medium text-lg">{{ node.label }}</div>
<div>{{ node.data.sex }} </div>
<p-popover #op class="flex w-2/5 h-2/5">
@if (node.data.image != "")
{
<div>
<p-button (onClick)="op.hide()" icon="pi pi-times" [rounded]="true" [text]="true" severity="danger" class="flex justify-end mb-2" />
<img [src]= "node.data.image" alt="person profile" />
</div>
}
</p-popover>
</div>
</ng-template>
</p-organization-chart>
</div>
<div class="flex justify-end">
<button pButton pRipple class="p-button-sm mr-2 p-button-success" type="button" icon="pi pi-sign-in" label="Close"
(click)="close()"></button>
</div>
</div>
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Family Tree </h3>
<div>
<label>link from partner and Father and Mother Id</label>
<div >
<!--this styleclass all logic in api one not here. background color -->
<!--the treeNode.Data has treeData image and sex-->
<!--image is 64 string so to show 'data:image/png;base64,'imageStr' here -->
<!-- selectionMode="multiple" [(selection)]="selectedNodes" -->
<p-organization-chart [value]="familyTree()" [collapsible]="true">
<ng-template let-node pTemplate="default">
<div #targetEl class="flex flex-col items-center">
@if (node.data.image != "")
{
<img (mouseover)="op.show($event, targetEl)" [src]= "node.data.image" class="mb-2 w-12 h-12 rounded-full " />
}
<!--img src="https://primefaces.org/cdn/primeng/images/flag/flag_placeholder.png" [alt]="node.label" [class]="'flag' + ' flag-' + node.data" width="32" /-->
<div class="mt-2 font-medium text-lg">{{ node.label }}</div>
<div>{{ node.data.sex }} </div>
<p-popover #op class="flex w-2/5 h-2/5">
@if (node.data.image != "")
{
<div>
<p-button (onClick)="op.hide()" icon="pi pi-times" [rounded]="true" [text]="true" severity="danger" class="flex justify-end mb-2" />
<img [src]= "node.data.image" alt="person profile" />
</div>
}
</p-popover>
</div>
</ng-template>
</p-organization-chart>
</div>
<div class="flex justify-end">
<button pButton pRipple class="p-button-sm mr-2 p-button-success" type="button" icon="pi pi-sign-in" label="Close"
(click)="close()"></button>
</div>
</div>
+256 -256
View File
@@ -1,256 +1,256 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { PersonService } from './person.service';
import { TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { TreeNode } from 'primeng/api';
import { InputTextModule } from 'primeng/inputtext';
import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
import { Utils } from '../shares';
import { PopoverModule } from 'primeng/popover';
@Component({
selector: 'family-orga',
templateUrl: './family.orga.html',
imports:[TableModule,FormsModule,TreeModule,PopoverModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
styleUrls: ['./family.orga.css'],
providers: [DialogService]
})
export class FamilyOrga implements OnInit, OnDestroy{
private subscription:Subscription = new Subscription();
person!: Person;
selectedNode!: TreeNode;
selectedNodes!: TreeNode[];
private cd = inject(ChangeDetectorRef);
familyTree = signal<TreeNode[]>([]);
relations: RelationShip[] =[];
_id = -10;
loading = false;
familyList:Person[] = [];
msg ="[Person organise component]";
private messageService = inject(MessageService);
public ref = inject(DynamicDialogRef);
public config = inject(DynamicDialogConfig);
/*
private store: Store<appValidationState>
*/
private personService = inject(PersonService);
public dialogService = inject(DialogService);
ngOnInit(): void
{
const id = this.config.data.id;
this.familyList = this.config.data.familyList;
const item = this.familyList.find(x => x.id == id);
if (item != undefined)
this.person = item;
this.loadPersonFamilyTree(id);
//this.populateTree();
}
populateTree() : void {
const data: TreeNode[] = [
{
expanded: true,
type: 'person',
styleClass: 'bg-indigo-500 text-white',
data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png',
name: 'Amy Elsner',
title: 'CEO'
},
children: [
{
expanded: true,
type: 'person',
styleClass: 'bg-purple-500 text-white',
data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png',
name: 'Anna Fali',
title: 'CMO'
},
children: [
{
label: 'Sales',
styleClass: 'bg-purple-500 text-white',
style: ' border-radius: 12px'
},
{
label: 'Marketing',
styleClass: 'bg-purple-500 text-white',
style: ' border-radius: 12px'
}
]
},
{
expanded: true,
type: 'person',
styleClass: 'bg-teal-500 text-white',
data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png',
name: 'Stephen Shaw',
title: 'CTO'
},
children: [
{
label: 'Development',
styleClass: 'bg-teal-500 text-white'
},
{
label: 'UI/UX Design',
styleClass: 'bg-teal-500 text-white'
}
]
}
]
}
];
this.familyTree.set(data);
}
loadPersonFamilyTree(id: number): void {
const relationShip$ = this.personService.loadPersonFamily(id);
this.subscription.add(relationShip$.subscribe(
{
next: x => {
if (x.statusCode == 1)
{
let tree : TreeNode[] =[];
tree.push(x.data);
this.familyTree.set(tree);
console.log("load person family", this.familyTree());
}
},
error: e => {
console.error("error load loadPersonFamily by personId", id);
this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message});
}
}
));
}
nodeSelect(event: TreeNodeSelectEvent) {
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
}
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
// console.log("double click node", event.node.key);
const id = event.node.key;
this.edit(Number(id));
}
getName(id:number): string
{
let result ="";
const item = this.familyList.find(x => x.id == id);
if (item)
result = item.lastName + " " + item.firstName;
return result;
}
updateParent(list:Person[]):void {
let i = 0;
let item:Person;
for (i = 0; i< list.length; i++)
{
item = list[i];
if (item.fatherId && item.fatherId > 0)
{
item.fatherName = this.getName(item.fatherId);
}
}
}
newFamily():void {
//console.log("add new employee");
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--);
}
edit(id: number) : void {
//console.log("edit family", id);
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id);
}
showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, {
data: {
id,
familyList: this.familyList,
},
header: 'Family',
width: '80%',
maximizable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
this.updateList(item);
}
});
}
}
updateList(item: Person) :void {
const idx = this.familyList.findIndex( x => x.id == item.id);
if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId);
if (idx < 0)
{
const olist = [... this.familyList, item];
this.familyList = olist;
}
else
{
const oitem = this.familyList[idx];
oitem.firstName = item.firstName;
oitem.lastName = item.lastName;
oitem.address = item.address;
oitem.alive = item.alive;
oitem.dob = item.dob;
oitem.email = item.email;
oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId;
oitem.motherName = item.motherName;
oitem.fatherName = item.fatherName;
}
}
deleteItem(id: number): void {
this.personService.deletePerson(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList.filter(d => d.id !== id);
this.familyList = nlist;
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
close() :void {
this.ref.close(null);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { PersonService } from './person.service';
import { TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { TreeNode } from 'primeng/api';
import { InputTextModule } from 'primeng/inputtext';
import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
import { Utils } from '../shares';
import { PopoverModule } from 'primeng/popover';
@Component({
selector: 'family-orga',
templateUrl: './family.orga.html',
imports:[TableModule,FormsModule,TreeModule,PopoverModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
styleUrls: ['./family.orga.css'],
providers: [DialogService]
})
export class FamilyOrga implements OnInit, OnDestroy{
private subscription:Subscription = new Subscription();
person!: Person;
selectedNode!: TreeNode;
selectedNodes!: TreeNode[];
private cd = inject(ChangeDetectorRef);
familyTree = signal<TreeNode[]>([]);
relations: RelationShip[] =[];
_id = -10;
loading = false;
familyList:Person[] = [];
msg ="[Person organise component]";
private messageService = inject(MessageService);
public ref = inject(DynamicDialogRef);
public config = inject(DynamicDialogConfig);
/*
private store: Store<appValidationState>
*/
private personService = inject(PersonService);
public dialogService = inject(DialogService);
ngOnInit(): void
{
const id = this.config.data.id;
this.familyList = this.config.data.familyList;
const item = this.familyList.find(x => x.id == id);
if (item != undefined)
this.person = item;
this.loadPersonFamilyTree(id);
//this.populateTree();
}
populateTree() : void {
const data: TreeNode[] = [
{
expanded: true,
type: 'person',
styleClass: 'bg-indigo-500 text-white',
data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png',
name: 'Amy Elsner',
title: 'CEO'
},
children: [
{
expanded: true,
type: 'person',
styleClass: 'bg-purple-500 text-white',
data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png',
name: 'Anna Fali',
title: 'CMO'
},
children: [
{
label: 'Sales',
styleClass: 'bg-purple-500 text-white',
style: ' border-radius: 12px'
},
{
label: 'Marketing',
styleClass: 'bg-purple-500 text-white',
style: ' border-radius: 12px'
}
]
},
{
expanded: true,
type: 'person',
styleClass: 'bg-teal-500 text-white',
data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png',
name: 'Stephen Shaw',
title: 'CTO'
},
children: [
{
label: 'Development',
styleClass: 'bg-teal-500 text-white'
},
{
label: 'UI/UX Design',
styleClass: 'bg-teal-500 text-white'
}
]
}
]
}
];
this.familyTree.set(data);
}
loadPersonFamilyTree(id: number): void {
const relationShip$ = this.personService.loadPersonFamily(id);
this.subscription.add(relationShip$.subscribe(
{
next: x => {
if (x.statusCode == 1)
{
let tree : TreeNode[] =[];
tree.push(x.data);
this.familyTree.set(tree);
console.log("load person family", this.familyTree());
}
},
error: e => {
console.error("error load loadPersonFamily by personId", id);
this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message});
}
}
));
}
nodeSelect(event: TreeNodeSelectEvent) {
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
}
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
// console.log("double click node", event.node.key);
const id = event.node.key;
this.edit(Number(id));
}
getName(id:number): string
{
let result ="";
const item = this.familyList.find(x => x.id == id);
if (item)
result = item.lastName + " " + item.firstName;
return result;
}
updateParent(list:Person[]):void {
let i = 0;
let item:Person;
for (i = 0; i< list.length; i++)
{
item = list[i];
if (item.fatherId && item.fatherId > 0)
{
item.fatherName = this.getName(item.fatherId);
}
}
}
newFamily():void {
//console.log("add new employee");
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--);
}
edit(id: number) : void {
//console.log("edit family", id);
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id);
}
showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, {
data: {
id,
familyList: this.familyList,
},
header: 'Family',
width: '80%',
maximizable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
this.updateList(item);
}
});
}
}
updateList(item: Person) :void {
const idx = this.familyList.findIndex( x => x.id == item.id);
if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId);
if (idx < 0)
{
const olist = [... this.familyList, item];
this.familyList = olist;
}
else
{
const oitem = this.familyList[idx];
oitem.firstName = item.firstName;
oitem.lastName = item.lastName;
oitem.address = item.address;
oitem.alive = item.alive;
oitem.dob = item.dob;
oitem.email = item.email;
oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId;
oitem.motherName = item.motherName;
oitem.fatherName = item.fatherName;
}
}
deleteItem(id: number): void {
this.personService.deletePerson(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList.filter(d => d.id !== id);
this.familyList = nlist;
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
close() :void {
this.ref.close(null);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+12 -12
View File
@@ -1,12 +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;
}
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
td.mat-column-edit, .mat-column-delete {
width: 35px;
padding-right: 2px;
}
+35 -35
View File
@@ -1,35 +1,35 @@
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Family Tree by Person has Father</h3>
<div>
<form>
<div class="grid grid-cols-12 gap-2">
<div class="flex items-center">
<p-checkbox inputId="isFather" [binary]="true" name="isFather" [(ngModel)]="isFather" />
<label for="isFather" class="ml-2"> Is Father </label>
</div>
<div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-sitemap" label="Load"
(click)="load()"></button>
</div>
</div>
</form>
</div>
<div>
<p-tree [value]="familyTree" class="w-full md:w-[30rem]"
(onNodeDoubleClick)="nodeDoubleSelect($event)"
selectionMode="single" [(selection)]="selectedNode" (onNodeSelect)="nodeSelect($event)" />
<!--div class="card flex justify-center overflow-x-auto">
<p-organization-chart [value]="familyTree" selectionMode="multiple" [(selection)]="selectedNodes" [collapsible]="true">
<ng-template let-node pTemplate="person">
<div class="flex flex-col">
<div class="flex flex-col items-center">
<img [src]="node.data.image" class="mb-4 w-12 h-12" />
<div class="font-bold mb-2">{{ node.data.name }}</div>
<div>{{ node.data.title }}</div>
</div>
</div>
</ng-template>
</p-organization-chart>
</div-->
</div>
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Family Tree by Person has Father</h3>
<div>
<form>
<div class="grid grid-cols-12 gap-2">
<div class="flex items-center">
<p-checkbox inputId="isFather" [binary]="true" name="isFather" [(ngModel)]="isFather" />
<label for="isFather" class="ml-2"> Is Father </label>
</div>
<div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-sitemap" label="Load"
(click)="load()"></button>
</div>
</div>
</form>
</div>
<div>
<p-tree [value]="familyTree" class="w-full md:w-[30rem]"
(onNodeDoubleClick)="nodeDoubleSelect($event)"
selectionMode="single" [(selection)]="selectedNode" (onNodeSelect)="nodeSelect($event)" />
<!--div class="card flex justify-center overflow-x-auto">
<p-organization-chart [value]="familyTree" selectionMode="multiple" [(selection)]="selectedNodes" [collapsible]="true">
<ng-template let-node pTemplate="person">
<div class="flex flex-col">
<div class="flex flex-col items-center">
<img [src]="node.data.image" class="mb-4 w-12 h-12" />
<div class="font-bold mb-2">{{ node.data.name }}</div>
<div>{{ node.data.title }}</div>
</div>
</div>
</ng-template>
</p-organization-chart>
</div-->
</div>
+227 -227
View File
@@ -1,227 +1,227 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { TreeNode } from 'primeng/api';
import { InputTextModule } from 'primeng/inputtext';
import { DialogService } from 'primeng/dynamicdialog';
import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api';
import { CheckboxModule } from 'primeng/checkbox';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
import { Utils } from '../shares';
@Component({
selector: 'family-tree',
templateUrl: './family.tree.html',
imports:[TableModule,FormsModule,TreeModule,CheckboxModule,
OrganizationChartModule,CommonModule,
FormsModule,
ButtonModule,InputTextModule],
styleUrls: ['./family.tree.css'],
providers: [DialogService]
})
export class FamilyTree implements OnInit, OnDestroy{
isFather = signal(true);
private subscription:Subscription = new Subscription();
firstname = '';
selectedNode!: TreeNode;
private cd = inject(ChangeDetectorRef);
familyTree: TreeNode[] = [];
email = '';
lastname = '';
_id = -10;
loading = false;
familyList:Person[] = [];
msg ="[Person tree component]";
private messageService = inject(MessageService);
/*
private store: Store<appValidationState>
*/
constructor(
public dialogService: DialogService,
private personService: PersonService,
private authenticationService: AuthenticationService,
private router: Router
) {}
getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = {
email: this.email,
firstName: this.firstname,
lastName: this.lastname,
};
return criteria;
}
canSearch():boolean {
let result = false;
result = this.email !== "";
result = result || this.lastname !== "";
result = result || this.firstname !== "";
return result;
}
ngOnInit(): void
{
this.authenticationService.isHome = false;
this.authenticationService.isReport = false;
const prev = this.personService.searchCriteria;
let goload = true;
this.load();
}
getActive(active:boolean):string {
let result = 'false-icon pi-times-circle';
if (active)
result = 'true-icon pi-check-circle';
return result;
}
load():void {
let father = this.isFather();
this.loading = true;
//const criteria = this.getSearchCiteria();
// this.personService.searchCriteria = criteria;
this.subscription.add(
this.personService.loadPersonFamilyTree(father, !father).subscribe(
{
next: x => {
if (x.statusCode == 1)
{
this.familyTree = x.data;
this.loading = false;
this.cd.detectChanges();
console.log("the family tree", this.familyTree);
}
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
this.loading = false;
console.log("error ", e);
}
})
);
}
nodeSelect(event: TreeNodeSelectEvent) {
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
}
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
// console.log("double click node", event.node.key);
const id = event.node.key;
this.edit(Number(id));
}
getName(id:number): string
{
let result ="";
const item = this.familyList.find(x => x.id == id);
if (item)
result = item.lastName + " " + item.firstName;
return result;
}
updateParent(list:Person[]):void {
let i = 0;
let item:Person;
for (i = 0; i< list.length; i++)
{
item = list[i];
if (item.fatherId && item.fatherId > 0)
{
item.fatherName = this.getName(item.fatherId);
}
}
}
newFamily():void {
//console.log("add new employee");
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--);
}
edit(id: number) : void {
//console.log("edit family", id);
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id);
}
showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, {
data: {
id,
familyList: this.familyList,
},
header: 'Family',
width: '80%',
maximizable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
this.updateList(item);
}
});
}
}
updateList(item: Person) :void {
const idx = this.familyList.findIndex( x => x.id == item.id);
if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId);
if (idx < 0)
{
const olist = [... this.familyList, item];
this.familyList = olist;
}
else
{
const oitem = this.familyList[idx];
oitem.firstName = item.firstName;
oitem.lastName = item.lastName;
oitem.address = item.address;
oitem.alive = item.alive;
oitem.dob = item.dob;
oitem.email = item.email;
oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId;
oitem.motherName = item.motherName;
oitem.fatherName = item.fatherName;
}
}
deleteItem(id: number): void {
this.personService.deletePerson(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList.filter(d => d.id !== id);
this.familyList = nlist;
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { TreeNode } from 'primeng/api';
import { InputTextModule } from 'primeng/inputtext';
import { DialogService } from 'primeng/dynamicdialog';
import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api';
import { CheckboxModule } from 'primeng/checkbox';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
import { Utils } from '../shares';
@Component({
selector: 'family-tree',
templateUrl: './family.tree.html',
imports:[TableModule,FormsModule,TreeModule,CheckboxModule,
OrganizationChartModule,CommonModule,
FormsModule,
ButtonModule,InputTextModule],
styleUrls: ['./family.tree.css'],
providers: [DialogService]
})
export class FamilyTree implements OnInit, OnDestroy{
isFather = signal(true);
private subscription:Subscription = new Subscription();
firstname = '';
selectedNode!: TreeNode;
private cd = inject(ChangeDetectorRef);
familyTree: TreeNode[] = [];
email = '';
lastname = '';
_id = -10;
loading = false;
familyList:Person[] = [];
msg ="[Person tree component]";
private messageService = inject(MessageService);
/*
private store: Store<appValidationState>
*/
constructor(
public dialogService: DialogService,
private personService: PersonService,
private authenticationService: AuthenticationService,
private router: Router
) {}
getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = {
email: this.email,
firstName: this.firstname,
lastName: this.lastname,
};
return criteria;
}
canSearch():boolean {
let result = false;
result = this.email !== "";
result = result || this.lastname !== "";
result = result || this.firstname !== "";
return result;
}
ngOnInit(): void
{
this.authenticationService.isHome = false;
this.authenticationService.isReport = false;
const prev = this.personService.searchCriteria;
let goload = true;
this.load();
}
getActive(active:boolean):string {
let result = 'false-icon pi-times-circle';
if (active)
result = 'true-icon pi-check-circle';
return result;
}
load():void {
let father = this.isFather();
this.loading = true;
//const criteria = this.getSearchCiteria();
// this.personService.searchCriteria = criteria;
this.subscription.add(
this.personService.loadPersonFamilyTree(father, !father).subscribe(
{
next: x => {
if (x.statusCode == 1)
{
this.familyTree = x.data;
this.loading = false;
this.cd.detectChanges();
console.log("the family tree", this.familyTree);
}
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
this.loading = false;
console.log("error ", e);
}
})
);
}
nodeSelect(event: TreeNodeSelectEvent) {
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
}
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
// console.log("double click node", event.node.key);
const id = event.node.key;
this.edit(Number(id));
}
getName(id:number): string
{
let result ="";
const item = this.familyList.find(x => x.id == id);
if (item)
result = item.lastName + " " + item.firstName;
return result;
}
updateParent(list:Person[]):void {
let i = 0;
let item:Person;
for (i = 0; i< list.length; i++)
{
item = list[i];
if (item.fatherId && item.fatherId > 0)
{
item.fatherName = this.getName(item.fatherId);
}
}
}
newFamily():void {
//console.log("add new employee");
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--);
}
edit(id: number) : void {
//console.log("edit family", id);
this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id);
}
showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, {
data: {
id,
familyList: this.familyList,
},
header: 'Family',
width: '80%',
maximizable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
this.updateList(item);
}
});
}
}
updateList(item: Person) :void {
const idx = this.familyList.findIndex( x => x.id == item.id);
if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId);
if (idx < 0)
{
const olist = [... this.familyList, item];
this.familyList = olist;
}
else
{
const oitem = this.familyList[idx];
oitem.firstName = item.firstName;
oitem.lastName = item.lastName;
oitem.address = item.address;
oitem.alive = item.alive;
oitem.dob = item.dob;
oitem.email = item.email;
oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId;
oitem.motherName = item.motherName;
oitem.fatherName = item.fatherName;
}
}
deleteItem(id: number): void {
this.personService.deletePerson(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList.filter(d => d.id !== id);
this.familyList = nlist;
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+12 -12
View File
@@ -1,12 +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;
}
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
td.mat-column-edit, .mat-column-delete {
width: 35px;
padding-right: 2px;
}
+86 -86
View File
@@ -1,86 +1,86 @@
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Person List</h3>
<div>
<form>
<div class="grid md:grid-cols-4 gap-2">
<div class="">
<label for="login">Email</label>
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="lastname1">Surname</label>
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="firstname">First Name</label>
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
(click)="search()"></button>
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" label="New Person"
(click)="newFamily()"></button>
</div>
</div>
</form>
</div>
<div>
<p-table [value]="familyList()" sortMode="multiple" dataKey="id" class="p-datatable-sm" #dt
class="p-datatable-sm" selectionMode="single" [(selection)]="selectedPerson"
[paginator]="true" rowHover="true" [globalFilterFields]="['lastName', 'firstName', 'email', 'fatherName', 'motherName']"
[rows]="20" [rowsPerPageOptions]="[10,20,50,100]"
[loading]="loading">
<ng-template #caption>
<div class="flex">
<p-button icon="pi pi-external-link" label="Export" (click)="exportExport()"
/>
<p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon>
<i class="pi pi-search"></i>
</p-inputicon>
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
</p-iconfield>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
</th>
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
</th>
<th pSortableColumn="sex">Sex <p-sortIcon field="sex"></p-sortIcon></th>
<th pSortableColumn="fatherName">Father<p-sortIcon field="fatherName"></p-sortIcon>
</th>
<th pSortableColumn="motherName">Mother<p-sortIcon field="motherName"></p-sortIcon>
</th>
<th>Edit</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr>
<td>{{user.lastName}}</td>
<td>{{user.firstName}}</td>
<td>{{user.sex}}</td>
<td>{{user.fatherName}}</td>
<td>{{user.motherName}}</td>
<td>
<p-button type="button" icon="pi pi-ellipsis-v"
[rounded]="true" [text]="true" [raised]="true"
class="p-button-rounded p-button-text"
(onClick)="actionClick(user.id,$event)"></p-button>
</td>
</tr>
</ng-template>
</p-table>
<p-menu #rowmenu [model]="items" [popup]="true" (onShow)="onMenuShow()" />
</div>
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Person List</h3>
<div>
<form>
<div class="grid md:grid-cols-4 gap-2">
<div class="">
<label for="login">Email</label>
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="lastname1">Surname</label>
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="firstname">First Name</label>
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
(click)="search()"></button>
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" label="New Person"
(click)="newFamily()"></button>
</div>
</div>
</form>
</div>
<div>
<p-table [value]="familyList()" sortMode="multiple" dataKey="id" class="p-datatable-sm" #dt
class="p-datatable-sm" selectionMode="single" [(selection)]="selectedPerson"
[paginator]="true" rowHover="true" [globalFilterFields]="['lastName', 'firstName', 'email', 'fatherName', 'motherName']"
[rows]="20" [rowsPerPageOptions]="[10,20,50,100]"
[loading]="loading">
<ng-template #caption>
<div class="flex">
<p-button icon="pi pi-external-link" label="Export" (click)="exportExport()"
/>
<p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon>
<i class="pi pi-search"></i>
</p-inputicon>
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
</p-iconfield>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
</th>
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
</th>
<th pSortableColumn="sex">Sex <p-sortIcon field="sex"></p-sortIcon></th>
<th pSortableColumn="fatherName">Father<p-sortIcon field="fatherName"></p-sortIcon>
</th>
<th pSortableColumn="motherName">Mother<p-sortIcon field="motherName"></p-sortIcon>
</th>
<th>Edit</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr>
<td>{{user.lastName}}</td>
<td>{{user.firstName}}</td>
<td>{{user.sex}}</td>
<td>{{user.fatherName}}</td>
<td>{{user.motherName}}</td>
<td>
<p-button type="button" icon="pi pi-ellipsis-v"
[rounded]="true" [text]="true" [raised]="true"
class="p-button-rounded p-button-text"
(onClick)="actionClick(user.id,$event)"></p-button>
</td>
</tr>
</ng-template>
</p-table>
<p-menu #rowmenu [model]="items" [popup]="true" (onShow)="onMenuShow()" />
</div>
+359 -359
View File
@@ -1,359 +1,359 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services';
import { Table, TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { DialogService } from 'primeng/dynamicdialog';
import { PersonEdit } from './person.edit';
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
import { Menu, MenuModule } from 'primeng/menu';
import { FamilyOrga } from './family.orga';
import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx';
@Component({
selector: 'family-list',
templateUrl: './familylist.html',
imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule,
InputTextModule,IconFieldModule,InputIconModule],
styleUrls: ['./familylist.css'],
providers: [DialogService]
})
export class FamilyList implements OnInit, OnDestroy{
private subscription:Subscription = new Subscription();
//private cd = inject(ChangeDetectorRef);
items: MenuItem[] | undefined;
selectedPerson!: Person;
firstname = '';
email = '';
lastname = '';
_id = -10;
selectId = -1;
loading = false;
familyList = signal<Person[]>([]);
msg ="[Person component]";
@ViewChild(Table) dt2!: Table;
@ViewChild('rowmenu') popMenu?: Menu;
private messageService = inject(MessageService);
public dialogService= inject( DialogService);
private personService= inject( PersonService);
private confirmationService = inject(ConfirmationService);
private cdr = inject(ChangeDetectorRef);
private authenticationService= inject( AuthenticationService);
private router= inject( Router);
getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = {
email: this.email,
firstName: this.firstname,
lastName: this.lastname,
};
console.log("get search citeria", criteria);
return criteria;
}
initMenu(): void {
this.items = [
{
label: 'Edit',
icon: 'pi pi-pencil',
command: () => {
this.edit(this.selectId);
}
},
{
label: 'Delete',
iconClass:'text-red-500',
styleClass:'text-red-500',
icon: 'pi pi-times',
command: () => {
this.delete(this.selectId);
}
},
{
label: 'Show Family',
icon: 'pi pi-sitemap',
command: () => {
this.showChildren(this.selectId);
}
}
];
}
exportExport() : void {
this.ExcelExport(this.familyList(), 'family_export');
}
ExcelExport(data: any, fileName:string) :void
{
const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
saveAs(blob, `${fileName}.xlsx`);
}
actionClick(id: number, event:Event): void {
// console.log("action edit "+ id);
this.selectId = id;
this.popMenu!.toggle(event);
}
showChildren(id: number): void {
console.log("show children of id", id);
this.showOrganise(id);
}
handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.dt2.filterGlobal(value, 'contains');
}
canSearch():boolean {
let result = false;
result = this.email !== "";
result = result || this.lastname !== "";
result = result || this.firstname !== "";
return result;
}
onMenuShow(): void {
console.log("this is show", this.selectedPerson);
}
ngOnInit(): void
{
this.initMenu();
this.authenticationService.isHome = false;
this.authenticationService.isReport = false;
const prev = this.personService.searchCriteria;
let goload = true;
if (prev.lastName !== '')
{
this.lastname = prev.lastName;
goload = true;
}
if (prev.firstName !== '')
{
this.firstname = prev.firstName;
goload = true;
}
if (prev.email !== '')
{
this.email = prev.email;
goload = true;
}
if (goload)
{
this.search();
}
}
getActive(active:boolean):string {
let result = 'false-icon pi-times-circle';
if (active)
result = 'true-icon pi-check-circle';
return result;
}
search():void {
const canSearch = true; // this.canSearch();
if (canSearch)
{
this.loading = true;
const criteria = this.getSearchCiteria();
this.personService.searchCriteria = criteria;
this.subscription.add(
this.personService.searchPersons(criteria).subscribe( {
next: result => {
// console.log(this.msg + "search load Data", result);
const familyList = result.data;
this.familyList.set(familyList);
this.updateParent( this.familyList());
//this.familyList.set(familyList);
console.log("the person from load", this.familyList());
this.loading = false;
this.cdr.detectChanges();
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
this.loading = false;
console.log("error ", e);
}
})
);
}
}
getName(id:number): string
{
let result ="";
const item = this.familyList().find(x => x.id == id);
if (item)
result = item.lastName + " " + item.firstName;
return result;
}
updateParent(list:Person[]):void {
let i = 0;
let item:Person;
for (i = 0; i< list.length; i++)
{
item = list[i];
if (item.fatherId && item.fatherId > 0)
{
item.fatherName = this.getName(item.fatherId);
}
if (item.motherId && item.motherId > 0)
{
item.motherName = this.getName(item.motherId);
}
}
}
delete(id: number): void {
this.confirmationService.confirm({
message: 'Do you want to delete this record?',
header: 'Confirmation Delete',
icon: 'pi pi-info-circle',
rejectLabel: 'Cancel',
rejectButtonProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true,
},
acceptButtonProps: {
label: 'Delete',
severity: 'danger',
},
accept: () => {
this.deleteItem(id);
}
});
}
deleteItem(id: number): void {
this.personService.deletePerson(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList().filter(d => d.id !== id);
this.familyList.set(nlist);
this.cdr.detectChanges();
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
newFamily():void {
//console.log("add new employee");
this.personService.parentList = this.familyList();
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--);
}
edit(id: number) : void {
//console.log("edit family", id);
this.personService.parentList = this.familyList();
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id);
}
showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, {
data: {
id,
familyList: this.familyList(),
},
header: 'Person',
width: '80%',
draggable: true,
maximizable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
this.updateList(item);
}
});
}
}
showOrganise(id:number) {
const ref = this.dialogService.open(FamilyOrga, {
data: {
id,
familyList: this.familyList(),
},
header: 'Family Tree and Children',
width: '80%',
maximizable: true,
closable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
//this.updateList(item);
}
});
}
}
updateList(item: Person) :void {
const list = this.familyList();
const idx = list.findIndex( x => x.id === item.id);
if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId);
if (idx < 0)
{
const olist = [... list, item];
this.familyList.set(olist);
}
else
{
const oitem = list[idx];
oitem.firstName = item.firstName;
oitem.lastName = item.lastName;
oitem.address = item.address;
oitem.alive = item.alive;
oitem.dob = item.dob;
oitem.sex = item.sex;
oitem.email = item.email;
oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId;
oitem.fatherName = item.fatherName;
oitem.motherName = item.motherName;
this.familyList.set(list);
}
this.cdr.markForCheck();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services';
import { Table, TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { DialogService } from 'primeng/dynamicdialog';
import { PersonEdit } from './person.edit';
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
import { Menu, MenuModule } from 'primeng/menu';
import { FamilyOrga } from './family.orga';
import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx';
@Component({
selector: 'family-list',
templateUrl: './familylist.html',
imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule,
InputTextModule,IconFieldModule,InputIconModule],
styleUrls: ['./familylist.css'],
providers: [DialogService]
})
export class FamilyList implements OnInit, OnDestroy{
private subscription:Subscription = new Subscription();
//private cd = inject(ChangeDetectorRef);
items: MenuItem[] | undefined;
selectedPerson!: Person;
firstname = '';
email = '';
lastname = '';
_id = -10;
selectId = -1;
loading = false;
familyList = signal<Person[]>([]);
msg ="[Person component]";
@ViewChild(Table) dt2!: Table;
@ViewChild('rowmenu') popMenu?: Menu;
private messageService = inject(MessageService);
public dialogService= inject( DialogService);
private personService= inject( PersonService);
private confirmationService = inject(ConfirmationService);
private cdr = inject(ChangeDetectorRef);
private authenticationService= inject( AuthenticationService);
private router= inject( Router);
getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = {
email: this.email,
firstName: this.firstname,
lastName: this.lastname,
};
console.log("get search citeria", criteria);
return criteria;
}
initMenu(): void {
this.items = [
{
label: 'Edit',
icon: 'pi pi-pencil',
command: () => {
this.edit(this.selectId);
}
},
{
label: 'Delete',
iconClass:'text-red-500',
styleClass:'text-red-500',
icon: 'pi pi-times',
command: () => {
this.delete(this.selectId);
}
},
{
label: 'Show Family',
icon: 'pi pi-sitemap',
command: () => {
this.showChildren(this.selectId);
}
}
];
}
exportExport() : void {
this.ExcelExport(this.familyList(), 'family_export');
}
ExcelExport(data: any, fileName:string) :void
{
const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
saveAs(blob, `${fileName}.xlsx`);
}
actionClick(id: number, event:Event): void {
// console.log("action edit "+ id);
this.selectId = id;
this.popMenu!.toggle(event);
}
showChildren(id: number): void {
console.log("show children of id", id);
this.showOrganise(id);
}
handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.dt2.filterGlobal(value, 'contains');
}
canSearch():boolean {
let result = false;
result = this.email !== "";
result = result || this.lastname !== "";
result = result || this.firstname !== "";
return result;
}
onMenuShow(): void {
console.log("this is show", this.selectedPerson);
}
ngOnInit(): void
{
this.initMenu();
this.authenticationService.isHome = false;
this.authenticationService.isReport = false;
const prev = this.personService.searchCriteria;
let goload = true;
if (prev.lastName !== '')
{
this.lastname = prev.lastName;
goload = true;
}
if (prev.firstName !== '')
{
this.firstname = prev.firstName;
goload = true;
}
if (prev.email !== '')
{
this.email = prev.email;
goload = true;
}
if (goload)
{
this.search();
}
}
getActive(active:boolean):string {
let result = 'false-icon pi-times-circle';
if (active)
result = 'true-icon pi-check-circle';
return result;
}
search():void {
const canSearch = true; // this.canSearch();
if (canSearch)
{
this.loading = true;
const criteria = this.getSearchCiteria();
this.personService.searchCriteria = criteria;
this.subscription.add(
this.personService.searchPersons(criteria).subscribe( {
next: result => {
// console.log(this.msg + "search load Data", result);
const familyList = result.data;
this.familyList.set(familyList);
this.updateParent( this.familyList());
//this.familyList.set(familyList);
console.log("the person from load", this.familyList());
this.loading = false;
this.cdr.detectChanges();
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
this.loading = false;
console.log("error ", e);
}
})
);
}
}
getName(id:number): string
{
let result ="";
const item = this.familyList().find(x => x.id == id);
if (item)
result = item.lastName + " " + item.firstName;
return result;
}
updateParent(list:Person[]):void {
let i = 0;
let item:Person;
for (i = 0; i< list.length; i++)
{
item = list[i];
if (item.fatherId && item.fatherId > 0)
{
item.fatherName = this.getName(item.fatherId);
}
if (item.motherId && item.motherId > 0)
{
item.motherName = this.getName(item.motherId);
}
}
}
delete(id: number): void {
this.confirmationService.confirm({
message: 'Do you want to delete this record?',
header: 'Confirmation Delete',
icon: 'pi pi-info-circle',
rejectLabel: 'Cancel',
rejectButtonProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true,
},
acceptButtonProps: {
label: 'Delete',
severity: 'danger',
},
accept: () => {
this.deleteItem(id);
}
});
}
deleteItem(id: number): void {
this.personService.deletePerson(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList().filter(d => d.id !== id);
this.familyList.set(nlist);
this.cdr.detectChanges();
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
newFamily():void {
//console.log("add new employee");
this.personService.parentList = this.familyList();
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--);
}
edit(id: number) : void {
//console.log("edit family", id);
this.personService.parentList = this.familyList();
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id);
}
showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, {
data: {
id,
familyList: this.familyList(),
},
header: 'Person',
width: '80%',
draggable: true,
maximizable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
this.updateList(item);
}
});
}
}
showOrganise(id:number) {
const ref = this.dialogService.open(FamilyOrga, {
data: {
id,
familyList: this.familyList(),
},
header: 'Family Tree and Children',
width: '80%',
maximizable: true,
closable: true
});
if (ref)
{
ref.onClose.subscribe((item: Person) => {
if (item) {
//console.log("after close ward edit", item);
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list
//this.updateList(item);
}
});
}
}
updateList(item: Person) :void {
const list = this.familyList();
const idx = list.findIndex( x => x.id === item.id);
if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId);
if (idx < 0)
{
const olist = [... list, item];
this.familyList.set(olist);
}
else
{
const oitem = list[idx];
oitem.firstName = item.firstName;
oitem.lastName = item.lastName;
oitem.address = item.address;
oitem.alive = item.alive;
oitem.dob = item.dob;
oitem.sex = item.sex;
oitem.email = item.email;
oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId;
oitem.fatherName = item.fatherName;
oitem.motherName = item.motherName;
this.familyList.set(list);
}
this.cdr.markForCheck();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+5 -5
View File
@@ -1,5 +1,5 @@
export * from './familylist';
export * from './person.edit';
export * from './family.tree';
export * from './person.service';
export * from './familylist';
export * from './person.edit';
export * from './family.tree';
export * from './person.service';
+12 -12
View File
@@ -1,13 +1,13 @@
.profilePhotoBorder
{
border-color: gray;
border-width: 2px;
border-radius: 15%;
}
.profilePhotoWH
{
width: 150px;
height: 100px;
.profilePhotoBorder
{
border-color: gray;
border-width: 2px;
border-radius: 15%;
}
.profilePhotoWH
{
width: 150px;
height: 100px;
}
+133 -133
View File
@@ -1,134 +1,134 @@
<div class=" mt-2">
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit($event)" >
<div class="ml-2 grid md:grid-cols-2 gap-3 p-2">
<div>
<div class="">
<label for="lastname1">Surname<strong class="app-require">*</strong></label>
<input id="lastname1" pInputText formControlName="lastname" type="text" [pAutoFocus]="true"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
</div>
<div class="">
<label for="firstname">First Name<strong class="app-require">*</strong></label>
<input id="firstname" pInputText formControlName="firstname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
</div>
</div>
<!--put here photo of person-->
<div class="flex flex-row gap-2">
<div>
<div class="">
<label for="dob" class="flex w-full">DOB</label>
<p-datepicker ariaLabelledBy="dob" formControlName="dob" [iconDisplay]="'input'" [showIcon]="true"
dateFormat="dd/mm/yy" ></p-datepicker>
</div>
<div class="">
<label class="flex w-full">Sex <strong class="app-require">*</strong></label>
<p-select [options]="sexList" optionLabel="name" optionValue="status" placeholder="Select Gender"
formControlName="sex"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','sex')"
class="w-full p-inputtext-sm mr-1"></p-select >
</div>
</div>
<div class="flex flex-row shadow-md rounded-xl border-gray-100 border-1 p-4">
<!--a href="{{hostsite}}/{{adminuserForm.value.image}}" target="_blank" class="text-blue-400">View Attachment
</a-->
<input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload>
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
{
<img id="blah" [src]= "dislayImage()" alt="your image"
class="profilePhotoWH profilePhotoBorder" (click)="doViewImage(adminuserForm.value.image)" />
}
@else
{
<div class="file-upload profilePhotoBorder profilePhotoWH text-center flex justify-center items-center ">
<label class="">{{ adminuserForm.value.image || "No Profile Photo."}}</label>
</div>
}
<div class="flex flex-col gap-2">
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
{
<button pButton type="button" icon="pi pi-times" pTooltip="remove profile photo"
class="p-button-rounded p-button-text text-red-500 p-button-raised ml-2"
(click)="deleteFile()"></button>
}
<button pButton type="button" icon="pi pi-paperclip"
class="self-end p-button-rounded p-button-text p-button-raised ml-2" pTooltip="load photo"
(click)="fileUpload.click()"></button>
</div>
</div>
</div>
<!---->
<div class="">
<label for="login">Email</label>
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
type="text" class="inputfield w-full">
</div>
<div class="">
<label for="phone" class="w-full">Phone</label>
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
</div>
<div class="">
<label class="flex w-full">Father</label>
<p-select [filter]="true" [showClear]="true" filterBy="name" [options]="fatherList" optionLabel="name" appendTo="body" optionValue="id" placeholder="Select Parent"
formControlName="fatherId" class="w-full p-inputtext-sm mr-1"></p-select >
</div>
<div class="">
<label class="flex w-full">Mother</label>
<p-select [options]="motherList" [showClear]="true" [filter]="true" filterBy="name" optionLabel="name" appendTo="body" optionValue="id" placeholder="Select Parent"
formControlName="motherId" class="w-full p-inputtext-sm mr-1"></p-select >
</div>
<div class="grid align-end">
<div class="p-field-checkbox">
<p-checkbox inputId="alive" formControlName="alive" [binary]="true"></p-checkbox>
<label for="alive" class="ml-2 w-full">Alive</label>
</div>
</div>
</div>
<div class="mt-2 w-full">
<div class="flex flex-row justify-between">
<p-button icon="pi pi-images" [raised]="true"
badge="{{photoList.length}}"
severity="success" label="Attach Photos"
(onClick)="viewAttachment()"/>
<p-button icon="pi pi-user" [raised]="true" severity="info" label="Add Partner"
(onClick)="addPartner()"/>
</div>
<div class="shadow rounded mt-2 mb-2">
<p-table [value]="partners()">
<ng-template #header>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Sex</th>
<th>Action</th>
</tr>
</ng-template>
<ng-template #body let-user>
<tr>
<td>{{ user.pfirstName }}</td>
<td>{{ user.plastName }}</td>
<td>{{ user.sex }}</td>
<td>
<button pButton type="button" icon="pi pi-times" class="p-button-rounded p-button-text p-button-danger"
(click)="onDeletePartner(user)"></button>
</td>
</tr>
</ng-template>
</p-table>
</div>
</div>
<div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" label="Save" [disabled]="isFieldsChange"></button>
<button pButton pRipple class="p-button-sm p-button-secondary" type="button" icon="pi pi-times" label="Cancel" (click)="cancel($event)"></button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
<div class=" mt-2">
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit($event)" >
<div class="ml-2 grid md:grid-cols-2 gap-3 p-2">
<div>
<div class="">
<label for="lastname1">Surname<strong class="app-require">*</strong></label>
<input id="lastname1" pInputText formControlName="lastname" type="text" [pAutoFocus]="true"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
</div>
<div class="">
<label for="firstname">First Name<strong class="app-require">*</strong></label>
<input id="firstname" pInputText formControlName="firstname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
</div>
</div>
<!--put here photo of person-->
<div class="flex flex-row gap-2">
<div>
<div class="">
<label for="dob" class="flex w-full">DOB</label>
<p-datepicker ariaLabelledBy="dob" formControlName="dob" [iconDisplay]="'input'" [showIcon]="true"
dateFormat="dd/mm/yy" ></p-datepicker>
</div>
<div class="">
<label class="flex w-full">Sex <strong class="app-require">*</strong></label>
<p-select [options]="sexList" optionLabel="name" optionValue="status" placeholder="Select Gender"
formControlName="sex"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','sex')"
class="w-full p-inputtext-sm mr-1"></p-select >
</div>
</div>
<div class="flex flex-row shadow-md rounded-xl border-gray-100 border-1 p-4">
<!--a href="{{hostsite}}/{{adminuserForm.value.image}}" target="_blank" class="text-blue-400">View Attachment
</a-->
<input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload>
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
{
<img id="blah" [src]= "dislayImage()" alt="your image"
class="profilePhotoWH profilePhotoBorder" (click)="doViewImage(adminuserForm.value.image)" />
}
@else
{
<div class="file-upload profilePhotoBorder profilePhotoWH text-center flex justify-center items-center ">
<label class="">{{ adminuserForm.value.image || "No Profile Photo."}}</label>
</div>
}
<div class="flex flex-col gap-2">
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
{
<button pButton type="button" icon="pi pi-times" pTooltip="remove profile photo"
class="p-button-rounded p-button-text text-red-500 p-button-raised ml-2"
(click)="deleteFile()"></button>
}
<button pButton type="button" icon="pi pi-paperclip"
class="self-end p-button-rounded p-button-text p-button-raised ml-2" pTooltip="load photo"
(click)="fileUpload.click()"></button>
</div>
</div>
</div>
<!---->
<div class="">
<label for="login">Email</label>
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
type="text" class="inputfield w-full">
</div>
<div class="">
<label for="phone" class="w-full">Phone</label>
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
</div>
<div class="">
<label class="flex w-full">Father</label>
<p-select [filter]="true" [showClear]="true" filterBy="name" [options]="fatherList" optionLabel="name" appendTo="body" optionValue="id" placeholder="Select Parent"
formControlName="fatherId" class="w-full p-inputtext-sm mr-1"></p-select >
</div>
<div class="">
<label class="flex w-full">Mother</label>
<p-select [options]="motherList" [showClear]="true" [filter]="true" filterBy="name" optionLabel="name" appendTo="body" optionValue="id" placeholder="Select Parent"
formControlName="motherId" class="w-full p-inputtext-sm mr-1"></p-select >
</div>
<div class="grid align-end">
<div class="p-field-checkbox">
<p-checkbox inputId="alive" formControlName="alive" [binary]="true"></p-checkbox>
<label for="alive" class="ml-2 w-full">Alive</label>
</div>
</div>
</div>
<div class="mt-2 w-full">
<div class="flex flex-row justify-between">
<p-button icon="pi pi-images" [raised]="true"
badge="{{photoList.length}}"
severity="success" label="Attach Photos"
(onClick)="viewAttachment()"/>
<p-button icon="pi pi-user" [raised]="true" severity="info" label="Add Partner"
(onClick)="addPartner()"/>
</div>
<div class="shadow rounded mt-2 mb-2">
<p-table [value]="partners()">
<ng-template #header>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Sex</th>
<th>Action</th>
</tr>
</ng-template>
<ng-template #body let-user>
<tr>
<td>{{ user.pfirstName }}</td>
<td>{{ user.plastName }}</td>
<td>{{ user.sex }}</td>
<td>
<button pButton type="button" icon="pi pi-times" class="p-button-rounded p-button-text p-button-danger"
(click)="onDeletePartner(user)"></button>
</td>
</tr>
</ng-template>
</p-table>
</div>
</div>
<div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" label="Save" [disabled]="isFieldsChange"></button>
<button pButton pRipple class="p-button-sm p-button-secondary" type="button" icon="pi pi-times" label="Cancel" (click)="cancel($event)"></button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
</div>
File diff suppressed because it is too large Load Diff
+109 -109
View File
@@ -1,109 +1,109 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
import { AppSettingService } from '../shares';
import { TreeNode } from 'primeng/api';
@Injectable({ providedIn: 'root' })
export class PersonService {
public searchCriteria: StaffSearch;
public parentList: Person[] =[];
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {
this.searchCriteria = {
email:'',
firstName: '',
lastName:''
};
}
searchPersons(criteria: StaffSearch): Observable<ResultModel<Person[]>> {
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson";
return this.http.post<ResultModel<Person[]>>(baseUrl, criteria, config);
}
loadPersonById(id:number): Observable<ResultModel<Person>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
/*
const params = new HttpParams().set("id", ""+id);
const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = {
headers: headers,
params: params
};
*/
return this.http.get<ResultModel<Person>>(baseUrl + "/GetById/" + id);
}
loadPersonFamily(id: number): Observable<ResultModel<TreeNode>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
return this.http.get<ResultModel<TreeNode>>(baseUrl + "/GetByPersonFamily/" + id);
}
loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable<ResultModel<TreeNode[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {useFather,useMother};
return this.http.post<ResultModel<TreeNode[]>>(baseUrl + "/GetFamilyTreeBy", data);
}
loadChildrenById(fatherId:number, motherId: number): Observable<ResultModel<Person[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {fatherId,motherId};
return this.http.post<ResultModel<Person[]>>(baseUrl + "/GetChildress/" ,data);
}
loadRelationshipById(personId:number): Observable<ResultModel<RelationShip[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl;
return this.http.get<ResultModel<RelationShip[]>>(baseUrl + "/GetByPersonId/" +personId);
}
savePerson(data:PersonContainer): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
console.log("save family", data);
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson";
return this.http.post<ResultModel<number>>(baseUrl, data, config);
}
deletePerson(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {id};
return this.http.post<ResultModel<number>>(baseUrl + "/DeleteById" ,data);
}
uploadFile(data: any): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
return this.http.post<ResultModel<string>>(baseUrl, data);
}
downloadFile(imageName: string): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile";
const data = {
fileName: imageName
};
return this.http.post<ResultModel<string>>(baseUrl, data);
}
deleteUploadFile(data: any): Observable<ResultModel<number>> {
//data ={filename};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
deletePersonPhotoFile(id: number): Observable<ResultModel<number>> {
const data ={id, fileName:''};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
savePersonPhotoList(data:FormData): Observable<ResultModel<number>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
downloadPersonPhoto(id: number): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto";
const data = {
id,
fileName: ''
};
return this.http.post<ResultModel<string>>(baseUrl, data);
}
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
import { AppSettingService } from '../shares';
import { TreeNode } from 'primeng/api';
@Injectable({ providedIn: 'root' })
export class PersonService {
public searchCriteria: StaffSearch;
public parentList: Person[] =[];
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {
this.searchCriteria = {
email:'',
firstName: '',
lastName:''
};
}
searchPersons(criteria: StaffSearch): Observable<ResultModel<Person[]>> {
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson";
return this.http.post<ResultModel<Person[]>>(baseUrl, criteria, config);
}
loadPersonById(id:number): Observable<ResultModel<Person>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
/*
const params = new HttpParams().set("id", ""+id);
const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = {
headers: headers,
params: params
};
*/
return this.http.get<ResultModel<Person>>(baseUrl + "/GetById/" + id);
}
loadPersonFamily(id: number): Observable<ResultModel<TreeNode>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
return this.http.get<ResultModel<TreeNode>>(baseUrl + "/GetByPersonFamily/" + id);
}
loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable<ResultModel<TreeNode[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {useFather,useMother};
return this.http.post<ResultModel<TreeNode[]>>(baseUrl + "/GetFamilyTreeBy", data);
}
loadChildrenById(fatherId:number, motherId: number): Observable<ResultModel<Person[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {fatherId,motherId};
return this.http.post<ResultModel<Person[]>>(baseUrl + "/GetChildress/" ,data);
}
loadRelationshipById(personId:number): Observable<ResultModel<RelationShip[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl;
return this.http.get<ResultModel<RelationShip[]>>(baseUrl + "/GetByPersonId/" +personId);
}
savePerson(data:PersonContainer): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
console.log("save family", data);
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson";
return this.http.post<ResultModel<number>>(baseUrl, data, config);
}
deletePerson(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {id};
return this.http.post<ResultModel<number>>(baseUrl + "/DeleteById" ,data);
}
uploadFile(data: any): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
return this.http.post<ResultModel<string>>(baseUrl, data);
}
downloadFile(imageName: string): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile";
const data = {
fileName: imageName
};
return this.http.post<ResultModel<string>>(baseUrl, data);
}
deleteUploadFile(data: any): Observable<ResultModel<number>> {
//data ={filename};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
deletePersonPhotoFile(id: number): Observable<ResultModel<number>> {
const data ={id, fileName:''};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
savePersonPhotoList(data:FormData): Observable<ResultModel<number>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
downloadPersonPhoto(id: number): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto";
const data = {
id,
fileName: ''
};
return this.http.post<ResultModel<string>>(baseUrl, data);
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
<img [src]="imageDataUrl()" alt="Person Image" width="400" height="300"/>
<div class="flex justify-end">
<button pButton pRipple class="p-button-sm mr-2 p-button-secondary" type="button" icon="pi pi-sign-in" label="Close"
(click)="close()"></button>
<img [src]="imageDataUrl()" alt="Person Image" width="400" height="300"/>
<div class="flex justify-end">
<button pButton pRipple class="p-button-sm mr-2 p-button-secondary" type="button" icon="pi pi-sign-in" label="Close"
(click)="close()"></button>
</div>
+63 -63
View File
@@ -1,64 +1,64 @@
import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { AppSettingService } from '../shares';
import { PersonService } from '../person';
import { Subscription } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
@Component({
selector: 'app-image-display',
imports:[CommonModule, ButtonModule],
templateUrl: './image.display.html',
styleUrls: ['./image.display.css']
})
export class ImageDisplayComponent implements OnInit, OnDestroy {
//base64ImageString: string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // Example Base64 (a tiny red dot PNG)
//imageDataUrl: SafeResourceUrl ={};
imageDataUrl = signal<string>("");
private personService = inject(PersonService);
private sanitizer = inject(DomSanitizer);
//private route = inject(ActivatedRoute);
public ref = inject(DynamicDialogRef);
public config = inject(DynamicDialogConfig);
private subscription:Subscription = new Subscription();
ngOnInit(): void {
const imageName = this.config.data.imageName;
this.loadImage(imageName);
}
loadImage(fileName: string| null): void {
const download = this.personService.downloadFile(fileName!);
this.subscription.add(download.subscribe({
next: x => {
if (x.statusCode == 1)
{
this.display(x.data);
console.log("this is show image" , this.imageDataUrl(), x);
}
else
{
console.log("error in download in api ", x.message);
}
},
error: e => console.error("error in download image", e)
}));
}
display(baseImage: string): void
{
const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity;
console.log('this is bypassSecurityTrustResourceUrl', changeUrl);
const fullDataUri = `data:image/png;base64,${changeUrl}`;
this.imageDataUrl.set(fullDataUri);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
close() :void {
this.ref.close(null);
}
import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { AppSettingService } from '../shares';
import { PersonService } from '../person';
import { Subscription } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
@Component({
selector: 'app-image-display',
imports:[CommonModule, ButtonModule],
templateUrl: './image.display.html',
styleUrls: ['./image.display.css']
})
export class ImageDisplayComponent implements OnInit, OnDestroy {
//base64ImageString: string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // Example Base64 (a tiny red dot PNG)
//imageDataUrl: SafeResourceUrl ={};
imageDataUrl = signal<string>("");
private personService = inject(PersonService);
private sanitizer = inject(DomSanitizer);
//private route = inject(ActivatedRoute);
public ref = inject(DynamicDialogRef);
public config = inject(DynamicDialogConfig);
private subscription:Subscription = new Subscription();
ngOnInit(): void {
const imageName = this.config.data.imageName;
this.loadImage(imageName);
}
loadImage(fileName: string| null): void {
const download = this.personService.downloadFile(fileName!);
this.subscription.add(download.subscribe({
next: x => {
if (x.statusCode == 1)
{
this.display(x.data);
console.log("this is show image" , this.imageDataUrl(), x);
}
else
{
console.log("error in download in api ", x.message);
}
},
error: e => console.error("error in download image", e)
}));
}
display(baseImage: string): void
{
const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity;
console.log('this is bypassSecurityTrustResourceUrl', changeUrl);
const fullDataUri = `data:image/png;base64,${changeUrl}`;
this.imageDataUrl.set(fullDataUri);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
close() :void {
this.ref.close(null);
}
}
+47 -47
View File
@@ -1,47 +1,47 @@
<div class="flex-row w-full h-full">
<p-table #dt2 [value]="familyList()" sortMode="multiple"
class="p-datatable-sm" paginatorDropdownAppendTo="body"
dataKey="id" selectionMode="single" [(selection)]="selectedPerson"
[paginator]="true" [globalFilterFields]="['lastName', 'firstName', 'sex']"
[rows]="10" [rowsPerPageOptions]="[5,10, 20,50]"
[loading]="loading">
<ng-template #caption>
<div class="flex">
<p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon>
<i class="pi pi-search"></i>
</p-inputicon>
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
</p-iconfield>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
</th>
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
</th>
<th pSortableColumn="sex">Sex<p-sortIcon field="sex"></p-sortIcon>
</th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr [pSelectableRow]="user">
<td>{{user.lastName}}</td>
<td>{{user.firstName}}</td>
<td>{{user.sex}}</td>
<td></td>
</tr>
</ng-template>
</p-table>
<div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2"
icon="pi pi-check" iconPos="left" label="Select"
(click)="select($event)" [disabled]="selectedPerson == null" ></button>
<button pButton pRipple class="p-button-sm" type="button"
icon="pi pi-times" iconPos="left"
label="Cancel" (click)="cancel($event)"></button>
</div>
</div>
<div class="flex-row w-full h-full">
<p-table #dt2 [value]="familyList()" sortMode="multiple"
class="p-datatable-sm" paginatorDropdownAppendTo="body"
dataKey="id" selectionMode="single" [(selection)]="selectedPerson"
[paginator]="true" [globalFilterFields]="['lastName', 'firstName', 'sex']"
[rows]="10" [rowsPerPageOptions]="[5,10, 20,50]"
[loading]="loading">
<ng-template #caption>
<div class="flex">
<p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon>
<i class="pi pi-search"></i>
</p-inputicon>
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
</p-iconfield>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
</th>
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
</th>
<th pSortableColumn="sex">Sex<p-sortIcon field="sex"></p-sortIcon>
</th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr [pSelectableRow]="user">
<td>{{user.lastName}}</td>
<td>{{user.firstName}}</td>
<td>{{user.sex}}</td>
<td></td>
</tr>
</ng-template>
</p-table>
<div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2"
icon="pi pi-check" iconPos="left" label="Select"
(click)="select($event)" [disabled]="selectedPerson == null" ></button>
<button pButton pRipple class="p-button-sm" type="button"
icon="pi pi-times" iconPos="left"
label="Cancel" (click)="cancel($event)"></button>
</div>
</div>
+53 -53
View File
@@ -1,53 +1,53 @@
import { CommonModule } from '@angular/common';
import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Table, TableModule } from 'primeng/table';
import { Person } from '../models';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
@Component({
selector: 'app-pickperson',
imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule],
templateUrl: './pickperson.html',
styleUrl: './pickperson.css'
})
export class Pickperson implements OnInit, OnDestroy{
@ViewChild(Table) dt2!: Table;
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig)
{
}
loading = false;
familyList = signal<Person[]>([]);
selectedPerson!: Person;
handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.dt2.filterGlobal(value, 'contains');
}
edit(id: number): void {
}
cancel(e:Event):void {
e.preventDefault();
this.ref.close(null);
}
select(e:Event):void {
e.preventDefault();
this.ref.close(this.selectedPerson);
}
ngOnDestroy(): void {
}
ngOnInit(): void {
console.log("pick person the familyList", this.config);
this.familyList.set(this.config.data.familyList);
}
}
import { CommonModule } from '@angular/common';
import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Table, TableModule } from 'primeng/table';
import { Person } from '../models';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
@Component({
selector: 'app-pickperson',
imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule],
templateUrl: './pickperson.html',
styleUrl: './pickperson.css'
})
export class Pickperson implements OnInit, OnDestroy{
@ViewChild(Table) dt2!: Table;
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig)
{
}
loading = false;
familyList = signal<Person[]>([]);
selectedPerson!: Person;
handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.dt2.filterGlobal(value, 'contains');
}
edit(id: number): void {
}
cancel(e:Event):void {
e.preventDefault();
this.ref.close(null);
}
select(e:Event):void {
e.preventDefault();
this.ref.close(this.selectedPerson);
}
ngOnDestroy(): void {
}
ngOnInit(): void {
console.log("pick person the familyList", this.config);
this.familyList.set(this.config.data.familyList);
}
}
+52 -52
View File
@@ -1,52 +1,52 @@
import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Utils } from '../shares';
import {AuthenticationService} from '../user-services/authentication.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard {
msg ="[AuthGuard] ";
constructor(private router: Router,
private authService :AuthenticationService ) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const roleAllowed = route.data['roleAllowed'];
const user = Utils.getCurrentUser();
const userRole = Utils.getUserRole(user);
let hasLoggedIn = Utils.getIsAuth();
//console.log(this.msg + " the userlogin ", user, hasLoggedIn);
if (hasLoggedIn) {
if (roleAllowed != undefined)
{
//it number toString
// console.log("[auth-guard] ther roleAllow ", roleAllowed);
// console.log("[auth-guard] before roleAllow ", roleAllowed,userRole);
if (roleAllowed.indexOf(userRole.toString()) != -1) {
// console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole);
return true;
}
else {
this.authService.logout();
this.router.navigate(['/login']);
return false;
}
}
else //if no role define just return hasLoggedIn
{
return hasLoggedIn;
}
}
else
{
// this.authService.logout();
this.router.navigate(['/login']);
return false;
}
// not logged in so redirect to login page with the return url
//this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
this.authService.logout();
return false;
}
}
import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Utils } from '../shares';
import {AuthenticationService} from '../user-services/authentication.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard {
msg ="[AuthGuard] ";
constructor(private router: Router,
private authService :AuthenticationService ) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const roleAllowed = route.data['roleAllowed'];
const user = Utils.getCurrentUser();
const userRole = Utils.getUserRole(user);
let hasLoggedIn = Utils.getIsAuth();
//console.log(this.msg + " the userlogin ", user, hasLoggedIn);
if (hasLoggedIn) {
if (roleAllowed != undefined)
{
//it number toString
// console.log("[auth-guard] ther roleAllow ", roleAllowed);
// console.log("[auth-guard] before roleAllow ", roleAllowed,userRole);
if (roleAllowed.indexOf(userRole.toString()) != -1) {
// console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole);
return true;
}
else {
this.authService.logout();
this.router.navigate(['/login']);
return false;
}
}
else //if no role define just return hasLoggedIn
{
return hasLoggedIn;
}
}
else
{
// this.authService.logout();
this.router.navigate(['/login']);
return false;
}
// not logged in so redirect to login page with the return url
//this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
this.authService.logout();
return false;
}
}
+1 -1
View File
@@ -1 +1 @@
export * from './auth.guard';
export * from './auth.guard';
+53 -53
View File
@@ -1,54 +1,54 @@
import { FormControl, FormGroup } from '@angular/forms';
export class CommonUtilities {
/** Display the blob */
static displayFileInNewTab(response:any, defaultFileName:string) {
const contentType = response.headers.get('content-type');
const blob = new Blob([response.body], { type: contentType });
const fileName = this.getFileName(response,defaultFileName);
const nav = (window.navigator as any);
if (nav.msSaveOrOpenBlob) {
nav.msSaveOrOpenBlob(blob, fileName);
} else {
const data = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = data;
link.target = '_blank';
link.click();
}
}
/*** Download the blob ***/
static downloadFile(response:any, defaultFileName:string) {
const contentType = response.headers.get('content-type');
const blob = new Blob([response.body], { type: contentType });
const fileName = this.getFileName(response,defaultFileName);
const nav = (window.navigator as any);
if (typeof nav.msSaveBlob === 'function')
{
console.log("download the report saveblob");
nav.msSaveBlob(blob, fileName);
console.log("download the report saveblob done");
}
else {
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
console.log("no msSaveBlob download the report saveblob done");
//link.download? = fileName;
link.download = fileName;
link.click();
}
}
/** Get the attachment file name */
static getFileName(response:any, defaultFileName:string) {
const contentDis = response.headers.get('content-disposition');
let fileName =defaultFileName;
if (contentDis && contentDis.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(contentDis);
if (matches != null && matches[1]) {
fileName = matches[1].replace(/['"]/g, '');
}
}
return fileName;
}
import { FormControl, FormGroup } from '@angular/forms';
export class CommonUtilities {
/** Display the blob */
static displayFileInNewTab(response:any, defaultFileName:string) {
const contentType = response.headers.get('content-type');
const blob = new Blob([response.body], { type: contentType });
const fileName = this.getFileName(response,defaultFileName);
const nav = (window.navigator as any);
if (nav.msSaveOrOpenBlob) {
nav.msSaveOrOpenBlob(blob, fileName);
} else {
const data = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = data;
link.target = '_blank';
link.click();
}
}
/*** Download the blob ***/
static downloadFile(response:any, defaultFileName:string) {
const contentType = response.headers.get('content-type');
const blob = new Blob([response.body], { type: contentType });
const fileName = this.getFileName(response,defaultFileName);
const nav = (window.navigator as any);
if (typeof nav.msSaveBlob === 'function')
{
console.log("download the report saveblob");
nav.msSaveBlob(blob, fileName);
console.log("download the report saveblob done");
}
else {
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
console.log("no msSaveBlob download the report saveblob done");
//link.download? = fileName;
link.download = fileName;
link.click();
}
}
/** Get the attachment file name */
static getFileName(response:any, defaultFileName:string) {
const contentDis = response.headers.get('content-disposition');
let fileName =defaultFileName;
if (contentDis && contentDis.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(contentDis);
if (matches != null && matches[1]) {
fileName = matches[1].replace(/['"]/g, '');
}
}
return fileName;
}
}
+76 -76
View File
@@ -1,77 +1,77 @@
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom, Observable } from 'rxjs';
export function initializeApp_old(appInitService: AppSettingService) {
return (): Promise<any> => {
return appInitService.loadAppSetting_p();
}
};
export function initializeApp() {
return (): Promise<any> => {
return inject(AppSettingService).loadAppSetting_p();
}
// return () => inject(AppSettingService).loadAppSetting();
};
@Injectable({ providedIn: 'root' })
export class AppSettingService {
private _appsetting: any;
constructor(private http: HttpClient
) { }
public loadAppSetting_p(): Promise<any> {
const source$ = this.http.get("config/appsetting.json")
const rs$ = firstValueFrom(source$)
.then(x => {
this._appsetting = x;
console.log("[AppSettingService] assign", x);
});
return rs$;
}
public loadAppSetting(): Observable<any> {
const source$ = this.http.get("config/appsetting.json")
return source$;
}
set appSetting(value: any) {
this._appsetting = value;
}
get appSetting(): any {
return this._appsetting;
}
}
/* angular 19 new one in app.config.ts
provideAppInitializer(initializeApp()),
/* module put this
to use it
in app.module.ts
add
1)
providers: [
{ provide : APP_INITIALIZER, multi : true, deps : [AppSettingService],
useFactory: initializeApp
},
]
2)
other this one work too.
providers: [
{
provide : APP_INITIALIZER,
multi : true,
deps : [AppSettingService],
useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting()
}
]
to use it calling something like this
export class TestComponent {
public test1ServiceUrl: string;
constructor(public configService: AppConfigService) {
this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl;
}
}
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom, Observable } from 'rxjs';
export function initializeApp_old(appInitService: AppSettingService) {
return (): Promise<any> => {
return appInitService.loadAppSetting_p();
}
};
export function initializeApp() {
return (): Promise<any> => {
return inject(AppSettingService).loadAppSetting_p();
}
// return () => inject(AppSettingService).loadAppSetting();
};
@Injectable({ providedIn: 'root' })
export class AppSettingService {
private _appsetting: any;
constructor(private http: HttpClient
) { }
public loadAppSetting_p(): Promise<any> {
const source$ = this.http.get("config/appsetting.json")
const rs$ = firstValueFrom(source$)
.then(x => {
this._appsetting = x;
console.log("[AppSettingService] assign", x);
});
return rs$;
}
public loadAppSetting(): Observable<any> {
const source$ = this.http.get("config/appsetting.json")
return source$;
}
set appSetting(value: any) {
this._appsetting = value;
}
get appSetting(): any {
return this._appsetting;
}
}
/* angular 19 new one in app.config.ts
provideAppInitializer(initializeApp()),
/* module put this
to use it
in app.module.ts
add
1)
providers: [
{ provide : APP_INITIALIZER, multi : true, deps : [AppSettingService],
useFactory: initializeApp
},
]
2)
other this one work too.
providers: [
{
provide : APP_INITIALIZER,
multi : true,
deps : [AppSettingService],
useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting()
}
]
to use it calling something like this
export class TestComponent {
public test1ServiceUrl: string;
constructor(public configService: AppConfigService) {
this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl;
}
}
*/
+97 -97
View File
@@ -1,97 +1,97 @@
import { Injectable, inject } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthenticationService } from '../user-services';
//import { ToastrService } from 'ngx-toastr';
import { NavigationExtras, Router } from '@angular/router';
/*
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
//private authenticationService: AuthenticationService;
constructor(
private router: Router,
// private toastr: ToastrService,
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// console.log("I am intercept to catch error status = 401...", request);
return next.handle(request).pipe(catchError(err => {
/* no use yet
if (err) {
switch(err.status) {
case 400:
if (err.error.errors) {
const modalStateErrors = [];
for (const key in err.error.errors) {
if (err.error.errors[key]) {
modalStateErrors.push(err.error.errors[key]);
}
}
throw modalStateErrors;
} else {
this.toastr.error(err.statusText, err.status);
}
break;
case 401:
this.toastr.error(err.statusText, err.status);
break;
case 404:
this.router.navigateByUrl('/not-found');
break;
case 500:
const navigationExtras: NavigationExtras = {state:{error:err.error}};
this.router.navigateByUrl('/server-error', navigationExtras);
break;
default:
this.toastr.error('Something unexpected went wrong');
console.log("this error interceptor",err);
break;
}
}
*/
/*
if (err.status === 401) {
// auto logout if 401 response returned from api Unauthorized
// this.authenticationService.logout();
//location.reload();
const error = err.error.message || err.statusText;
console.log("After I Get Error status = 401...", error, err);
return throwError(error);
}
else {
console.log("I Get Error .", err);
return throwError(err);
}
}))
}
}
*/
//new one
//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/
////////////////
export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const authenticationService = inject(AuthenticationService);
// logger.log(`Request is on its way to ${req.url}`);
return next(req).pipe(catchError(err => {
if (err.status === 401) {
// auto logout if 401 response returned from api Unauthorized
// this.authenticationService.logout();
//location.reload();
const error = err.error.message || err.statusText;
// console.log("After I Get Error status = 401...", error, err);
return throwError(() => error);
}
else {
console.log("I Get Error .", err);
return throwError(() => err);
}
}));
}
////////////////
import { Injectable, inject } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthenticationService } from '../user-services';
//import { ToastrService } from 'ngx-toastr';
import { NavigationExtras, Router } from '@angular/router';
/*
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
//private authenticationService: AuthenticationService;
constructor(
private router: Router,
// private toastr: ToastrService,
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// console.log("I am intercept to catch error status = 401...", request);
return next.handle(request).pipe(catchError(err => {
/* no use yet
if (err) {
switch(err.status) {
case 400:
if (err.error.errors) {
const modalStateErrors = [];
for (const key in err.error.errors) {
if (err.error.errors[key]) {
modalStateErrors.push(err.error.errors[key]);
}
}
throw modalStateErrors;
} else {
this.toastr.error(err.statusText, err.status);
}
break;
case 401:
this.toastr.error(err.statusText, err.status);
break;
case 404:
this.router.navigateByUrl('/not-found');
break;
case 500:
const navigationExtras: NavigationExtras = {state:{error:err.error}};
this.router.navigateByUrl('/server-error', navigationExtras);
break;
default:
this.toastr.error('Something unexpected went wrong');
console.log("this error interceptor",err);
break;
}
}
*/
/*
if (err.status === 401) {
// auto logout if 401 response returned from api Unauthorized
// this.authenticationService.logout();
//location.reload();
const error = err.error.message || err.statusText;
console.log("After I Get Error status = 401...", error, err);
return throwError(error);
}
else {
console.log("I Get Error .", err);
return throwError(err);
}
}))
}
}
*/
//new one
//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/
////////////////
export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const authenticationService = inject(AuthenticationService);
// logger.log(`Request is on its way to ${req.url}`);
return next(req).pipe(catchError(err => {
if (err.status === 401) {
// auto logout if 401 response returned from api Unauthorized
// this.authenticationService.logout();
//location.reload();
const error = err.error.message || err.statusText;
// console.log("After I Get Error status = 401...", error, err);
return throwError(() => error);
}
else {
console.log("I Get Error .", err);
return throwError(() => err);
}
}));
}
////////////////
+114 -114
View File
@@ -1,115 +1,115 @@
import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ConfigureUrl } from '../models';
import { AppSettingService } from './appsetting';
@Injectable({
providedIn: 'root'
})
export class HttpUtility {
private baseApiUrl: string = "";
constructor(private http: HttpClient,
private appSetting :AppSettingService,
private router: Router) {
}
/**
* getFile
* @param url
*/
public getFileAsText(url: string): Observable<any> {
return this.http
.get(this.getApiUrl(url), { responseType: 'blob' as 'text' })
.pipe(catchError(e => this.handleError(e)));
}
/**
* getFile
* @param url
*/
public getFile(url: string): Observable<any> {
return this.http
.get(this.getApiUrl(url), { responseType: 'blob' as 'json' })
.pipe(catchError(e => this.handleError(e)));
}
/**
* handleError
* @param response
*/
private handleError(errorResponse: HttpErrorResponse) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
return throwError(errorResponse);
}
//get excel file zip file etc ..
/*
allowedToDisplay = [
this.mimeConstant.png,
this.mimeConstant.jpeg,
this.mimeConstant.jpg,
this.mimeConstant.gif,
this.mimeConstant.txt,
this.mimeConstant.pdf
];
*/
/* how to use it
this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe(
catchError(err => {
this.snackBar.error(err || this.constants.error_Getting_photo);
return EMPTY;
}),
finalize(() => {
this.spinner.hide();
})).subscribe(response => {
if (response) {
if (isDownload) {
CommonUtilities.downloadFile(response);
} else {
if (response.body && response.body.type) {
if (this.allowedToDisplay.includes(response.body.type)) {
CommonUtilities.displayFileInNewTab(response);
} else {
this.temporaryFile = response;
this.downloadWarning = true;
}
}
}
}
});
*/
public getFileResponse(url: string, data?: any): Observable<any> {
if (data) {
return this.http.post(this.getApiUrl(url),
data,
{ responseType: 'blob', observe: 'response' })
.pipe(catchError(e => this.handleError(e)));
}
return this.http
.get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' })
.pipe(catchError(e => this.handleError(e)));
}
public getFileAsPDF(url: string, data: any): Observable<any> {
let updateURL;
if (data) {
updateURL = url + data;
} else {
updateURL = url;
}
return this.http
.get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' })
.pipe(catchError(e => this.handleError(e)));
}
/**
* getApiUrl
* @param url
*/
private getApiUrl(url:string) {
const baseUrl = this.appSetting.appSetting.baseUrl;
return baseUrl + "/"+ url;
}
import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ConfigureUrl } from '../models';
import { AppSettingService } from './appsetting';
@Injectable({
providedIn: 'root'
})
export class HttpUtility {
private baseApiUrl: string = "";
constructor(private http: HttpClient,
private appSetting :AppSettingService,
private router: Router) {
}
/**
* getFile
* @param url
*/
public getFileAsText(url: string): Observable<any> {
return this.http
.get(this.getApiUrl(url), { responseType: 'blob' as 'text' })
.pipe(catchError(e => this.handleError(e)));
}
/**
* getFile
* @param url
*/
public getFile(url: string): Observable<any> {
return this.http
.get(this.getApiUrl(url), { responseType: 'blob' as 'json' })
.pipe(catchError(e => this.handleError(e)));
}
/**
* handleError
* @param response
*/
private handleError(errorResponse: HttpErrorResponse) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
return throwError(errorResponse);
}
//get excel file zip file etc ..
/*
allowedToDisplay = [
this.mimeConstant.png,
this.mimeConstant.jpeg,
this.mimeConstant.jpg,
this.mimeConstant.gif,
this.mimeConstant.txt,
this.mimeConstant.pdf
];
*/
/* how to use it
this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe(
catchError(err => {
this.snackBar.error(err || this.constants.error_Getting_photo);
return EMPTY;
}),
finalize(() => {
this.spinner.hide();
})).subscribe(response => {
if (response) {
if (isDownload) {
CommonUtilities.downloadFile(response);
} else {
if (response.body && response.body.type) {
if (this.allowedToDisplay.includes(response.body.type)) {
CommonUtilities.displayFileInNewTab(response);
} else {
this.temporaryFile = response;
this.downloadWarning = true;
}
}
}
}
});
*/
public getFileResponse(url: string, data?: any): Observable<any> {
if (data) {
return this.http.post(this.getApiUrl(url),
data,
{ responseType: 'blob', observe: 'response' })
.pipe(catchError(e => this.handleError(e)));
}
return this.http
.get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' })
.pipe(catchError(e => this.handleError(e)));
}
public getFileAsPDF(url: string, data: any): Observable<any> {
let updateURL;
if (data) {
updateURL = url + data;
} else {
updateURL = url;
}
return this.http
.get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' })
.pipe(catchError(e => this.handleError(e)));
}
/**
* getApiUrl
* @param url
*/
private getApiUrl(url:string) {
const baseUrl = this.appSetting.appSetting.baseUrl;
return baseUrl + "/"+ url;
}
}
+7 -7
View File
@@ -1,8 +1,8 @@
export * from './utils';
export {initializeApp, AppSettingService} from './appsetting';
export * from './error.interceptor';
export * from './jwt.interceptor';
export * from './lookup.service';
export * from './httpfile.service';
export * from './CommonUtilities';
export * from './utils';
export {initializeApp, AppSettingService} from './appsetting';
export * from './error.interceptor';
export * from './jwt.interceptor';
export * from './lookup.service';
export * from './httpfile.service';
export * from './CommonUtilities';
export * from './timeinput';
File diff suppressed because it is too large Load Diff
+46 -46
View File
@@ -1,47 +1,47 @@
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Utils } from './utils';
/*
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
console.log('I am interceptor debug for currentUser obj', currentUser);
if (currentUser && currentUser.token) {
//console.log('I am interceptor add authorization header with jwt token');
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next.handle(request);
}
}
/*
new one
////////////////
*/
export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
//const logger = inject(Logger);
//logger.log(`Request is on its way to ${req.url}`);
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
// console.log('I am interceptor debug for currentUser obj', currentUser);
if (currentUser && currentUser.token && currentUser.token != "") {
// console.log('I am interceptor add authorization header with jwt token');
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next(request);
}
////////////////
/*
providers: [provideHttpClient(withInterceptors([loggerInterceptor]))]
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Utils } from './utils';
/*
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
console.log('I am interceptor debug for currentUser obj', currentUser);
if (currentUser && currentUser.token) {
//console.log('I am interceptor add authorization header with jwt token');
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next.handle(request);
}
}
/*
new one
////////////////
*/
export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
//const logger = inject(Logger);
//logger.log(`Request is on its way to ${req.url}`);
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
// console.log('I am interceptor debug for currentUser obj', currentUser);
if (currentUser && currentUser.token && currentUser.token != "") {
// console.log('I am interceptor add authorization header with jwt token');
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next(request);
}
////////////////
/*
providers: [provideHttpClient(withInterceptors([loggerInterceptor]))]
*/
+136 -136
View File
@@ -1,136 +1,136 @@
import { Injectable, Inject , Output} from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import { ConfigureUrl,Lookup,LookupEdit,ResultModel
} from '../models';
import { Utils,AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class LookupService {
msg: string = "[lookupService] ";
_lookupByStatus:any;
_staffList:any;
_clientList:any;
_jobList:any;
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {
// this default when load look for previous login information
// if no need then comment out downthere
this.setAllNull();
}
setAllNull() :void {
// this._lhdList = undefined;
this._lookupByStatus = {};
this._staffList = null;
this._clientList = null;
this._jobList = null;
}
loadLookupEditBystatus(type:string): Observable<ResultModel<LookupEdit[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit";
const params = new HttpParams().append('type', type);
console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type);
return this.http.get<ResultModel<LookupEdit[]>>(baseUrl, {params});
}
loadLookupById(id:number, type:string): Observable<ResultModel<LookupEdit>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
}
saveLookup(item:LookupEdit): Observable<ResultModel<number>> { //insert Lookup
this.setAllNull();
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
return this.http.post<ResultModel<number>>(baseUrl, item, config);
}
deleteLookup(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id );
}
loadLookupBystatus(type:string): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs";
console.log(this.msg + "loadLookupBystatus", this._lookupByStatus);
if (this._lookupByStatus[type])
{
console.log(this.msg + "Not go server this LoadJobs is in cache ",type);
return of(this._lookupByStatus[type]);
}
else
{
const params = new HttpParams().append('type', type);
console.log(this.msg + "go server this loadLookupBystatus is in cache ", type);
return this.http.get<ResultModel<Lookup[]>>(baseUrl, {params})
.pipe(map(resultModel => {
this._lookupByStatus[type] = resultModel;
return resultModel;
})
);
}
}
loadStaffs(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs";
/* const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/
if (this._staffList != null)
{
return of(this._staffList);
}
else
{
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map( x => {
this._staffList = x;
return x;
}));
}
}
loadClients(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients";
/* const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/
if (this._clientList != null)
{
return of(this._clientList);
}
else
{
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map(resultModel => {
this._clientList = resultModel;
return resultModel;
})
);
}
}
loadJobs(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs";
/* const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/
if (this._jobList != null)
{
return of(this._jobList);
}
else
{
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map(x => {
this._jobList = x;
return x;
}))
}
}
}
import { Injectable, Inject , Output} from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import { ConfigureUrl,Lookup,LookupEdit,ResultModel
} from '../models';
import { Utils,AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class LookupService {
msg: string = "[lookupService] ";
_lookupByStatus:any;
_staffList:any;
_clientList:any;
_jobList:any;
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {
// this default when load look for previous login information
// if no need then comment out downthere
this.setAllNull();
}
setAllNull() :void {
// this._lhdList = undefined;
this._lookupByStatus = {};
this._staffList = null;
this._clientList = null;
this._jobList = null;
}
loadLookupEditBystatus(type:string): Observable<ResultModel<LookupEdit[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit";
const params = new HttpParams().append('type', type);
console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type);
return this.http.get<ResultModel<LookupEdit[]>>(baseUrl, {params});
}
loadLookupById(id:number, type:string): Observable<ResultModel<LookupEdit>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
}
saveLookup(item:LookupEdit): Observable<ResultModel<number>> { //insert Lookup
this.setAllNull();
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
return this.http.post<ResultModel<number>>(baseUrl, item, config);
}
deleteLookup(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id );
}
loadLookupBystatus(type:string): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs";
console.log(this.msg + "loadLookupBystatus", this._lookupByStatus);
if (this._lookupByStatus[type])
{
console.log(this.msg + "Not go server this LoadJobs is in cache ",type);
return of(this._lookupByStatus[type]);
}
else
{
const params = new HttpParams().append('type', type);
console.log(this.msg + "go server this loadLookupBystatus is in cache ", type);
return this.http.get<ResultModel<Lookup[]>>(baseUrl, {params})
.pipe(map(resultModel => {
this._lookupByStatus[type] = resultModel;
return resultModel;
})
);
}
}
loadStaffs(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs";
/* const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/
if (this._staffList != null)
{
return of(this._staffList);
}
else
{
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map( x => {
this._staffList = x;
return x;
}));
}
}
loadClients(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients";
/* const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/
if (this._clientList != null)
{
return of(this._clientList);
}
else
{
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map(resultModel => {
this._clientList = resultModel;
return resultModel;
})
);
}
}
loadJobs(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs";
/* const params = new HttpParams()
.append('id', id)
.append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/
if (this._jobList != null)
{
return of(this._jobList);
}
else
{
return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map(x => {
this._jobList = x;
return x;
}))
}
}
}
+149 -149
View File
@@ -1,149 +1,149 @@
import { CommonModule } from '@angular/common';
import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'time-input',
imports:[ReactiveFormsModule,CommonModule],
template: `
<input
type="text"
[formControl]="timeControl"
(input)="onInput($event)"
(blur)="onBlur()"
class="time-input"
style="width: 80px; text-align: center;"
/>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeInputComponent),
multi: true,
},
],
})
export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
@Input() placeholder: string = 'HH:MM';
@Input() initialValue: string = ''; // Added initialValue input
@Input() disabled: boolean = false;
timeControl = new FormControl('', [
Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
]);
private destroy$ = new Subject<void>();
private _value: string = '';
private onChange: (value: string) => void = () => {};
private onTouched: () => void = () => {};
constructor() {}
ngOnInit(): void {
this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.onChange(value || ''); // Pass the value to the parent form
});
if (this.initialValue) {
this.writeValue(this.initialValue);
}
this.timeControl.disable();
if(!this.disabled){
this.timeControl.enable();
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
get value(): string {
return this._value;
}
set value(val: string) {
this._value = val;
this.onChange(val);
this.onTouched();
}
writeValue(value: string): void {
this._value = value || '';
this.formatInput(this._value); // Use the formatting function
}
registerOnChange(fn: (value: string) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.timeControl.disable();
} else {
this.timeControl.enable();
}
}
onInput(event: Event): void {
let input = (event.target as HTMLInputElement).value;
input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
if (input.length > 4) {
input = input.slice(0, 4); // Limit to 4 digits
}
let formatted = '';
if (input.length > 2) {
formatted = input.slice(0, 2) + ':' + input.slice(2);
} else {
formatted = input;
}
this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
this.value = formatted;
}
onBlur(): void {
this.onTouched();
if (this.timeControl.valid) {
return;
}
if (this.timeControl.value?.length === 0) {
this.timeControl.setValue('', {emitEvent: false});
this.value = '';
return
}
this.formatInput(this.timeControl.value);
}
private formatInput(val: string | null): void {
if (!val) {
this.timeControl.setValue('', {emitEvent: false});
this.value = '';
return;
}
let numbersOnly = val.replace(/[^0-9]/g, '');
let formatted = '';
if (numbersOnly.length > 2) {
formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
} else {
formatted = numbersOnly;
}
if (formatted.length === 5 && this.timeControl.valid) {
this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted;
} else if (numbersOnly.length <= 2) {
this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted;
} else {
this.timeControl.setValue('00:00', {emitEvent: false});
this.value = '00:00'
}
}
}
import { CommonModule } from '@angular/common';
import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'time-input',
imports:[ReactiveFormsModule,CommonModule],
template: `
<input
type="text"
[formControl]="timeControl"
(input)="onInput($event)"
(blur)="onBlur()"
class="time-input"
style="width: 80px; text-align: center;"
/>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeInputComponent),
multi: true,
},
],
})
export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
@Input() placeholder: string = 'HH:MM';
@Input() initialValue: string = ''; // Added initialValue input
@Input() disabled: boolean = false;
timeControl = new FormControl('', [
Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
]);
private destroy$ = new Subject<void>();
private _value: string = '';
private onChange: (value: string) => void = () => {};
private onTouched: () => void = () => {};
constructor() {}
ngOnInit(): void {
this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.onChange(value || ''); // Pass the value to the parent form
});
if (this.initialValue) {
this.writeValue(this.initialValue);
}
this.timeControl.disable();
if(!this.disabled){
this.timeControl.enable();
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
get value(): string {
return this._value;
}
set value(val: string) {
this._value = val;
this.onChange(val);
this.onTouched();
}
writeValue(value: string): void {
this._value = value || '';
this.formatInput(this._value); // Use the formatting function
}
registerOnChange(fn: (value: string) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.timeControl.disable();
} else {
this.timeControl.enable();
}
}
onInput(event: Event): void {
let input = (event.target as HTMLInputElement).value;
input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
if (input.length > 4) {
input = input.slice(0, 4); // Limit to 4 digits
}
let formatted = '';
if (input.length > 2) {
formatted = input.slice(0, 2) + ':' + input.slice(2);
} else {
formatted = input;
}
this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
this.value = formatted;
}
onBlur(): void {
this.onTouched();
if (this.timeControl.valid) {
return;
}
if (this.timeControl.value?.length === 0) {
this.timeControl.setValue('', {emitEvent: false});
this.value = '';
return
}
this.formatInput(this.timeControl.value);
}
private formatInput(val: string | null): void {
if (!val) {
this.timeControl.setValue('', {emitEvent: false});
this.value = '';
return;
}
let numbersOnly = val.replace(/[^0-9]/g, '');
let formatted = '';
if (numbersOnly.length > 2) {
formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
} else {
formatted = numbersOnly;
}
if (formatted.length === 5 && this.timeControl.valid) {
this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted;
} else if (numbersOnly.length <= 2) {
this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted;
} else {
this.timeControl.setValue('00:00', {emitEvent: false});
this.value = '00:00'
}
}
}
+218 -218
View File
@@ -1,219 +1,219 @@
import moment from "moment";
import { Person, User, userRole } from "../models"
import { TreeNode } from "primeng/api";
type MyProName = "fatherId" | "motherId";
export class Utils {
static toBase64 (file:any)
{
let promise = new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
return promise;
}
static getBase64(file: any) {
let result: any;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
result = reader.result;
console.log('tobase64 ', result);
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
static getCurrentUser(): User {
let myUser = new User();
myUser.username = "";
const obj = sessionStorage.getItem('currentUser');
//const obj = localStorage.getItem('currentUser');
if (obj != undefined) {
let user = JSON.parse(obj);
myUser.firstName = user.firstName;
myUser.id = user.id;
myUser.lastName = user.lastName;
myUser.role = user.role;
myUser.username = user.username;
myUser.email = user.email;
myUser.token = user.token;
myUser.phone = user.phone;
myUser.department = user.department;
myUser.position = user.position;
myUser.managerEmail = user.managerEmail;
}
// console.log("[util] getcurrentUser",myUser);
return myUser;
}
static setLocalStore(user: User): void {
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
// localStorage.setItem('currentUser', JSON.stringify(user));
sessionStorage.setItem('currentUser', JSON.stringify(user));
}
}
static getLastMonth(): Date {
let result = moment().subtract(1, 'months').toDate();
result.setDate(1);
return result;
}
static getHome(): string {
let result ="";
const user = this.getCurrentUser();
const urole = user.role;
if (urole == userRole.Accounting)
{
result = "staffworkw";
}
else if (urole == userRole.Admin)
{
result = "staff";
}
else if (urole == userRole.ServiceManager)
{
result = "servicetask";
}
else if (urole == userRole.WorkShop)
{
result = "staffwork";
}
else if (urole == userRole.Normal)
{
result = "staff";
}
return result;
}
static getTimeStr(val:Date): string {
let result = "00:00";
if (val)
{
const hh = val.getHours();
const min = val.getMinutes();
if (hh < 10)
result = '0' + hh.toString();
else
result = hh.toString();
if (min < 10)
result = result + ":0" + min.toString();
else
result = result + ":" + min.toString();
}
return result;
}
static canRunReport(role: number): boolean {
let result = false;
if (role == userRole.Admin)
result = true;
return result;
}
static getUserRole(user: User): number {
/*
sysadmin
request_manager
ward_user
*/
let ret = 0;
if (user) {
// console.log("utils user", user);
if (user.role)
ret = user.role;
}
return ret;
}
static getIsAuth(): boolean {
const user = this.getCurrentUser();
if (user.username != "") {
return true;
}
else
return false;
}
static getAdminRoleName(): number {
return userRole.Admin;
}
static clearCurrentUser(): void {
sessionStorage.clear();
//localStorage.clear();
}
static formatNode(item:Person): TreeNode
{
let label = item.lastName + " " + item.firstName;
let key = item.id.toString();
let node: TreeNode ={
key,
label,
type: 'person',
data: {title: item.title, name: label, image: item.image},
icon: 'pi pi-user'
};
return node;
}
static getFileExtension(filename: string): string {
const lastDotIndex = filename.lastIndexOf('.');
if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { // Ensure a dot exists and is not the last character
return filename.substring(lastDotIndex + 1);
}
return ''; // No extension found
}
static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] {
let result: TreeNode[] =[];
let tree_node_child: TreeNode[];
let node:TreeNode;
let item: Person;
const children = childressNodes.filter(x => x[proName] == pid);
for (let c = 0; c < children.length; c++)
{
item = children[c];
node = this.formatNode(item);
tree_node_child = this.getChildForParentId(proName,item.id, childressNodes);
console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child);
if (tree_node_child.length > 0)
{
node.expanded = true;
node.children = tree_node_child;
}
result.push(node);
}
return result;
}
static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] {
let familyTree: TreeNode[] = [];
let node:TreeNode;
let child_nodes:TreeNode[];
let childressNodes:Person[];
let item: Person;
const topNodes = familyList.filter(x => x[proName]! < 1);
childressNodes = familyList.filter(x => x[proName]! > 0);
for(let i = 0; i < topNodes.length; i++)
{
item = topNodes[i];
node = Utils.formatNode(item);
child_nodes = this.getChildForParentId(proName, item.id,childressNodes);
console.log("populate getchildrenForParentId", child_nodes);
if (child_nodes.length > 0)
{
node.expanded = true;
node.children = child_nodes;
}
familyTree.push(node);
//childressNodes =
}
return familyTree;
}
import moment from "moment";
import { Person, User, userRole } from "../models"
import { TreeNode } from "primeng/api";
type MyProName = "fatherId" | "motherId";
export class Utils {
static toBase64 (file:any)
{
let promise = new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
return promise;
}
static getBase64(file: any) {
let result: any;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
result = reader.result;
console.log('tobase64 ', result);
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
static getCurrentUser(): User {
let myUser = new User();
myUser.username = "";
const obj = sessionStorage.getItem('currentUser');
//const obj = localStorage.getItem('currentUser');
if (obj != undefined) {
let user = JSON.parse(obj);
myUser.firstName = user.firstName;
myUser.id = user.id;
myUser.lastName = user.lastName;
myUser.role = user.role;
myUser.username = user.username;
myUser.email = user.email;
myUser.token = user.token;
myUser.phone = user.phone;
myUser.department = user.department;
myUser.position = user.position;
myUser.managerEmail = user.managerEmail;
}
// console.log("[util] getcurrentUser",myUser);
return myUser;
}
static setLocalStore(user: User): void {
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
// localStorage.setItem('currentUser', JSON.stringify(user));
sessionStorage.setItem('currentUser', JSON.stringify(user));
}
}
static getLastMonth(): Date {
let result = moment().subtract(1, 'months').toDate();
result.setDate(1);
return result;
}
static getHome(): string {
let result ="";
const user = this.getCurrentUser();
const urole = user.role;
if (urole == userRole.Accounting)
{
result = "staffworkw";
}
else if (urole == userRole.Admin)
{
result = "staff";
}
else if (urole == userRole.ServiceManager)
{
result = "servicetask";
}
else if (urole == userRole.WorkShop)
{
result = "staffwork";
}
else if (urole == userRole.Normal)
{
result = "staff";
}
return result;
}
static getTimeStr(val:Date): string {
let result = "00:00";
if (val)
{
const hh = val.getHours();
const min = val.getMinutes();
if (hh < 10)
result = '0' + hh.toString();
else
result = hh.toString();
if (min < 10)
result = result + ":0" + min.toString();
else
result = result + ":" + min.toString();
}
return result;
}
static canRunReport(role: number): boolean {
let result = false;
if (role == userRole.Admin)
result = true;
return result;
}
static getUserRole(user: User): number {
/*
sysadmin
request_manager
ward_user
*/
let ret = 0;
if (user) {
// console.log("utils user", user);
if (user.role)
ret = user.role;
}
return ret;
}
static getIsAuth(): boolean {
const user = this.getCurrentUser();
if (user.username != "") {
return true;
}
else
return false;
}
static getAdminRoleName(): number {
return userRole.Admin;
}
static clearCurrentUser(): void {
sessionStorage.clear();
//localStorage.clear();
}
static formatNode(item:Person): TreeNode
{
let label = item.lastName + " " + item.firstName;
let key = item.id.toString();
let node: TreeNode ={
key,
label,
type: 'person',
data: {title: item.title, name: label, image: item.image},
icon: 'pi pi-user'
};
return node;
}
static getFileExtension(filename: string): string {
const lastDotIndex = filename.lastIndexOf('.');
if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { // Ensure a dot exists and is not the last character
return filename.substring(lastDotIndex + 1);
}
return ''; // No extension found
}
static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] {
let result: TreeNode[] =[];
let tree_node_child: TreeNode[];
let node:TreeNode;
let item: Person;
const children = childressNodes.filter(x => x[proName] == pid);
for (let c = 0; c < children.length; c++)
{
item = children[c];
node = this.formatNode(item);
tree_node_child = this.getChildForParentId(proName,item.id, childressNodes);
console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child);
if (tree_node_child.length > 0)
{
node.expanded = true;
node.children = tree_node_child;
}
result.push(node);
}
return result;
}
static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] {
let familyTree: TreeNode[] = [];
let node:TreeNode;
let child_nodes:TreeNode[];
let childressNodes:Person[];
let item: Person;
const topNodes = familyList.filter(x => x[proName]! < 1);
childressNodes = familyList.filter(x => x[proName]! > 0);
for(let i = 0; i < topNodes.length; i++)
{
item = topNodes[i];
node = Utils.formatNode(item);
child_nodes = this.getChildForParentId(proName, item.id,childressNodes);
console.log("populate getchildrenForParentId", child_nodes);
if (child_nodes.length > 0)
{
node.expanded = true;
node.children = child_nodes;
}
familyTree.push(node);
//childressNodes =
}
return familyTree;
}
}
+4 -4
View File
@@ -1,4 +1,4 @@
export * from './staff.component';
export * from './staff.edit.component';
export * from './staff.service';
export * from './staff.pass.component';
export * from './staff.component';
export * from './staff.edit.component';
export * from './staff.service';
export * from './staff.pass.component';
+12 -12
View File
@@ -1,12 +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;
}
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
td.mat-column-edit, .mat-column-delete {
width: 35px;
padding-right: 2px;
}
+64 -64
View File
@@ -1,64 +1,64 @@
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Staff List</h3>
<div>
<form>
<div class="grid md:grid-cols-4 gap-2">
<div class="">
<label for="login">Email</label>
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="lastname1">Surname</label>
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="firstname">First Name</label>
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
(click)="search()"></button>
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" iconPos="left" label="New Staff"
(click)="newUser()"></button>
</div>
</div>
</form>
</div>
<div>
<!--[value]="userList"-->
<p-table [value]="users()" sortMode="multiple"
class="p-datatable-sm" [loading]="loading">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="email">Email<p-sortIcon field="email"></p-sortIcon>
</th>
<th pSortableColumn="lastname">Surname <p-sortIcon field="lastname"></p-sortIcon>
</th>
<th pSortableColumn="firstname">First Name <p-sortIcon field="firstname"></p-sortIcon>
</th>
<th >Active </th>
<th>Edit</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr>
<td>{{user.email}}</td>
<td>{{user.lastname}}</td>
<td>{{user.firstname}}</td>
<td>
<i class="pi" [ngClass]="{'true-icon pi-check-circle': user.active, 'false-icon pi-times-circle': !user.active}"></i>
</td>
<td>
<button pButton type="button" icon="pi pi-pencil" class="p-button-rounded p-button-text"
(click)="edit(user.id)"></button>
</td>
</tr>
</ng-template>
</p-table>
</div>
<div class="shadow-2xl rounded p-2 mt-2">
<h3>Staff List</h3>
<div>
<form>
<div class="grid md:grid-cols-4 gap-2">
<div class="">
<label for="login">Email</label>
<input id="login" pInputText name="login" type="text" [(ngModel)]="email"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="lastname1">Surname</label>
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="">
<label for="firstname">First Name</label>
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
class="inputfield w-full p-inputtext-sm">
</div>
<div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
(click)="search()"></button>
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" iconPos="left" label="New Staff"
(click)="newUser()"></button>
</div>
</div>
</form>
</div>
<div>
<!--[value]="userList"-->
<p-table [value]="users()" sortMode="multiple"
class="p-datatable-sm" [loading]="loading">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="email">Email<p-sortIcon field="email"></p-sortIcon>
</th>
<th pSortableColumn="lastname">Surname <p-sortIcon field="lastname"></p-sortIcon>
</th>
<th pSortableColumn="firstname">First Name <p-sortIcon field="firstname"></p-sortIcon>
</th>
<th >Active </th>
<th>Edit</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr>
<td>{{user.email}}</td>
<td>{{user.lastname}}</td>
<td>{{user.firstname}}</td>
<td>
<i class="pi" [ngClass]="{'true-icon pi-check-circle': user.active, 'false-icon pi-times-circle': !user.active}"></i>
</td>
<td>
<button pButton type="button" icon="pi pi-pencil" class="p-button-rounded p-button-text"
(click)="edit(user.id)"></button>
</td>
</tr>
</ng-template>
</p-table>
</div>
+162 -162
View File
@@ -1,162 +1,162 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDetectorRef} from '@angular/core';
import { StaffView ,StaffSearch } from '../models';
import { Store } from '@ngrx/store';
import { StaffsActions, StaffsApiActions } from '../state/staff.actions';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { StaffService } from './staff.service';
import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { selectStaffLoaded,selectStaff } from '../state/staff.selectors';
@Component({
selector: 'staff-list',
templateUrl: './staff.component.html',
imports:[TableModule,FormsModule,CommonModule,ButtonModule,InputTextModule],
styleUrls: ['./staff.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StaffComponent implements OnInit, OnDestroy{
private readonly store = inject(Store);
private subscription:Subscription = new Subscription();
firstname = '';
email = '';
lastname = '';
loading = false;
userList:StaffView[] = [];
users = this.store.selectSignal(selectStaff);
staffloadyet = this.store.selectSignal(selectStaffLoaded)
private cd = inject(ChangeDetectorRef);
msg ="[Staff component]";
/*
private store: Store<appValidationState>
*/
constructor(
private staffService: StaffService,
private authenticationService: AuthenticationService,
private router: Router
) {}
getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = {
email: this.email,
firstName: this.firstname,
lastName: this.lastname,
};
return criteria;
}
canSearch():boolean {
let result = false;
result = this.email !== "";
result = result || this.lastname !== "";
result = result || this.firstname !== "";
return result;
}
ngOnInit(): void
{
this.authenticationService.isHome = false;
this.authenticationService.isReport = false;
const prev = this.staffService.searchCriteria;
let goload = true;
if (prev.lastName !== '')
{
this.lastname = prev.lastName;
goload = true;
}
if (prev.firstName !== '')
{
this.firstname = prev.firstName;
goload = true;
}
if (prev.email !== '')
{
this.email = prev.email;
goload = true;
}
if (goload)
{
this.search();
}
}
getActive(active:boolean):string {
let result = 'false-icon pi-times-circle';
if (active)
result = 'true-icon pi-check-circle';
return result;
}
search():void {
const canSearch = true; // this.canSearch();
if (canSearch)
{
const criteria = this.getSearchCiteria();
this.staffService.searchCriteria = criteria;
console.log("search function store staffload yet",this.staffloadyet(), this.users);
if (this.staffloadyet() != true) {
this.store.dispatch(StaffsActions.loadStaff({criteria}));
}
/*
if (this.staffloadyet() != true) {
this.loading = true;
const loadStaff$ = this.staffService.searchStaffs(criteria);
this.subscription.add(
loadStaff$.subscribe( {
next: result => {
this.loading = false;
this.userList = result.data;
this.store.dispatch(StaffsApiActions.loadStaffSuccess({staffs:this.userList, staffLoad: true}));
this.cd.detectChanges();
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
this.loading = false;
console.log("error ", e);
}
})
);
}
*/
}
}
newUser():void {
//console.log("add new employee");
this.router.navigate( ['/staff/new'], { queryParams: {returnUrl:'/staff' } });
}
edit(id: number) : void {
//console.log("edit staff", id);
this.router.navigate( ['/staff/'+id], { queryParams: {returnUrl:'/staff' } });
}
deleteItem(id: number): void {
this.staffService.deleteStaff(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
//let data = this.dataSource.data;
//let index: number = data.findIndex(d => d.id === id);
// console.log(this.msg + "delete from datasource");
//data.splice(index, 1)
//this.dataSource.data = data;
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDetectorRef} from '@angular/core';
import { StaffView ,StaffSearch } from '../models';
import { Store } from '@ngrx/store';
import { StaffsActions, StaffsApiActions } from '../state/staff.actions';
import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { StaffService } from './staff.service';
import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { selectStaffLoaded,selectStaff } from '../state/staff.selectors';
@Component({
selector: 'staff-list',
templateUrl: './staff.component.html',
imports:[TableModule,FormsModule,CommonModule,ButtonModule,InputTextModule],
styleUrls: ['./staff.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StaffComponent implements OnInit, OnDestroy{
private readonly store = inject(Store);
private subscription:Subscription = new Subscription();
firstname = '';
email = '';
lastname = '';
loading = false;
userList:StaffView[] = [];
users = this.store.selectSignal(selectStaff);
staffloadyet = this.store.selectSignal(selectStaffLoaded)
private cd = inject(ChangeDetectorRef);
msg ="[Staff component]";
/*
private store: Store<appValidationState>
*/
constructor(
private staffService: StaffService,
private authenticationService: AuthenticationService,
private router: Router
) {}
getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = {
email: this.email,
firstName: this.firstname,
lastName: this.lastname,
};
return criteria;
}
canSearch():boolean {
let result = false;
result = this.email !== "";
result = result || this.lastname !== "";
result = result || this.firstname !== "";
return result;
}
ngOnInit(): void
{
this.authenticationService.isHome = false;
this.authenticationService.isReport = false;
const prev = this.staffService.searchCriteria;
let goload = true;
if (prev.lastName !== '')
{
this.lastname = prev.lastName;
goload = true;
}
if (prev.firstName !== '')
{
this.firstname = prev.firstName;
goload = true;
}
if (prev.email !== '')
{
this.email = prev.email;
goload = true;
}
if (goload)
{
this.search();
}
}
getActive(active:boolean):string {
let result = 'false-icon pi-times-circle';
if (active)
result = 'true-icon pi-check-circle';
return result;
}
search():void {
const canSearch = true; // this.canSearch();
if (canSearch)
{
const criteria = this.getSearchCiteria();
this.staffService.searchCriteria = criteria;
console.log("search function store staffload yet",this.staffloadyet(), this.users);
if (this.staffloadyet() != true) {
this.store.dispatch(StaffsActions.loadStaff({criteria}));
}
/*
if (this.staffloadyet() != true) {
this.loading = true;
const loadStaff$ = this.staffService.searchStaffs(criteria);
this.subscription.add(
loadStaff$.subscribe( {
next: result => {
this.loading = false;
this.userList = result.data;
this.store.dispatch(StaffsApiActions.loadStaffSuccess({staffs:this.userList, staffLoad: true}));
this.cd.detectChanges();
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
this.loading = false;
console.log("error ", e);
}
})
);
}
*/
}
}
newUser():void {
//console.log("add new employee");
this.router.navigate( ['/staff/new'], { queryParams: {returnUrl:'/staff' } });
}
edit(id: number) : void {
//console.log("edit staff", id);
this.router.navigate( ['/staff/'+id], { queryParams: {returnUrl:'/staff' } });
}
deleteItem(id: number): void {
this.staffService.deleteStaff(id)
.pipe(take(1))
.subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result);
//let data = this.dataSource.data;
//let index: number = data.findIndex(d => d.id === id);
// console.log(this.msg + "delete from datasource");
//data.splice(index, 1)
//this.dataSource.data = data;
},
error: e => console.error(e)
});
//console.log(this.msg + "click button to delete");
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+2 -2
View File
@@ -1,2 +1,2 @@
+47 -47
View File
@@ -1,48 +1,48 @@
<div class="shadow-2xl rounded mt-2">
<div class="mt-2 mb-2 ml-2">
{{getEditText()}}
</div>
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" >
<div class="ml-2 grid md:grid-cols-2 gap-3 p-2">
<div class="">
<label for="lastname1">Surname<strong class="app-require">*</strong></label>
<input id="lastname1" pInputText formControlName="lastname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
</div>
<div class="">
<label for="firstname">First Name<strong class="app-require">*</strong></label>
<input id="firstname" pInputText formControlName="firstname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
</div>
<div class="">
<label for="login">Email<strong class="app-require">*</strong></label>
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','email')" type="text" class="inputfield w-full">
</div>
<div class="">
<label for="phone" class="w-full">Phone</label>
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
</div>
<div class="">
<label class="flex w-full">Role<strong class="app-require">*</strong></label>
<p-select [options]="Roles" optionLabel="name" optionValue="id" placeholder="Select access Level"
formControlName="roleType" styleClass="w-full p-inputtext-sm mr-1"></p-select >
</div>
<div class="">
<label for="passwd" class="flex w-full">Password</label>
<input id="passwd" pInputText formControlName="password" type="password">
</div>
<div class="grid align-end">
<div class="p-field-checkbox">
<p-checkbox inputId="active" formControlName="active" [binary]="true"></p-checkbox>
<label for="active" class="ml-2 w-full">Active</label>
</div>
</div>
</div>
<div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" iconPos="left" label="Save" [disabled]="isFieldsChange"></button>
<button pButton pRipple class="p-button-sm" type="button" icon="pi pi-times" iconPos="left" label="Cancel" (click)="goBack()"></button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
<div class="shadow-2xl rounded mt-2">
<div class="mt-2 mb-2 ml-2">
{{getEditText()}}
</div>
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" >
<div class="ml-2 grid md:grid-cols-2 gap-3 p-2">
<div class="">
<label for="lastname1">Surname<strong class="app-require">*</strong></label>
<input id="lastname1" pInputText formControlName="lastname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
</div>
<div class="">
<label for="firstname">First Name<strong class="app-require">*</strong></label>
<input id="firstname" pInputText formControlName="firstname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
</div>
<div class="">
<label for="login">Email<strong class="app-require">*</strong></label>
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','email')" type="text" class="inputfield w-full">
</div>
<div class="">
<label for="phone" class="w-full">Phone</label>
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
</div>
<div class="">
<label class="flex w-full">Role<strong class="app-require">*</strong></label>
<p-select [options]="Roles" optionLabel="name" optionValue="id" placeholder="Select access Level"
formControlName="roleType" styleClass="w-full p-inputtext-sm mr-1"></p-select >
</div>
<div class="">
<label for="passwd" class="flex w-full">Password</label>
<input id="passwd" pInputText formControlName="password" type="password">
</div>
<div class="grid align-end">
<div class="p-field-checkbox">
<p-checkbox inputId="active" formControlName="active" [binary]="true"></p-checkbox>
<label for="active" class="ml-2 w-full">Active</label>
</div>
</div>
</div>
<div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" iconPos="left" label="Save" [disabled]="isFieldsChange"></button>
<button pButton pRipple class="p-button-sm" type="button" icon="pi pi-times" iconPos="left" label="Cancel" (click)="goBack()"></button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
</div>
+214 -214
View File
@@ -1,214 +1,214 @@
import { Component, inject, input, numberAttribute, OnDestroy, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs';
import { MessageService } from 'primeng/api';
import {Staff, Code, userRole} from '../models';
import { LookupService, Utils } from '../shares';
import { StaffService } from './staff.service';
import { ButtonModule } from 'primeng/button';
import { SelectModule } from 'primeng/select';
import { CheckboxModule } from 'primeng/checkbox';
import { InputTextModule } from 'primeng/inputtext';
@Component({
templateUrl: 'staff.edit.component.html',
selector: 'staff-edit',
imports:[ButtonModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule],
styleUrls: ['staff.edit.component.css']
})
export class StaffEditComponent implements OnInit, OnDestroy {
returnUrl ='';
loginUser ='';
_error ='';
_id= -1;
Roles: Code[] = [];
id = input(0);
isNew = false;
validationPoints?: Code[];
private staffService = inject(StaffService);
private messageService = inject(MessageService);
private route = inject(ActivatedRoute);
private router = inject(Router);
private lookupService = inject(LookupService);
msg="[adminUser Component] ";
private formBuilder = inject(UntypedFormBuilder);
//for focus input
// @ViewChild('mystaffid') mystaffNo!: MatInput;
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
private subscription:Subscription = new Subscription();
subChanged$ = new Subject<boolean>();
adminuserForm = this.formBuilder.group({
id: [0], //Validators.required
email: ['',Validators.required], //Validators.required
firstname: ['',Validators.required], //Validators.required
lastname: ['',Validators.required], //Validators.required
phone: [''], //Validators.required
password: [''],
active: [false], //Validators.required
roleType: [0,[Validators.required, Validators.min(1)]],
});
constructor() {
this.fillRole();
}
fillRole() : void
{
this.Roles = [];
let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'};
this.Roles.push(item);
item = {id:userRole.Accounting, name: 'Accounting', status:'Accounting'};
this.Roles.push(item);
item = {id:userRole.Normal, name: 'Normal', status:'Normal'};
this.Roles.push(item);
item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'};
this.Roles.push(item);
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
//this.Roles.push(item);
}
getClassForRequire(prev:string,name:string): string {
const notok = !this.adminuserForm.controls[name].valid &&
this.adminuserForm.controls[name].touched;
let str =prev;
if (notok)
str += " ng-invalid ng-dirty";
return str;
}
ngOnInit(): void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
const id = this.id(); // Number(this.route.snapshot.paramMap.get('id'));
// now load thing up
const user = Utils.getCurrentUser();
// console.log(this.msg + "current login user ", user);
if (user.username === '')
alert("you are not login.");
else
this.loginUser = user.firstName;
this._id = id;
//console.log(this.msg + " " + id );
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
if (id > 0)
{
this.isNew = false;
this.subscription.add(
this.staffService.loadStaffById(id).subscribe({
next: x => this.assignValue(x.data),
error: e => console.log(e)
})
);
}
else
{
this.isNew = true;
}
}
getEditText(): string {
if (this._id > 0)
return "Edit Staff";
else
return "New Staff";
}
// this for disable submit button
get isFieldsChange() {
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
assignValue(adminuser:Staff): void {
this.adminuserForm.patchValue({
id: adminuser.id,
email: adminuser.email,
firstname: adminuser.firstname,
lastname: adminuser.lastname,
active: adminuser.active,
phone: adminuser.phone,
roleType: adminuser.roleType,
});
// disable use false//true for not disable.
this.subChanged$.next(true);
}
// convenience getter for easy access in form fields
get f() { return this.adminuserForm.controls;}
validate(adminuser:Staff): boolean {
let result = true;
if (adminuser.email.trim() == '')
{
this._error = 'email is blank or empty';
result = false;
}
else if (adminuser.firstname.trim() == '')
{
this._error = 'Firstname is blank or empty';
result = false;
}
else if (adminuser.lastname.trim() == '')
{
this._error = 'lastname is blank or empty';
result = false;
}
return result;
}
onSubmit(): void {
// if form valid then go save
//make sure
const okToSave = this.adminuserForm.valid;
if (okToSave)
{
let adminuserValue = this.adminuserForm.value;
let adminuser:Staff = {
id: adminuserValue.id,
email: adminuserValue.email,
firstname: adminuserValue.firstname,
lastname: adminuserValue.lastname,
active: adminuserValue.active,
roleType: adminuserValue.roleType,
phone: adminuserValue.phone,
password: adminuserValue.password,
type: 0
};
this._error ='';
const allOK = this.validate(adminuser);
if (allOK)
{
this.subscription.add (
this.staffService.saveStaff(adminuser).subscribe({
next: x => {
if (x.statusCode >= 1)
{
this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.firstname + " " + adminuser.lastname });
this.router.navigate([this.returnUrl]);
}
else
{
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
}
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
console.log("error ", e);
}
})
);
}
else
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
}
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Component, inject, input, numberAttribute, OnDestroy, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs';
import { MessageService } from 'primeng/api';
import {Staff, Code, userRole} from '../models';
import { LookupService, Utils } from '../shares';
import { StaffService } from './staff.service';
import { ButtonModule } from 'primeng/button';
import { SelectModule } from 'primeng/select';
import { CheckboxModule } from 'primeng/checkbox';
import { InputTextModule } from 'primeng/inputtext';
@Component({
templateUrl: 'staff.edit.component.html',
selector: 'staff-edit',
imports:[ButtonModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule],
styleUrls: ['staff.edit.component.css']
})
export class StaffEditComponent implements OnInit, OnDestroy {
returnUrl ='';
loginUser ='';
_error ='';
_id= -1;
Roles: Code[] = [];
id = input(0);
isNew = false;
validationPoints?: Code[];
private staffService = inject(StaffService);
private messageService = inject(MessageService);
private route = inject(ActivatedRoute);
private router = inject(Router);
private lookupService = inject(LookupService);
msg="[adminUser Component] ";
private formBuilder = inject(UntypedFormBuilder);
//for focus input
// @ViewChild('mystaffid') mystaffNo!: MatInput;
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
private subscription:Subscription = new Subscription();
subChanged$ = new Subject<boolean>();
adminuserForm = this.formBuilder.group({
id: [0], //Validators.required
email: ['',Validators.required], //Validators.required
firstname: ['',Validators.required], //Validators.required
lastname: ['',Validators.required], //Validators.required
phone: [''], //Validators.required
password: [''],
active: [false], //Validators.required
roleType: [0,[Validators.required, Validators.min(1)]],
});
constructor() {
this.fillRole();
}
fillRole() : void
{
this.Roles = [];
let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'};
this.Roles.push(item);
item = {id:userRole.Accounting, name: 'Accounting', status:'Accounting'};
this.Roles.push(item);
item = {id:userRole.Normal, name: 'Normal', status:'Normal'};
this.Roles.push(item);
item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'};
this.Roles.push(item);
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
//this.Roles.push(item);
}
getClassForRequire(prev:string,name:string): string {
const notok = !this.adminuserForm.controls[name].valid &&
this.adminuserForm.controls[name].touched;
let str =prev;
if (notok)
str += " ng-invalid ng-dirty";
return str;
}
ngOnInit(): void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
const id = this.id(); // Number(this.route.snapshot.paramMap.get('id'));
// now load thing up
const user = Utils.getCurrentUser();
// console.log(this.msg + "current login user ", user);
if (user.username === '')
alert("you are not login.");
else
this.loginUser = user.firstName;
this._id = id;
//console.log(this.msg + " " + id );
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
if (id > 0)
{
this.isNew = false;
this.subscription.add(
this.staffService.loadStaffById(id).subscribe({
next: x => this.assignValue(x.data),
error: e => console.log(e)
})
);
}
else
{
this.isNew = true;
}
}
getEditText(): string {
if (this._id > 0)
return "Edit Staff";
else
return "New Staff";
}
// this for disable submit button
get isFieldsChange() {
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
assignValue(adminuser:Staff): void {
this.adminuserForm.patchValue({
id: adminuser.id,
email: adminuser.email,
firstname: adminuser.firstname,
lastname: adminuser.lastname,
active: adminuser.active,
phone: adminuser.phone,
roleType: adminuser.roleType,
});
// disable use false//true for not disable.
this.subChanged$.next(true);
}
// convenience getter for easy access in form fields
get f() { return this.adminuserForm.controls;}
validate(adminuser:Staff): boolean {
let result = true;
if (adminuser.email.trim() == '')
{
this._error = 'email is blank or empty';
result = false;
}
else if (adminuser.firstname.trim() == '')
{
this._error = 'Firstname is blank or empty';
result = false;
}
else if (adminuser.lastname.trim() == '')
{
this._error = 'lastname is blank or empty';
result = false;
}
return result;
}
onSubmit(): void {
// if form valid then go save
//make sure
const okToSave = this.adminuserForm.valid;
if (okToSave)
{
let adminuserValue = this.adminuserForm.value;
let adminuser:Staff = {
id: adminuserValue.id,
email: adminuserValue.email,
firstname: adminuserValue.firstname,
lastname: adminuserValue.lastname,
active: adminuserValue.active,
roleType: adminuserValue.roleType,
phone: adminuserValue.phone,
password: adminuserValue.password,
type: 0
};
this._error ='';
const allOK = this.validate(adminuser);
if (allOK)
{
this.subscription.add (
this.staffService.saveStaff(adminuser).subscribe({
next: x => {
if (x.statusCode >= 1)
{
this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.firstname + " " + adminuser.lastname });
this.router.navigate([this.returnUrl]);
}
else
{
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
}
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
console.log("error ", e);
}
})
);
}
else
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
}
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+2 -2
View File
@@ -1,2 +1,2 @@
+23 -23
View File
@@ -1,24 +1,24 @@
<div class="shadow-2xl rounded mt-2">
<div class="mt-2 mb-2 ml-2">
{{getEditText()}}
</div>
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" >
<div class="ml-2 formgrid grid">
<div class="field col-12 md:col-3">
<label class="w-full">Password<strong class="app-require">*</strong></label>
<input id="password" pInputText formControlName="password" type="password" >
</div>
<div class="field col-12 md:col-3">
<label class="w-full">Password Again<strong class="app-require">*</strong></label>
<input id="passwordA" pInputText formControlName="passwordAgain" type="password">
</div>
</div>
<div class="flex justify-content-end mr-2 mb-2">
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" iconPos="left" label="Save" [disabled]="isFieldsChange"></button>
<button pButton pRipple class="p-button-sm" type="button" icon="pi pi-times" iconPos="left" label="Cancel" (click)="goBack()"></button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
<div class="shadow-2xl rounded mt-2">
<div class="mt-2 mb-2 ml-2">
{{getEditText()}}
</div>
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit()" >
<div class="ml-2 formgrid grid">
<div class="field col-12 md:col-3">
<label class="w-full">Password<strong class="app-require">*</strong></label>
<input id="password" pInputText formControlName="password" type="password" >
</div>
<div class="field col-12 md:col-3">
<label class="w-full">Password Again<strong class="app-require">*</strong></label>
<input id="passwordA" pInputText formControlName="passwordAgain" type="password">
</div>
</div>
<div class="flex justify-content-end mr-2 mb-2">
<button pButton type="submit" class="p-button-sm mr-2" icon="pi pi-check" iconPos="left" label="Save" [disabled]="isFieldsChange"></button>
<button pButton pRipple class="p-button-sm" type="button" icon="pi pi-times" iconPos="left" label="Cancel" (click)="goBack()"></button>
</div>
</form>
<!--pre>{{ adminuserForm.value|json}}</pre-->
</div>
+191 -191
View File
@@ -1,191 +1,191 @@
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs';
import { MessageService } from 'primeng/api';
import {Staff, Code, userRole, ResetPassword} from '../models';
import { LookupService, Utils } from '../shares';
import { StaffService } from './staff.service';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
@Component({
templateUrl: 'staff.pass.component.html',
selector: 'staff-pass',
imports:[ReactiveFormsModule,CommonModule,ButtonModule],
styleUrls: ['staff.pass.component.css']
})
export class StaffPassComponent implements OnInit, OnDestroy {
returnUrl ='';
loginUser ='';
_error ='';
_id= -1;
isNew = false;
validationPoints?: Code[];
msg="[adminUser Component] ";
private formBuilder = inject(UntypedFormBuilder);
//for focus input
// @ViewChild('mystaffid') mystaffNo!: MatInput;
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
private subscription:Subscription = new Subscription();
subChanged$ = new Subject<boolean>();
adminuserForm = this.formBuilder.group({
id: [0], //Validators.required
password: ['',Validators.required], //Validators.required
passwordAgain: ['',Validators.required], //Validators.required
});
constructor(
private staffService: StaffService,
private messageService: MessageService,
private lookupService: LookupService,
private router: Router, private route: ActivatedRoute,
) {
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
//this.Roles.push(item);
}
getClassForRequire(prev:string,name:string){
const notok = !this.adminuserForm.controls[name].valid &&
this.adminuserForm.controls[name].touched;
let str =prev;
if (notok)
str += " ng-invalid ng-dirty";
return str;
}
ngOnInit():void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
const id = Number(this.route.snapshot.paramMap.get('id'));
// now load thing up
const user = Utils.getCurrentUser();
// console.log(this.msg + "current login user ", user);
if (user.username === '')
alert("you are not login.");
else
this.loginUser = user.firstName;
this._id = id;
//console.log(this.msg + " " + id );
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}
getEditText(): string {
return "Reset Password";
}
// this for disable submit button
get isFieldsChange() {
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
searchUser(): void {
const staffid = this.adminuserForm.controls["stafflinkNo"].value;
// console.log(this.msg + " the staffid is ", staffid);
if (staffid != '') {
//this.form.controls['your form control name'].value
this.staffService.loadStaffById(staffid).subscribe({
next: x => {
//console.log(this.msg + " this is stafflink no ", x);
// now get current user.
if (x.statusCode == 1) {
//this.error = true;
const fname = x.data.firstname + " " + x.data.lastname;
this.adminuserForm.patchValue({name:fname, addedBy:this.loginUser});
}
else {
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
}
},
error: e => {
const message = e || e.message;
//this.toastr.error(message);
console.log("error ", e);
}
});
}
else
{
//this.toastr.error("please enter staff link no");
}
}
// convenience getter for easy access in form fields
get f() { return this.adminuserForm.controls;}
validate(adminuser:any, passwordAgain:string): boolean {
let result = true;
const pass = adminuser.password.trim();
if (pass == '')
{
this._error = 'password is blank or empty';
result = false;
}
if (pass !== passwordAgain)
{
this._error = 'password is not match';
result = false;
}
return result;
}
onSubmit(): void {
// if form valid then go save
//make sure
const okToSave = this.adminuserForm.valid;
if (okToSave)
{
let adminuserValue = this.adminuserForm.value;
let adminuser:ResetPassword = {
id: this._id,
password: adminuserValue.password,
};
this._error ='';
const again = adminuserValue.passwordAgain;
const allOK = this.validate(adminuserValue, again);
if (allOK)
{
this.subscription.add (
this.staffService.saveResetPassword(adminuser).subscribe({
next: x => {
if (x.statusCode >= 1)
{
this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.id.toString() });
this.router.navigate([this.returnUrl]);
}
else
{
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
}
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
console.log("error ", e);
}
})
);
}
else
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
}
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs';
import { MessageService } from 'primeng/api';
import {Staff, Code, userRole, ResetPassword} from '../models';
import { LookupService, Utils } from '../shares';
import { StaffService } from './staff.service';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button';
@Component({
templateUrl: 'staff.pass.component.html',
selector: 'staff-pass',
imports:[ReactiveFormsModule,CommonModule,ButtonModule],
styleUrls: ['staff.pass.component.css']
})
export class StaffPassComponent implements OnInit, OnDestroy {
returnUrl ='';
loginUser ='';
_error ='';
_id= -1;
isNew = false;
validationPoints?: Code[];
msg="[adminUser Component] ";
private formBuilder = inject(UntypedFormBuilder);
//for focus input
// @ViewChild('mystaffid') mystaffNo!: MatInput;
isChange = true; // disable use false//true for not disable. make sure it true is disable button.
private subscription:Subscription = new Subscription();
subChanged$ = new Subject<boolean>();
adminuserForm = this.formBuilder.group({
id: [0], //Validators.required
password: ['',Validators.required], //Validators.required
passwordAgain: ['',Validators.required], //Validators.required
});
constructor(
private staffService: StaffService,
private messageService: MessageService,
private lookupService: LookupService,
private router: Router, private route: ActivatedRoute,
) {
//item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
//this.Roles.push(item);
}
getClassForRequire(prev:string,name:string){
const notok = !this.adminuserForm.controls[name].valid &&
this.adminuserForm.controls[name].touched;
let str =prev;
if (notok)
str += " ng-invalid ng-dirty";
return str;
}
ngOnInit():void {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
const id = Number(this.route.snapshot.paramMap.get('id'));
// now load thing up
const user = Utils.getCurrentUser();
// console.log(this.msg + "current login user ", user);
if (user.username === '')
alert("you are not login.");
else
this.loginUser = user.firstName;
this._id = id;
//console.log(this.msg + " " + id );
this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}
getEditText(): string {
return "Reset Password";
}
// this for disable submit button
get isFieldsChange() {
const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan);
return chan;
}
searchUser(): void {
const staffid = this.adminuserForm.controls["stafflinkNo"].value;
// console.log(this.msg + " the staffid is ", staffid);
if (staffid != '') {
//this.form.controls['your form control name'].value
this.staffService.loadStaffById(staffid).subscribe({
next: x => {
//console.log(this.msg + " this is stafflink no ", x);
// now get current user.
if (x.statusCode == 1) {
//this.error = true;
const fname = x.data.firstname + " " + x.data.lastname;
this.adminuserForm.patchValue({name:fname, addedBy:this.loginUser});
}
else {
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
}
},
error: e => {
const message = e || e.message;
//this.toastr.error(message);
console.log("error ", e);
}
});
}
else
{
//this.toastr.error("please enter staff link no");
}
}
// convenience getter for easy access in form fields
get f() { return this.adminuserForm.controls;}
validate(adminuser:any, passwordAgain:string): boolean {
let result = true;
const pass = adminuser.password.trim();
if (pass == '')
{
this._error = 'password is blank or empty';
result = false;
}
if (pass !== passwordAgain)
{
this._error = 'password is not match';
result = false;
}
return result;
}
onSubmit(): void {
// if form valid then go save
//make sure
const okToSave = this.adminuserForm.valid;
if (okToSave)
{
let adminuserValue = this.adminuserForm.value;
let adminuser:ResetPassword = {
id: this._id,
password: adminuserValue.password,
};
this._error ='';
const again = adminuserValue.passwordAgain;
const allOK = this.validate(adminuserValue, again);
if (allOK)
{
this.subscription.add (
this.staffService.saveResetPassword(adminuser).subscribe({
next: x => {
if (x.statusCode >= 1)
{
this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.id.toString() });
this.router.navigate([this.returnUrl]);
}
else
{
this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
}
},
error: e => {
const message = e || e.message;
// this.toastr.error(message);
console.log("error ", e);
}
})
);
}
else
this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
}
}
goBack(): void {
this.router.navigate([this.returnUrl]);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
+55 -55
View File
@@ -1,55 +1,55 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword } from '../models';
import { AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class StaffService {
public searchCriteria: StaffSearch;
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {
this.searchCriteria = {
email:'',
firstName: '',
lastName:''
};
}
searchStaffs(criteria: StaffSearch): Observable<ResultModel<StaffView[]>> {
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl + "/SearchStaff";
return this.http.post<ResultModel<StaffView[]>>(baseUrl, criteria, config);
}
loadStaffById(id:number): Observable<ResultModel<Staff>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl;
/*
const params = new HttpParams().set("id", ""+id);
const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = {
headers: headers,
params: params
};
*/
//return this.http.get<AdminUser>(this.baseUrl + 'api/Adminuser/LoadAdminuserById',options);
return this.http.get<ResultModel<Staff>>(baseUrl + "/" + id);
}
saveStaff(data:Staff): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/SaveStaff";
return this.http.post<ResultModel<number>>(baseUrl, data, config);
}
saveResetPassword(data:ResetPassword): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/ResetPassStaff";
return this.http.post<ResultModel<number>>(baseUrl, data, config);
}
deleteStaff(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl;
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id);
}
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword } from '../models';
import { AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' })
export class StaffService {
public searchCriteria: StaffSearch;
constructor(private http: HttpClient,
private appSetting :AppSettingService
) {
this.searchCriteria = {
email:'',
firstName: '',
lastName:''
};
}
searchStaffs(criteria: StaffSearch): Observable<ResultModel<StaffView[]>> {
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl + "/SearchStaff";
return this.http.post<ResultModel<StaffView[]>>(baseUrl, criteria, config);
}
loadStaffById(id:number): Observable<ResultModel<Staff>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl;
/*
const params = new HttpParams().set("id", ""+id);
const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = {
headers: headers,
params: params
};
*/
//return this.http.get<AdminUser>(this.baseUrl + 'api/Adminuser/LoadAdminuserById',options);
return this.http.get<ResultModel<Staff>>(baseUrl + "/" + id);
}
saveStaff(data:Staff): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/SaveStaff";
return this.http.post<ResultModel<number>>(baseUrl, data, config);
}
saveResetPassword(data:ResetPassword): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/ResetPassStaff";
return this.http.post<ResultModel<number>>(baseUrl, data, config);
}
deleteStaff(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl;
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id);
}
}
+5 -5
View File
@@ -1,6 +1,6 @@
import { StaffView } from '../models';
export interface AppState {
staffs: Array<StaffView>;
staffloaded : boolean;
import { StaffView } from '../models';
export interface AppState {
staffs: Array<StaffView>;
staffloaded : boolean;
}
+4 -4
View File
@@ -1,5 +1,5 @@
export * from './app.state';
export * from './staff.actions';
export * from './staff.effects';
export * from './staff.reducer';
export * from './app.state';
export * from './staff.actions';
export * from './staff.effects';
export * from './staff.reducer';
export * from './staff.selectors';
+18 -18
View File
@@ -1,19 +1,19 @@
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { StaffSearch, StaffView } from '../models';
export const StaffsActions = createActionGroup({
source: 'Staffs',
events: {
'Add Staff': props<{ staffId: number }>(),
'Remove Staff': props<{ staffId: number }>(),
'Load Staff': props<{criteria: StaffSearch}>(),
},
});
export const StaffsApiActions = createActionGroup({
source: 'Staffs API',
events: {
'LoadStaffSuccess': props<{ staffs: Array<StaffView>; staffLoad: boolean }>(),
'LoadStaffFail': props<{ error: string }>(),
},
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { StaffSearch, StaffView } from '../models';
export const StaffsActions = createActionGroup({
source: 'Staffs',
events: {
'Add Staff': props<{ staffId: number }>(),
'Remove Staff': props<{ staffId: number }>(),
'Load Staff': props<{criteria: StaffSearch}>(),
},
});
export const StaffsApiActions = createActionGroup({
source: 'Staffs API',
events: {
'LoadStaffSuccess': props<{ staffs: Array<StaffView>; staffLoad: boolean }>(),
'LoadStaffFail': props<{ error: string }>(),
},
});
+34 -34
View File
@@ -1,35 +1,35 @@
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, filter, map, of, switchMap, exhaustMap } from 'rxjs';
//import { concatLatestFrom } from '@ngrx/operators';
import { HttpErrorResponse } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { StaffsActions, StaffsApiActions } from './staff.actions';
import { StaffService } from '../staff/staff.service';
import { StaffSearch } from '../models';
/* how to use this load and put in store. then use selector to get slide of entity
this.store.dispatch(StaffsApiActions.loadStaff());
then using
selector to get from store
*/
@Injectable()
export class staffEffects {
private actions$: Actions = inject(Actions);
private staffService: StaffService = inject(StaffService);
private store: Store = inject(Store);
LoadStaffs$ = createEffect(() =>
this.actions$.pipe(
ofType(StaffsActions.loadStaff),
exhaustMap((action: {criteria:StaffSearch}) =>
this.staffService.searchStaffs(action.criteria).pipe(
map((response) => StaffsApiActions.loadStaffSuccess({staffs: response.data, staffLoad: true})),
catchError((err: HttpErrorResponse) => of(StaffsApiActions.loadStaffFail({ error: err.error})))
)
)
));
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, filter, map, of, switchMap, exhaustMap } from 'rxjs';
//import { concatLatestFrom } from '@ngrx/operators';
import { HttpErrorResponse } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { StaffsActions, StaffsApiActions } from './staff.actions';
import { StaffService } from '../staff/staff.service';
import { StaffSearch } from '../models';
/* how to use this load and put in store. then use selector to get slide of entity
this.store.dispatch(StaffsApiActions.loadStaff());
then using
selector to get from store
*/
@Injectable()
export class staffEffects {
private actions$: Actions = inject(Actions);
private staffService: StaffService = inject(StaffService);
private store: Store = inject(Store);
LoadStaffs$ = createEffect(() =>
this.actions$.pipe(
ofType(StaffsActions.loadStaff),
exhaustMap((action: {criteria:StaffSearch}) =>
this.staffService.searchStaffs(action.criteria).pipe(
map((response) => StaffsApiActions.loadStaffSuccess({staffs: response.data, staffLoad: true})),
catchError((err: HttpErrorResponse) => of(StaffsApiActions.loadStaffFail({ error: err.error})))
)
)
));
}
+24 -24
View File
@@ -1,24 +1,24 @@
import { createReducer, on } from '@ngrx/store';
import { StaffsApiActions } from './staff.actions';
import { StaffView } from '../models';
import { AppState } from './app.state';
export const initialState: AppState ={
staffs:[],
staffloaded: false
}
export const staffsReducer = createReducer(
initialState,
on(StaffsApiActions.loadStaffSuccess, (
state, action) => {
return {
...state,
staffs: action.staffs,
staffloaded: true
}
}
)
);
import { createReducer, on } from '@ngrx/store';
import { StaffsApiActions } from './staff.actions';
import { StaffView } from '../models';
import { AppState } from './app.state';
export const initialState: AppState ={
staffs:[],
staffloaded: false
}
export const staffsReducer = createReducer(
initialState,
on(StaffsApiActions.loadStaffSuccess, (
state, action) => {
return {
...state,
staffs: action.staffs,
staffloaded: true
}
}
)
);
+16 -16
View File
@@ -1,16 +1,16 @@
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { StaffView } from '../models';
import { AppState } from './app.state';
export const selectState = createFeatureSelector<AppState>('myStaffs');
// 2. Create a selector for a specific piece of state
export const selectStaff = createSelector(
selectState,
(state: AppState) => state.staffs
);
// 2. Create a selector for a specific piece of state
export const selectStaffLoaded = createSelector(
selectState,
(state: AppState) => state.staffloaded);
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { StaffView } from '../models';
import { AppState } from './app.state';
export const selectState = createFeatureSelector<AppState>('myStaffs');
// 2. Create a selector for a specific piece of state
export const selectStaff = createSelector(
selectState,
(state: AppState) => state.staffs
);
// 2. Create a selector for a specific piece of state
export const selectStaffLoaded = createSelector(
selectState,
(state: AppState) => state.staffloaded);
+43 -43
View File
@@ -1,44 +1,44 @@
@if (currentUserS() && isAuth == true)
{
<div class="flex flex-column justify-between md:flex-row rounded" style="background-color:transparent">
<div style="margin-top:5px;padding-right:35px; cursor: pointer;width:400px;">
<img (click)="onHome()" src="images/application_image.png" alt="Banner" style="height:80px;width:100%" />
</div>
<div class="flex justify-end items-end flex-row">
<div class="flex-column">
<label class=" flex justify-end items-end mr-1 mb-2 myname">{{loginUser}}</label>
<div>
<span class="hidden md:inline-flex"> <!--hidden when small screen-->
<p-buttonGroup >
<button type="button" pButton icon="pi pi-slack" label="Home" (click)="onHome()"
class="p-button-sm " [attr.disabled]="isHome?true:null"></button>
<!--button *ngIf="hasRoleAdmin" type="button" pButton icon="pi pi-file" label="Report" (click)="onReport()"
[attr.disabled]="isReport?true:null" class="p-button-sm "></button-->
<p-menu #reportmenu [popup]="true" [model]="reportMenu"></p-menu>
<p-menu #sysmenu [popup]="true" [model]="systemMenu"></p-menu>
@if (hasRoleAdmin)
{
<button type="button" pButton icon="pi pi-chevron-down" iconPos="right" label="System"
(click)="sysmenu.toggle($event);" class="p-button-sm ">
<!--i class="pi pi-cog mr-2" style="font-size: 1rem"></i-->
</button>
}
<button pButton type="button" icon="pi pi-power-off" iconPos="left" label="Logout"
(click)="logout()" class="p-button-sm p"></button>
</p-buttonGroup>
</span>
</div>
</div>
</div>
</div>
<!--put in the left new row if in mobile not much room-->
<span class="md:hidden flex justify-start"> <!--hidden on md screen show on small-->
<button type="button" pButton icon="pi pi-align-justify" (click)="onemenu.toggle($event);"></button>
<p-menu #onemenu [popup]="true" [model]="oneMenu"></p-menu>
</span>
@if (currentUserS() && isAuth == true)
{
<div class="flex flex-column justify-between md:flex-row rounded" style="background-color:transparent">
<div style="margin-top:5px;padding-right:35px; cursor: pointer;width:400px;">
<img (click)="onHome()" src="images/application_image.png" alt="Banner" style="height:80px;width:100%" />
</div>
<div class="flex justify-end items-end flex-row">
<div class="flex-column">
<label class=" flex justify-end items-end mr-1 mb-2 myname">{{loginUser}}</label>
<div>
<span class="hidden md:inline-flex"> <!--hidden when small screen-->
<p-buttonGroup >
<button type="button" pButton icon="pi pi-slack" label="Home" (click)="onHome()"
class="p-button-sm " [attr.disabled]="isHome?true:null"></button>
<!--button *ngIf="hasRoleAdmin" type="button" pButton icon="pi pi-file" label="Report" (click)="onReport()"
[attr.disabled]="isReport?true:null" class="p-button-sm "></button-->
<p-menu #reportmenu [popup]="true" [model]="reportMenu"></p-menu>
<p-menu #sysmenu [popup]="true" [model]="systemMenu"></p-menu>
@if (hasRoleAdmin)
{
<button type="button" pButton icon="pi pi-chevron-down" iconPos="right" label="System"
(click)="sysmenu.toggle($event);" class="p-button-sm ">
<!--i class="pi pi-cog mr-2" style="font-size: 1rem"></i-->
</button>
}
<button pButton type="button" icon="pi pi-power-off" iconPos="left" label="Logout"
(click)="logout()" class="p-button-sm p"></button>
</p-buttonGroup>
</span>
</div>
</div>
</div>
</div>
<!--put in the left new row if in mobile not much room-->
<span class="md:hidden flex justify-start"> <!--hidden on md screen show on small-->
<button type="button" pButton icon="pi pi-align-justify" (click)="onemenu.toggle($event);"></button>
<p-menu #onemenu [popup]="true" [model]="oneMenu"></p-menu>
</span>
}
+181 -181
View File
@@ -1,181 +1,181 @@
import { Component, OnInit, OnDestroy, effect, computed, Signal, } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { MenuItem } from 'primeng/api';
import { MenuModule } from 'primeng/menu';
import { ButtonModule} from 'primeng/button';
import { ButtonGroupModule } from 'primeng/buttongroup';
import { Subscription } from 'rxjs';
import { AuthenticationService } from '../user-services';
import { Utils } from '../shares';
import { User } from '../models';
@Component({
selector: 'app-toolbar',
imports: [CommonModule, MenuModule, ButtonModule, ButtonGroupModule],
templateUrl: './toolbar.component.html',
styleUrl: './toolbar.component.css'
})
export class ToolbarComponent implements OnInit, OnDestroy {
title = 'Safe Assessment Unit UI';
isAuth = false;
hasRoleAdmin = false;
hasRoleReport = false;
homeUrl = "/person";
oneMenu: MenuItem[] = [];
reportMenu: MenuItem[] = [];
systemMenu: MenuItem[] = [];
currentUserName = "";
currentUserS: Signal<User> = computed (() => {
const currentUser = this.authenticationService.authChange();
this.isAuth =false;
if (currentUser.username) {
this.isAuth =true;
this.assignRole(currentUser);
console.log(`${this.title} on auth currentUser signal now ${this.isAuth}`);
// add this to the one menu
if (this.hasRoleAdmin) {
let i = 0;
for (i = 0; i < this.systemMenu.length; i++) {
this.oneMenu.push(this.systemMenu[i]);
}
}
//add the logout here
this.oneMenu.push(
{
label: 'Logout', icon: 'pi pi-power-off', command: () => {
this.logout()
}
}
);
}
return currentUser;
});
private subscription: Subscription = new Subscription();
constructor(
private router: Router,
private authenticationService: AuthenticationService
) {
this.authenticationService.isReport = false;
this.authenticationService.isHome = true;
}
onHome(): void {
this.authenticationService.isHome = true;
this.router.navigate([this.homeUrl]);
}
get isHome(): boolean {
return this.authenticationService.isHome;
}
get loginUser(): string {
let result = "";
let greeting = "Good ";
const d = new Date();
const hour = d.getHours();
if (hour >= 5 && hour < 12)
greeting = "Good morning, ";
else if (hour >= 12 && hour < 17)
greeting = "Good afternoon, ";
else if (hour >= 17 && hour < 21)
greeting = "Good evening, ";
else
greeting = "Good night, ";
if (this.isAuth) {
const user = Utils.getCurrentUser();
result = user.firstName;
}
return greeting + result;
}
get isReport(): boolean {
return this.authenticationService.isReport;
}
initOneMenu(): void {
this.oneMenu = [
{
label: 'Home', icon: 'pi pi-slack', command: () => {
this.router.navigate([this.homeUrl]);
}
}
];
}
initSystemMenu(): void {
this.systemMenu = [
/*
{
label: 'Lookup', icon: 'pi pi-bars', command: () => {
this.router.navigate(['/lookup']);
this.authenticationService.isReport = false;
this.authenticationService.isHome = false;
}
},
{
label: 'Waiting List', icon: 'pi pi-map-marker', command: () => {
this.router.navigate(['/waitinglist']);
this.authenticationService.isReport = false;
this.authenticationService.isHome = false;
}
},
*/
{
label: 'Person', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/person']);
}
},
{
label: 'Import', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/import']);
}
},
{
label: 'Family tree', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/familytree']);
}
},
{
label: 'Staff', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/staff']);
}
},
];
}
logout(): void {
this.initOneMenu();
this.authenticationService.logout();
this.router.navigate(['/login']);
}
assignRole(user: User): void {
const userRole = Utils.getUserRole(user);
this.hasRoleReport = Utils.canRunReport(userRole);
this.currentUserName = user.firstName;
//this.hasRoleAdmin = true;
this.hasRoleAdmin = userRole == Utils.getAdminRoleName();
//console.log(this.msg + " hasRoleAdmin ", this.hasRoleAdmin);
}
ngOnInit(): void {
this.initOneMenu();
this.initSystemMenu();
console.log("ngOnInit toolbar called");
// this.initStart();
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
import { Component, OnInit, OnDestroy, effect, computed, Signal, } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { MenuItem } from 'primeng/api';
import { MenuModule } from 'primeng/menu';
import { ButtonModule} from 'primeng/button';
import { ButtonGroupModule } from 'primeng/buttongroup';
import { Subscription } from 'rxjs';
import { AuthenticationService } from '../user-services';
import { Utils } from '../shares';
import { User } from '../models';
@Component({
selector: 'app-toolbar',
imports: [CommonModule, MenuModule, ButtonModule, ButtonGroupModule],
templateUrl: './toolbar.component.html',
styleUrl: './toolbar.component.css'
})
export class ToolbarComponent implements OnInit, OnDestroy {
title = 'Safe Assessment Unit UI';
isAuth = false;
hasRoleAdmin = false;
hasRoleReport = false;
homeUrl = "/person";
oneMenu: MenuItem[] = [];
reportMenu: MenuItem[] = [];
systemMenu: MenuItem[] = [];
currentUserName = "";
currentUserS: Signal<User> = computed (() => {
const currentUser = this.authenticationService.authChange();
this.isAuth =false;
if (currentUser.username) {
this.isAuth =true;
this.assignRole(currentUser);
console.log(`${this.title} on auth currentUser signal now ${this.isAuth}`);
// add this to the one menu
if (this.hasRoleAdmin) {
let i = 0;
for (i = 0; i < this.systemMenu.length; i++) {
this.oneMenu.push(this.systemMenu[i]);
}
}
//add the logout here
this.oneMenu.push(
{
label: 'Logout', icon: 'pi pi-power-off', command: () => {
this.logout()
}
}
);
}
return currentUser;
});
private subscription: Subscription = new Subscription();
constructor(
private router: Router,
private authenticationService: AuthenticationService
) {
this.authenticationService.isReport = false;
this.authenticationService.isHome = true;
}
onHome(): void {
this.authenticationService.isHome = true;
this.router.navigate([this.homeUrl]);
}
get isHome(): boolean {
return this.authenticationService.isHome;
}
get loginUser(): string {
let result = "";
let greeting = "Good ";
const d = new Date();
const hour = d.getHours();
if (hour >= 5 && hour < 12)
greeting = "Good morning, ";
else if (hour >= 12 && hour < 17)
greeting = "Good afternoon, ";
else if (hour >= 17 && hour < 21)
greeting = "Good evening, ";
else
greeting = "Good night, ";
if (this.isAuth) {
const user = Utils.getCurrentUser();
result = user.firstName;
}
return greeting + result;
}
get isReport(): boolean {
return this.authenticationService.isReport;
}
initOneMenu(): void {
this.oneMenu = [
{
label: 'Home', icon: 'pi pi-slack', command: () => {
this.router.navigate([this.homeUrl]);
}
}
];
}
initSystemMenu(): void {
this.systemMenu = [
/*
{
label: 'Lookup', icon: 'pi pi-bars', command: () => {
this.router.navigate(['/lookup']);
this.authenticationService.isReport = false;
this.authenticationService.isHome = false;
}
},
{
label: 'Waiting List', icon: 'pi pi-map-marker', command: () => {
this.router.navigate(['/waitinglist']);
this.authenticationService.isReport = false;
this.authenticationService.isHome = false;
}
},
*/
{
label: 'Person', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/person']);
}
},
{
label: 'Import', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/import']);
}
},
{
label: 'Family tree', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/familytree']);
}
},
{
label: 'Staff', icon: 'pi pi-users',
command: () => {
this.router.navigate(['/staff']);
}
},
];
}
logout(): void {
this.initOneMenu();
this.authenticationService.logout();
this.router.navigate(['/login']);
}
assignRole(user: User): void {
const userRole = Utils.getUserRole(user);
this.hasRoleReport = Utils.canRunReport(userRole);
this.currentUserName = user.firstName;
//this.hasRoleAdmin = true;
this.hasRoleAdmin = userRole == Utils.getAdminRoleName();
//console.log(this.msg + " hasRoleAdmin ", this.hasRoleAdmin);
}
ngOnInit(): void {
this.initOneMenu();
this.initSystemMenu();
console.log("ngOnInit toolbar called");
// this.initStart();
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}

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