Compare commits

...
24 Commits
Author SHA1 Message Date
kham 1a9316ed6f put in new function call 2026-09-26 09:23:54 +10:00
kham f3b1d901a8 put in vieww 2026-09-24 22:28:34 +10:00
kham df7e8827be change to v22 and ui optimus ng 2026-09-20 23:07:32 +10:00
kham 2e477ca023 change to person id to photo id 2026-09-20 17:38:15 +10:00
kham 5292b99af6 fixed download if no image return empty 2026-09-20 15:30:22 +10:00
kham 3d83e95613 put in new ui and backend store image to table 2026-09-20 14:32:59 +10:00
kham 75b49be476 change all save to database no folder anymore 2026-09-19 22:37:43 +10:00
kham ba4976fce4 add clear log provide 2026-08-23 18:15:40 +10:00
khamandand logout too 4eafa2fa8d add in cookie in now
Co-authored-by: and logout too
2026-08-14 17:59:47 +10:00
kham 70083f1230 commit first 2026-08-14 17:52:42 +10:00
kham 9070bebb54 put in two launch.json and tasks.json 2026-08-05 10:49:18 +10:00
kham 5d2e989ee9 fixed the program.cs and run in VC code 2026-07-30 17:23:48 +10:00
kham ce2b3ba8c9 add toolbar layout on the left when mobile on below image and text 2026-05-17 13:07:11 +10:00
kham c9228950d5 add check db is not there create db not there same as table 2026-05-16 23:22:42 +10:00
kham 2e7f9e7a27 add menu on left 2026-05-16 19:16:47 +10:00
kham d656229ab3 remove hard code password and username 2026-05-15 22:07:13 +10:00
kham fa3b289a77 update config file and sample graphql 2026-05-15 21:34:45 +10:00
kham 09ac6c6bb1 add nginx in arch linux style 2026-05-14 21:59:52 +10:00
kham 8228ff27b8 adding config for arch linux and nginx 2026-05-14 21:59:19 +10:00
kham c0bc89083d put in fixed error in appsetting.cs does not have other key 2026-05-13 18:22:17 +10:00
kham fb751ecbc3 add file deploy in linux with nginx configure file 2026-05-12 21:39:26 +10:00
kham d4543b5169 add new folder wsl 2026-05-12 21:02:00 +10:00
kham adf62c8dcc change namme and project 2026-05-12 20:59:47 +10:00
kham 936094a4bf remove ensure mirgration
and scope.serviceProvider.getService
2026-05-10 21:49:38 +10:00
131 changed files with 11574 additions and 11413 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. # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# User-specific files # User-specific files
*.rsuser *.rsuser
*.suo *.suo
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs
# Mono auto generated files # Mono auto generated files
mono_crash.* mono_crash.*
# Build results # Build results
[Dd]ebug/ [Dd]ebug/
[Dd]ebugPublic/ [Dd]ebugPublic/
[Rr]elease/ [Rr]elease/
[Rr]eleases/ [Rr]eleases/
myZip/ myZip/
x64/ x64/
x86/ x86/
[Ww][Ii][Nn]32/ [Ww][Ii][Nn]32/
[Aa][Rr][Mm]/ [Aa][Rr][Mm]/
[Aa][Rr][Mm]64/ [Aa][Rr][Mm]64/
bld/ bld/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
[Oo]ut/ [Oo]ut/
[Ll]og/ [Ll]og/
[Ll]ogs/ [Ll]ogs/
# Compiled output # Compiled output
dist/ dist/
bin/ bin/
obj/ obj/
tmp/ tmp/
**/out-tsc **/out-tsc
**/bazel-out **/bazel-out
# Node # Node
**/node_modules **/node_modules
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
# IDEs and editors # IDEs and editors
.idea/ .idea/
.project .project
.classpath .classpath
.c9/ .c9/
*.launch *.launch
.settings/ .settings/
*.sublime-workspace *.sublime-workspace
# Visual Studio Code # Visual Studio Code
.vscode/* .vscode/*
.vs/ .vs/
!.vscode/settings.json !.vscode/settings.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
.history/* .history/*
# Miscellaneous # Miscellaneous
**/.angular/cache **/.angular/cache
.sass-cache/ .sass-cache/
/connect.lock /connect.lock
/coverage /coverage
/libpeerconnection.log /libpeerconnection.log
testem.log testem.log
/typings /typings
# System files # System files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
/API/FamilyTreeAPI/uploads /API/FamilyTreeAPI/uploads
+54
View File
@@ -0,0 +1,54 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C#: FamilyTreeAPI",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/FamilyTreeAPI/bin/Debug/net10.0/FamilyTreeAPI.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5019"
},
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
},
},
{
"name": ".NET Core Launch (web) - Release",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "publish",
"program": "${workspaceFolder}/FamilyTreeAPI/bin/Release/net10.0/linux-x64/FamilyTreeAPI.dll",
"serverReadyAction": {
"action": "openExternally",
"pattern": "listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Production",
"ASPNETCORE_URLS": "https://localhost:5019"
},
"cwd": "${workspaceFolder}"
}
]
}
+31
View File
@@ -0,0 +1,31 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"${workspaceFolder}/FamilyTreeAPI/FamilyTreeAPI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/FamilyTreeAPI/FamilyTreeAPI.csproj",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
]
}
+1 -2
View File
@@ -1,5 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.7.34202.233 VisualStudioVersion = 17.7.34202.233
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
@@ -3,6 +3,7 @@ using FamilyTreeAPI.Repository;
namespace FamilyTreeAPI.Controllers; namespace FamilyTreeAPI.Controllers;
using DocumentFormat.OpenXml.Drawing.Charts;
using FamilyTreeAPI.Interface; using FamilyTreeAPI.Interface;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
@@ -110,14 +111,44 @@ public class FileUploadController : ControllerBase
return Ok(ret); return Ok(ret);
} }
[HttpPost("[action]")]
public async Task<IActionResult> DownloadPersonPhotoById(DeleteCriteria<int> criteria)
{
var result = await _personPhoto.DownloadPersonPhotoById(criteria.Id);
if (result.StatusCode > 0)
return File(result.Data.Content, result.Data.ContentType, result.Data.FileName);
else
return NotFound("not found image");
}
[HttpPost("[action]")] [HttpPost("[action]")]
public async Task<IActionResult> DownloadPersonPhoto(DownloadFileCriteria criteria) public async Task<IActionResult> DownloadPersonPhoto(DownloadFileCriteria criteria)
{ {
var result = await _personPhoto.DownloadPersonPhoto(criteria.Id, criteria.FileName); var result = await _personPhoto.DownloadPersonPhotoPerson(criteria.Id,criteria.PhotoType ,criteria.FileName);
if (result.StatusCode > 0)
return File(result.Data.Content, result.Data.ContentType, result.Data.FileName);
else
return NotFound("not found image");
}
[HttpPost("[action]")]
public async Task<IActionResult> DownloadPersonPhoto64(DownloadFileCriteria criteria)
{
return File(result.Data.Content, result.Data.ContentType, result.Data.FileName); var result = await _personPhoto.DownloadPersonPhotoPerson(criteria.Id,criteria.PhotoType ,criteria.FileName);
if (result.StatusCode > 0)
{
byte[] file64 = result.Data.Content;
string rval = Convert.ToBase64String(file64);
ResultModel<string> retval = new() {
Data = rval,
StatusCode = 1,
Message = ""
};
return Ok(retval);
}
else
return NotFound("not found image");
} }
[HttpPost("[action]")] [HttpPost("[action]")]
public async Task<ActionResult> DeletePersonPhoto(DeleteFileCriteria criteria) public async Task<ActionResult> DeletePersonPhoto(DeleteFileCriteria criteria)
@@ -127,8 +158,18 @@ public class FileUploadController : ControllerBase
if ( criteria.Id > 0) if ( criteria.Id > 0)
{ {
ret = await _personPhoto.DeletePersonPhoto(criteria.Id); ret = await _personPhoto.DeletePersonPhoto(criteria.Id, criteria.PhotoType);
} }
return Ok(ret); return Ok(ret);
} }
[HttpPost("[action]")]
public async Task<ActionResult> DeletePersonPhotoById(DeleteCriteria<int> criteria)
{
ResultModel<int> ret = new();
ret = await _personPhoto.DeletePersonPhotoById(criteria.Id);
return Ok(ret);
}
} }
@@ -14,6 +14,7 @@ namespace FamilyTreeAPI.Controllers
{ {
_repo = repo; _repo = repo;
} }
[HttpPost("[action]")] [HttpPost("[action]")]
public async Task<IActionResult> UploadImage(IFormFile file) public async Task<IActionResult> UploadImage(IFormFile file)
{ {
@@ -37,8 +37,25 @@ public class UsersController : ControllerBase
public async Task<IActionResult> LoginApiAD([FromBody] AuthenticateRequest model) public async Task<IActionResult> LoginApiAD([FromBody] AuthenticateRequest model)
{ {
string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
var response = await _userService.LoginApiAD(model, remoteIpAddress); var rval = await _userService.LoginApiAD(model, remoteIpAddress);
return Ok(response); if (rval.Data != null && rval.Data.Token != null)
{
// Configure cookie options
var cookieOptions = new CookieOptions
{
HttpOnly = true, // Blocks client-side JavaScript reading/theft
Secure = false, // Requires HTTPS connections
SameSite = SameSiteMode.Unspecified, // Prevents CSRF attacks
Expires = DateTimeOffset.UtcNow.AddDays(1), // Match your token lifespan
Path = "/" // Accessible across entire domain structure
};
// Write cookie into response headers
Response.Cookies.Append("token", rval.Data.Token, cookieOptions);
}
return Ok(rval);
} }
[HttpPost("[action]")] [HttpPost("[action]")]
@@ -51,6 +68,14 @@ public class UsersController : ControllerBase
string token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); string token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
string remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(); string remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
var response = await _userService.Logout(token, currentUser, remoteIpAddress); var response = await _userService.Logout(token, currentUser, remoteIpAddress);
Response.Cookies.Delete("token", new CookieOptions
{
HttpOnly = true,
Secure = false,
SameSite = SameSiteMode.Unspecified,
Path = "/"
});
return Ok(response); return Ok(response);
} }
+2 -2
View File
@@ -3,7 +3,7 @@
# This stage is used when running from VS in fast mode (Default for Debug configuration) # 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 #FROM mcr.microsoft.com/dotnet/aspnet:9::.0 AS base
ARG BUILD_CONFIGURATION=Release 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 WORKDIR /src
COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"] COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"]
RUN dotnet restore "./FamilyTreeAPI/FamilyTreeAPI.csproj" 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 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) # 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 WORKDIR /app
ENV ASPNETCORE_HTTP_PORTS=8080 ENV ASPNETCORE_HTTP_PORTS=8080
EXPOSE 8080 EXPOSE 8080
+2 -2
View File
@@ -4,9 +4,9 @@ public class AppSettings
{ {
public string Secret { get; set; } public string Secret { get; set; }
public string SQLConnectionString { get; set; } public string SQLConnectionString { get; set; }
public string LoginWebAPI { get; set; } public string ImageFolder { get; set; }
public string ClientURL { get; set; } public string ImportFolder { get; set; }
} }
/* /*
+4 -1
View File
@@ -15,16 +15,19 @@ public class DownloadFileCriteria
{ {
public string FileName { get; set; } public string FileName { get; set; }
public int Id { get; set; } public int Id { get; set; }
public int PhotoType {get; set;}
} }
public class UploadCriteria public class UploadCriteria
{ {
public string FileName { get; set; } public string FileName { get; set; }
public int PersonId { get; set; } public int PersonId { get; set; }
public int PhotoType {get; set;}
public IFormFile File { get; set; } public IFormFile File { get; set; }
} }
public class DeleteFileCriteria public class DeleteFileCriteria
{ {
public int Id { get; set; } public int Id { get; set; }
public string Filename { get; set; } public string Filename { get; set; }
public int PhotoType {get; set;}
} }
@@ -1,5 +1,12 @@
namespace FamilyTreeAPI.Entities; namespace FamilyTreeAPI.Entities;
//photo Type string number
public enum enumPhotoType
{
Profile = 1,
Other = 2
}
public class PersonPhotoDto public class PersonPhotoDto
{ {
public int Id { get; set; } public int Id { get; set; }
+20 -17
View File
@@ -1,38 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<PublishSingleFile>true</PublishSingleFile>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Models_old\**" /> <PackageReference Include="HotChocolate.AspNetCore" Version="16.6.1" />
<Content Remove="Models_old\**" /> <PackageReference Include="HotChocolate.Data.EntityFramework" Version="16.6.1" />
<EmbeddedResource Remove="Models_old\**" /> <PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.1" />
<None Remove="Models_old\**" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
</ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.11">
<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.13" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.13" />
<PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.5"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.11">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
<PackageReference Include="SpreadsheetLight" Version="3.5.0" /> <PackageReference Include="SpreadsheetLight" Version="3.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
<PackageReference Include="System.Drawing.Common" Version="10.0.11" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>
+7 -1
View File
@@ -2,6 +2,12 @@
{ {
public class Helpers public class Helpers
{ {
public static string GetFileExtension(string fileName)
{
string result = System.IO.Path.GetExtension(fileName);
return result;
}
public static string GetFileContent(string extension) public static string GetFileContent(string extension)
{ {
string contentType = ""; string contentType = "";
@@ -82,7 +88,7 @@
if (date.HasValue) if (date.HasValue)
{ {
result = date.Value.ToString("yyyy-MM-dd"); result = date.Value.ToString("yyyy-MM-dd");
} }
return result; return result;
} }
public static DateTime? DateToDateTime(string? date) public static DateTime? DateToDateTime(string? date)
+8 -1
View File
@@ -71,7 +71,11 @@ $ sudo nano /etc/nginx/sites-avaible/default
After adding the content, save it. Then let’s enable and start the service: After adding the content, save it. Then let’s enable and start the service:
sudo systemctl enable kestrel-app.service sudo systemctl enable kestrel-app.service
sudo systemctl start kestrel-app.service sudo systemctl start kestrel-app.service
put familytreeapp.conf
put in /etc/nginx/sites-availalble/familytreeapp.conf
$) sudo ln -s /etc/nginx/sites-available/familytreeapp.conf /etc/nginx/sites-enabled/
postgresql.conf: postgresql.conf:
Edit postgresql.conf: Edit postgresql.conf:
@@ -104,4 +108,7 @@ $ sudo nano /etc/nginx/sites-avaible/default
docker run -p 5432:5432 -e POSTGRES_PASSWORD=123456789 \ docker run -p 5432:5432 -e POSTGRES_PASSWORD=123456789 \
-d postgres:9.3.6 \ -d postgres:9.3.6 \
-c config_file=/path/to/postgresql.conf -c config_file=/path/to/postgresql.conf
+4 -3
View File
@@ -5,8 +5,9 @@ namespace FamilyTreeAPI.Interface;
public interface IPersonPhoto public interface IPersonPhoto
{ {
Task<ResultModel<int>> SaveAsync(UploadCriteria criteria); Task<ResultModel<int>> SaveAsync(UploadCriteria criteria);
Task<ResultModel<FileContent>> DownloadPersonPhoto(int id, string? filename); Task<ResultModel<FileContent>> DownloadPersonPhotoPerson(int personId, int photoType, string filename);
Task<ResultModel<FileContent>> DownloadPersonPhotoById(int id);
Task<ResultModel<int>> DeletePersonPhoto(int id); Task<ResultModel<int>> DeletePersonPhoto(int id, int photoType);
Task<ResultModel<int>> DeletePersonPhotoById(int id);
Task<ResultModel<List<PersonPhotoDto>>> LoadPersonPhoto(int personId); Task<ResultModel<List<PersonPhotoDto>>> LoadPersonPhoto(int personId);
} }
+82 -28
View File
@@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Text; using System.Text;
using DocumentFormat.OpenXml.Office2010.Drawing.Charts;
using FamilyTreeAPI.Authorization; using FamilyTreeAPI.Authorization;
using FamilyTreeAPI.Entities; using FamilyTreeAPI.Entities;
using FamilyTreeAPI.GraphQL.Query; using FamilyTreeAPI.GraphQL.Query;
@@ -14,6 +13,15 @@ using FamilyTreeAPI.Repository;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
// 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. // Add services to the container.
#region Services #region Services
var services = builder.Services; var services = builder.Services;
@@ -22,11 +30,26 @@ services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer(); services.AddEndpointsApiExplorer();
services.AddSwaggerGen(); services.AddSwaggerGen();
string skey = "Super Secret SIGN AND VERIFY JWT TOKENS";
var appSettingsSection = builder.Configuration.GetSection("AppSettings"); var appSettingsSection = builder.Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection); if (appSettingsSection.Exists())
var appSettings = appSettingsSection.Get<AppSettings>(); {
var key = Encoding.ASCII.GetBytes(appSettings.Secret); services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
if ( appSettings != null)
skey = appSettings.Secret;
else
{
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 => services.AddAuthentication(x =>
{ {
@@ -103,19 +126,7 @@ using (var scope = app.Services.CreateScope())
var db = context.Database; var db = context.Database;
if (db != null) if (db != null)
{ {
try var staff = scope.ServiceProvider.GetService<Seed>();
{
using var cts = new CancellationTokenSource();
CancellationToken cancellationToken = cts.Token;
await db.MigrateAsync(cancellationToken);
db.EnsureCreated();
}
catch
{
//continue
}
var staff = context.GetService<Seed>();
int id = staff.InsertOneUser(); int id = staff.InsertOneUser();
if (id < 0) if (id < 0)
{ {
@@ -125,29 +136,32 @@ using (var scope = app.Services.CreateScope())
//if (!databaseCreator.Exists()) //if (!databaseCreator.Exists())
try try
{ {
databaseCreator.CreateTables(); // Check if database exists
if (!databaseCreator.Exists())
{
databaseCreator.Create();
}
if (!databaseCreator.HasTables())
{
databaseCreator.CreateTables();
}
} }
catch catch
{ {
//continue //continue
} }
} }
id = staff.InsertOneUser(); id = staff.InsertOneUser();
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.ToString()); Console.WriteLine(e.ToString());
} }
} }
app.UseMiddleware<JwtMiddleware>(); app.UseMiddleware<JwtMiddleware>();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())// //if (app.Environment.IsDevelopment())//
@@ -173,6 +187,17 @@ app.Run();
#endregion #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 https://www.youtube.com/watch?v=WQFx2m5Ub9M
@@ -188,14 +213,43 @@ PM> Scaffold-DbContext "Host=postgresdb;Port=5432;database=FamilyTreeDB;user id=
https://www.youtube.com/watch?v=HnXA8RI7Tvc https://www.youtube.com/watch?v=HnXA8RI7Tvc
query { query {
family { persons {
nodes{ nodes{
id id
firstname firstName
lastname sex
email email
} }
} }
} }
*/
dotnet --list-sdks
In the VS Code Explorer, click and open your <ProjectName>.csproj file.
Locate the <TargetFramework> tag inside the <PropertyGroup> block.
Change the value to your desired version (e.g., change net11.0 or net9.0 to net10.0
now run to see out of date
dotnet list package --outdated
dotnet tool uninstall --global dotnet-outdated
dotnet tool install --global dotnet-outdated-tool
//update the outdate
dotnet outdated -u
update the package
dotnet add package <Package_Name>
Open the .vscode folder in your workspace and select launch.json.
Look for lines containing your old framework directory string (e.g., /bin/Debug/net8.0/).
Update those paths to reflect your new version (e.g., /bin/Debug/net9.0/).
clear and rebuild
dotnet clean
dotnet restore
dotnet build
*/
@@ -8,35 +8,17 @@
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"dotnetRunMessages": true, "dotnetRunMessages": true,
"applicationUrl": "http://localhost:5016", "applicationUrl": "http://localhost:5016"
"applicationUrl1": "http://192.168.8.188:5015"
}, },
"IIS Express": { "http": {
"commandName": "IISExpress", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"launchUrl": "swagger", "launchUrl": "swagger",
"environmentVariables": { "applicationUrl": "http://localhost:5000"
"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", "$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:4559",
"sslPort": 0
}
} }
} }
@@ -35,7 +35,7 @@ namespace FamilyTreeAPI.Repository
string error = ""; string error = "";
try try
{ {
Lookup model = null!; FamilyTreeAPI.Models.Lookup model = null!;
bool ok = !checkDescription(desc, lookup.Type, lookup.Id); bool ok = !checkDescription(desc, lookup.Type, lookup.Id);
if (ok) if (ok)
{ {
@@ -52,7 +52,7 @@ namespace FamilyTreeAPI.Repository
} }
else else
{ {
Lookup? model1 = await _context.Lookups.FindAsync(lookup.Id); FamilyTreeAPI.Models.Lookup? model1 = await _context.Lookups.FindAsync(lookup.Id);
if (model1 != null) if (model1 != null)
{ {
model1.Description = desc; model1.Description = desc;
@@ -3,6 +3,7 @@ using FamilyTreeAPI.Entities;
using FamilyTreeAPI.Helper; using FamilyTreeAPI.Helper;
using FamilyTreeAPI.Interface; using FamilyTreeAPI.Interface;
using FamilyTreeAPI.Models; using FamilyTreeAPI.Models;
using HotChocolate.CostAnalysis.Types;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace FamilyTreeAPI.Repository namespace FamilyTreeAPI.Repository
@@ -31,12 +32,12 @@ namespace FamilyTreeAPI.Repository
dto.PhotoType = model.PhotoType; dto.PhotoType = model.PhotoType;
return dto; return dto;
} }
private PersonPhoto FillModel(PersonPhoto model, int personId, string filename) private PersonPhoto FillModel(PersonPhoto model, int personId, int photoType, string filename)
{ {
model.PersonId = personId; model.PersonId = personId;
model.Photo = filename; model.Photo = filename;
model.PhotoType = System.IO.Path.GetExtension(filename); model.PhotoType = photoType.ToString(); //System.IO.Path.GetExtension(filename);
return model; return model;
} }
@@ -44,8 +45,10 @@ namespace FamilyTreeAPI.Repository
{ {
int result = default(int); int result = default(int);
int statuscode = 0; int statuscode = 0;
string error = ""; string error = "";//Other
string photoType = "2"; //
if (criteria.PhotoType > 0)
photoType = criteria.PhotoType.ToString();
HttpContext? httpContext = _httpContext.HttpContext; HttpContext? httpContext = _httpContext.HttpContext;
string loginName = ""; string loginName = "";
if (httpContext != null) if (httpContext != null)
@@ -63,7 +66,7 @@ namespace FamilyTreeAPI.Repository
model = new(); model = new();
model = FillModel(model,criteria.PersonId, formfile.FileName); model = FillModel(model,criteria.PersonId, criteria.PhotoType,formfile.FileName);
using (var stream = new MemoryStream()) using (var stream = new MemoryStream())
{ {
@@ -90,15 +93,50 @@ namespace FamilyTreeAPI.Repository
Message = error Message = error
}; };
} }
public async Task<ResultModel<FileContent>> DownloadPersonPhotoById(int Id)
public async Task<ResultModel<FileContent>> DownloadPersonPhoto(int id, string? filename)
{ {
int statusCode = -1; int statusCode = -1;
string error = ""; string error = "";
string extension = "";
PersonPhoto? model = null; PersonPhoto? model = null;
FileContent fileContent = new(); FileContent fileContent = new();
if (id < 1) try {
model = await _context.PersonPhotos.FindAsync(Id);
if (model != null)
{
extension = Helpers.GetFileExtension(model.Photo);
fileContent.ContentType = Helpers.GetFileContent(extension);
fileContent.Content = (byte[])model.ImagePhoto;
fileContent.FileName = model.Photo;
statusCode = 1;
}
}
catch (Exception e)
{
error = e.ToString();
statusCode = -1;
}
return new ResultModel<FileContent>()
{
Data = fileContent,
StatusCode = statusCode,
Message = error
};
}
public async Task<ResultModel<FileContent>> DownloadPersonPhotoPerson(int personId, int photoType, string filename)
{
int statusCode = -1;
string error = "";
string extension = "";
PersonPhoto? model = null;
FileContent fileContent = new();
if (personId < 1)
{ {
var rlist = await _context.PersonPhotos.Where(x => x.Photo == filename).ToListAsync(); var rlist = await _context.PersonPhotos.Where(x => x.Photo == filename).ToListAsync();
if (rlist != null) if (rlist != null)
@@ -109,19 +147,26 @@ namespace FamilyTreeAPI.Repository
} }
} }
} }
else else
{ {
model = await _context.PersonPhotos.FindAsync(id); var rlist = await _context.PersonPhotos.Where (x => x.PersonId == personId && photoType.ToString() == x.PhotoType).ToListAsync();
} for ( int i = 0; i < rlist.Count; i++)
{
model = rlist[i];
break;
}
}
if (model != null) if (model != null)
{ {
fileContent.ContentType = Helpers.GetFileContent(model.PhotoType); extension = Helpers.GetFileExtension(model.Photo);
fileContent.ContentType = Helpers.GetFileContent(extension);
fileContent.Content = (byte[])model.ImagePhoto; fileContent.Content = (byte[])model.ImagePhoto;
fileContent.FileName = model.Photo; fileContent.FileName = model.Photo;
statusCode = 1; statusCode = 1;
} }
return new ResultModel<FileContent>() return new ResultModel<FileContent>()
{ {
Data = fileContent, Data = fileContent,
@@ -130,20 +175,69 @@ namespace FamilyTreeAPI.Repository
}; };
} }
public async Task<ResultModel<int>> DeletePersonPhoto(int id) public async Task<ResultModel<int>> DeletePersonPhoto(int personId, int photoType)
{ {
int result = default(int); int result = default(int);
int statuscode = 0; int statuscode = 0;
string error = ""; string error = "";
PersonPhoto? model;
try try
{ {
PersonPhoto? model = await _context.PersonPhotos.FindAsync(id); if (photoType < 1)
if (model != null)
{ {
_context.PersonPhotos.Remove(model); List<PersonPhoto> list = await _context.PersonPhotos.Where ( x => x.PersonId == personId).ToListAsync();
await _context.SaveChangesAsync(); for (int i = 0; i< list.Count; i++)
statuscode = 1; {
model = list[i];
_context.PersonPhotos.Remove(model);
await _context.SaveChangesAsync();
statuscode = 1;
}
} }
else
{
List<PersonPhoto> list = await _context.PersonPhotos.Where ( x => x.PersonId == personId && x.PhotoType == photoType.ToString()).ToListAsync();
for (int i = 0; i< list.Count; i++)
{
model = list[i];
_context.PersonPhotos.Remove(model);
await _context.SaveChangesAsync();
statuscode = 1;
}
}
}
catch (Exception ex)
{
error = ex.ToString();
statuscode = -1;
}
//var dto = await Task.Run(() => result);
return new ResultModel<int>()
{
Data = result,
StatusCode = statuscode,
Message = error
};
}
public async Task<ResultModel<int>> DeletePersonPhotoById(int id)
{
int result = default(int);
int statuscode = 0;
string error = "";
try
{
PersonPhoto? model = await _context.PersonPhotos.FindAsync(id);
if (model != null)
{
_context.PersonPhotos.Remove(model);
await _context.SaveChangesAsync();
statuscode = 1;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -283,7 +283,7 @@ public partial class PersonRepository : IPerson
node.Key = item.Id.ToString(); node.Key = item.Id.ToString();
node.Type = type; node.Type = type;
if (item.Image != null) if (item.Image != null)
node.Data.Image = await LoadPersonImage(item.Image); node.Data.Image = await LoadPersonImage(item.Id, (int) enumPhotoType.Profile, item.Image);
node.Icon = "T"; node.Icon = "T";
node.StyleClass = getStyleClass(node.Icon); node.StyleClass = getStyleClass(node.Icon);
@@ -319,8 +319,8 @@ public partial class PersonRepository : IPerson
TreeNode<TreeData> child; TreeNode<TreeData> child;
PersonDto dto; PersonDto dto;
Person pe; Person pe;
int fatherId, motherId; int fatherId, motherId, personId;
fatherId = motherId = 0; personId = fatherId = motherId = 0;
string data = person.Sex; string data = person.Sex;
TreeNode<TreeData> citem; TreeNode<TreeData> citem;
string pName = ""; string pName = "";
@@ -371,6 +371,7 @@ public partial class PersonRepository : IPerson
pName = pe.FirstName; pName = pe.FirstName;
image = pe.Image; image = pe.Image;
key = motherId.ToString(); key = motherId.ToString();
personId = motherId;
data = pe.Sex; data = pe.Sex;
} }
} }
@@ -382,6 +383,7 @@ public partial class PersonRepository : IPerson
pName = pe.FirstName; pName = pe.FirstName;
image = pe.Image; image = pe.Image;
key = fatherId.ToString(); key = fatherId.ToString();
personId = fatherId;
data = pe.Sex; data = pe.Sex;
} }
} }
@@ -390,7 +392,7 @@ public partial class PersonRepository : IPerson
citem.Data = new(); citem.Data = new();
citem.Label = pName; citem.Label = pName;
if (image != null) if (image != null)
citem.Data.Image = await LoadPersonImage(image); citem.Data.Image = await LoadPersonImage(personId, (int) enumPhotoType.Profile, image);
citem.Icon = "P"; citem.Icon = "P";
@@ -411,7 +413,7 @@ public partial class PersonRepository : IPerson
child.Type = type; child.Type = type;
image = dto.Image; image = dto.Image;
if (image != null) if (image != null)
child.Data.Image = await LoadPersonImage(image); child.Data.Image = await LoadPersonImage(dto.Id,(int) enumPhotoType.Profile, image);
child.Icon = "C"; child.Icon = "C";
child.StyleClass = getStyleClass(child.Icon); child.StyleClass = getStyleClass(child.Icon);
child.Label = dto.FirstName; child.Label = dto.FirstName;
@@ -480,12 +482,12 @@ public partial class PersonRepository : IPerson
/// <param name="fileName"></param> /// <param name="fileName"></param>
/// <param name="familyId"></param> /// <param name="familyId"></param>
/// <param name="base64"></param> /// <param name="base64"></param>
private string SaveFile(string fileName, int familyId, string base64) private async Task<string> SaveFile(string fileName, int personId, string base64)
{ {
string path = ""; string path = "";
int statusCode = 0; int statusCode = 0;
string error = ""; // string error = "";
string sdate = GetCurrentDateTime(); string sdate = GetCurrentDateTime();
string filename, extention; string filename, extention;
filename = extention = ""; filename = extention = "";
@@ -494,14 +496,33 @@ public partial class PersonRepository : IPerson
string rbase = base64.Substring(first, last - first); string rbase = base64.Substring(first, last - first);
byte[] newBytes = Convert.FromBase64String(rbase); byte[] newBytes = Convert.FromBase64String(rbase);
extention = System.IO.Path.GetExtension(fileName);
filename = personId + "_" + sdate + extention;
// Wrap the byte array in a MemoryStream
var stream = new MemoryStream(newBytes);
// Create the IFormFile instance
IFormFile file = new FormFile(stream, 0, newBytes.Length, "Profile", filename)
{
Headers = new HeaderDictionary(),
ContentType = "application/octet-stream" // Change to your specific MIME type if known (e.g., "image/jpeg", "application/pdf")
};
//save to database.
UploadCriteria criteria = new();
criteria.FileName = filename;
criteria.PersonId = personId;
criteria.PhotoType = (int) enumPhotoType.Profile;
criteria.File = file;
var rval = await _personPhoto.SaveAsync(criteria);
/*
path = _config["AppSettings:ImageFolder"]; path = _config["AppSettings:ImageFolder"];
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
} }
extention = System.IO.Path.GetExtension(fileName);
filename = familyId + "_" + sdate + extention;
string fullpath = System.IO.Path.Combine(path, filename); string fullpath = System.IO.Path.Combine(path, filename);
/* /*
using (var fileStream = new FileStream(fullpath, FileMode.Create)) using (var fileStream = new FileStream(fullpath, FileMode.Create))
@@ -520,10 +541,12 @@ public partial class PersonRepository : IPerson
} }
*/ */
/*
using (FileStream fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None)) using (FileStream fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None))
{ {
fileStream.Write(newBytes, 0, newBytes.Length); fileStream.Write(newBytes, 0, newBytes.Length);
} }
*/
return filename; return filename;
} }
@@ -538,7 +561,7 @@ public partial class PersonRepository : IPerson
string loginName = ""; string loginName = "";
if (httpContext != null) if (httpContext != null)
{ {
UserDto? user = (UserDto?)httpContext.Items["User"]; UserDto? user = (UserDto?)httpContext.Items["User"];
if (user != null) if (user != null)
loginName = user.FirstName + " " + user.LastName; loginName = user.FirstName + " " + user.LastName;
} }
@@ -571,7 +594,7 @@ public partial class PersonRepository : IPerson
if (!string.IsNullOrEmpty(container.FileName)) if (!string.IsNullOrEmpty(container.FileName))
{ {
string image = SaveFile(container.FileName, result, container.FormData); string image = await SaveFile(container.FileName, result, container.FormData);
// update the image in table to new // update the image in table to new
model.Image = image; model.Image = image;
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@@ -587,7 +610,7 @@ public partial class PersonRepository : IPerson
result = item.Id; result = item.Id;
if (!string.IsNullOrEmpty(container.FileName)) if (!string.IsNullOrEmpty(container.FileName))
{ {
string image = SaveFile(container.FileName, result, container.FormData); string image = await SaveFile(container.FileName, result, container.FormData);
// update the image in table to new // update the image in table to new
model1.Image = image; model1.Image = image;
} }
@@ -714,17 +737,37 @@ public partial class PersonRepository : IPerson
}; };
} }
private async Task<string> LoadPersonImage(string filename) private async Task<string> LoadPersonImage_old(string filename)
{ {
string result = ""; string result = "";
DownloadFileCriteria criteria = new() { FileName = filename }; DownloadFileCriteria criteria = new() { FileName = filename };
var resultModel = await _importPersonRepository.DownloadFile(criteria); var resultModel = await _importPersonRepository.DownloadFile(criteria);
if (!string.IsNullOrEmpty((resultModel.Data)))
if (!string.IsNullOrEmpty(resultModel.Data))
result = "data:image/png;base64," + resultModel.Data; result = "data:image/png;base64," + resultModel.Data;
return result; return result;
} }
private async Task<string> LoadPersonImage(int personId, int photoType, string filename)
{
string result = "";
// DownloadFileCriteria criteria = new() { FileName = filename };
var resultModel = await _personPhoto.DownloadPersonPhotoPerson(personId, photoType , filename);
if (resultModel.StatusCode > 0)
{
string base64String = Convert.ToBase64String(resultModel.Data.Content);
if (!string.IsNullOrEmpty(base64String))
result = "data:image/png;base64," + base64String;
}
return result;
}
public ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria) public ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria)
{ {
int result = -1; int result = -1;
-1
View File
@@ -66,7 +66,6 @@ public class Seed
var conn = _context.Database.GetDbConnection(); var conn = _context.Database.GetDbConnection();
try try
{ {
conn.Open(); conn.Open();
var command = conn.CreateCommand(); var command = conn.CreateCommand();
command.CommandText = sptext; command.CommandText = sptext;
@@ -146,7 +146,7 @@ public class UserServiceRepository : IUserService
AuthenticateResponse retval = null; AuthenticateResponse retval = null;
string error = "user name is not in DB or user Name profile is not generic"; string error = "user name is not in DB or user Name profile is not generic";
int statuscode = 0; int statuscode = 0;
string webAPIUrl = _appSettings.LoginWebAPI; string webAPIUrl = "_appSettings.LoginWebAPI";
//KCO, D204KCO //KCO, D204KCO
//now check the adminuser table //now check the adminuser table
//ward clerk //ward clerk
+10 -9
View File
@@ -1,12 +1,12 @@
{ {
"AppSettings": { "AppSettings": {
"Secret": "Nepean Blue Mountain Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.", "Secret": "Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.",
"SQLConnectionString_o": "host=localhost;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;", "iSQLConnectionString_i": "host=localhost;port=5432;database=familytreedb;username=postgres;password=Positive~1;",
"SQLConnectionString": "host=192.168.1.102;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;", "SQLConnectionString": "host=127.0.0.1;port=5432;database=familytreedb;username=postgres;password=Positive~1;",
"ImageFolder": "c:\\temp\\Family", "ImageFolder": "/temp/Family",
"ImportFolder": "c:\\temp" "ImportFolder": "/temp"
}, },
"Logging": { "Logging": {
@@ -17,11 +17,12 @@
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel_not_use": {
"Kestrel": {
"Endpoints": { "Endpoints": {
"Http": { "Http": {
"Url": "http://*:5015" "Url": "http://localhost:5016"
} }
} }
} }
} }
+8 -6
View File
@@ -1,15 +1,17 @@
[Unit] [Unit]
Description=ASP.NET Core Web App running on Ubuntu Description=ASP.NET Core Web App running on arch linux
[Service] [Service]
WorkingDirectory=/var/www/familytree/api WorkingDirectory=/var/www/familytree/api
ExecStart=/usr/bin/dotnet /var/www/fadmilytree/api/FamilyTreeAPI.dll ExecStart=/usr/bin/dotnet /var/www/familytree/api/FamilyTreeAPI.dll
Restart=always Restart=always
# Restart service after 10 seconds if the dotnet service crashes: # Restart service after 20 seconds if the dotnet service crashes:
RestartSec=10 RestartSec=20
KillSignal=SIGINT KillSignal=SIGINT
SyslogIdentifier=dotnet-web-familytree-api SyslogIdentifier=dotnet-web-familytree-api
# This user should exist on the server and have ownership of the deployment directory # This user should exist on the server and have ownership of the deployment directory
User=www-data #User=www-data this is other ubuntu
#arch linux has user http
User=http
Environment=ASPNETCORE_ENVIRONMENT=Production Environment=ASPNETCORE_ENVIRONMENT=Production
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+44
View File
@@ -0,0 +1,44 @@
#make sure in nginx.conf has include module.d/*.conf inside http block one.
server {
listen 80; #make nginx out side listen 82;
location /familytreeapi/ {
#proxy_pass http://127.0.0.1:5015;
proxy_pass http://192.168.1.219:5015/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
return 301 /familytreeui/; #if same one redirect to /familytreeui/
}
location /familytreeui {
return 301 /familytreeui/; #if same one redirect to /familytreeui/
}
location /familytreeui/ {
#root /var/www/familytree/; #path is real must exist like /var/www/familytree/familytreeui and /var/www/familytree/api folder.
alias /var/www/familytree/ui/; #location does not to match. but angular index baseurl must same as location.
#index index.html;
try_files $uri $uri/ /familytreeui/index.html /index.html;
}
# Optional: Cache static assets for better performance
#location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
# expires 30d;
# add_header Cache-Control "public";
#}
# error_page 404 /index.html;
# put in /etc/nginx/sites-availalble/familytreeapp.conf
# link to enable
# $) sudo ln -s /etc/nginx/sites-availalble/familytreeapp.conf /etc/nginx/sites-enabled/
}
+123
View File
@@ -0,0 +1,123 @@
user http;
worker_processes auto;
worker_cpu_affinity auto;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
# Load all installed modules
#include modules.d/*.conf;
events {
worker_connections 1024;
}
http {
charset utf-8;
include mime.types;
default_type application/octet-stream;
types_hash_max_size 4096;
client_max_body_size 16M;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
include modules.d/*.conf;
server {
listen 81;
server_name localhost;
#access_log logs/host.access.log main;
#kham comment out first
#location / {
# root /usr/share/nginx/html;
# index index.html index.htm;
# }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
+17 -17
View File
@@ -1,17 +1,17 @@
# Editor configuration, see https://editorconfig.org # Editor configuration, see https://editorconfig.org
root = true root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.ts] [*.ts]
quote_type = single quote_type = single
ij_typescript_use_double_quotes = false ij_typescript_use_double_quotes = false
[*.md] [*.md]
max_line_length = off max_line_length = off
trim_trailing_whitespace = false 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. # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output # Compiled output
/dist /dist
/tmp /tmp
/out-tsc /out-tsc
/bazel-out /bazel-out
# Node # Node
/node_modules /node_modules
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
# IDEs and editors # IDEs and editors
.idea/ .idea/
.project .project
.classpath .classpath
.c9/ .c9/
*.launch *.launch
.settings/ .settings/
*.sublime-workspace *.sublime-workspace
# Visual Studio Code # Visual Studio Code
.vscode/* .vscode/*
!.vscode/settings.json !.vscode/settings.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
.history/* .history/*
# Miscellaneous # Miscellaneous
/.angular/cache /.angular/cache
.sass-cache/ .sass-cache/
/connect.lock /connect.lock
/coverage /coverage
/libpeerconnection.log /libpeerconnection.log
testem.log testem.log
/typings /typings
__screenshots__/ __screenshots__/
# System files # System files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
+4 -4
View File
@@ -1,5 +1,5 @@
{ {
"plugins": { "plugins": {
"@tailwindcss/postcss": {} "@tailwindcss/postcss": {}
} }
} }
+4 -4
View File
@@ -1,4 +1,4 @@
{ {
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"] "recommendations": ["angular.ng-template"]
} }
+20 -20
View File
@@ -1,20 +1,20 @@
{ {
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "ng serve", "name": "ng serve",
"type": "chrome", "type": "chrome",
"request": "launch", "request": "launch",
"preLaunchTask": "npm: start", "preLaunchTask": "npm: start",
"url": "http://localhost:4200/" "url": "http://localhost:4200/"
}, },
{ {
"name": "ng test", "name": "ng test",
"type": "chrome", "type": "chrome",
"request": "launch", "request": "launch",
"preLaunchTask": "npm: test", "preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html" "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 // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"type": "npm", "type": "npm",
"script": "start", "script": "start",
"isBackground": true, "isBackground": true,
"problemMatcher": { "problemMatcher": {
"owner": "typescript", "owner": "typescript",
"pattern": "$tsc", "pattern": "$tsc",
"background": { "background": {
"activeOnStart": true, "activeOnStart": true,
"beginsPattern": { "beginsPattern": {
"regexp": "(.*?)" "regexp": "(.*?)"
}, },
"endsPattern": { "endsPattern": {
"regexp": "bundle generation complete" "regexp": "bundle generation complete"
} }
} }
} }
}, },
{ {
"type": "npm", "type": "npm",
"script": "test", "script": "test",
"isBackground": true, "isBackground": true,
"problemMatcher": { "problemMatcher": {
"owner": "typescript", "owner": "typescript",
"pattern": "$tsc", "pattern": "$tsc",
"background": { "background": {
"activeOnStart": true, "activeOnStart": true,
"beginsPattern": { "beginsPattern": {
"regexp": "(.*?)" "regexp": "(.*?)"
}, },
"endsPattern": { "endsPattern": {
"regexp": "bundle generation complete" "regexp": "bundle generation complete"
} }
} }
} }
} }
] ]
} }
+31 -31
View File
@@ -1,32 +1,32 @@
FROM node:24.0.0 AS build FROM node:24.0.0 AS build
RUN mkdir -p /app RUN mkdir -p /app
WORKDIR /app WORKDIR /app
COPY ["./*.json", "./"] COPY ["./*.json", "./"]
RUN npm install -g @angular/cli RUN npm install -g @angular/cli
COPY . . COPY . .
# Run command in Virtual directory # Run command in Virtual directory
RUN npm cache clean --force RUN npm cache clean --force
RUN npm install RUN npm install
#RUN ng build --configuration=production #RUN ng build --configuration=production
RUN ng build -c=production RUN ng build -c=production
FROM nginx:latest FROM nginx:latest
#### copy nginx conf #### copy nginx conf
COPY ./nginx.conf /etc/nginx/conf.d/default.conf COPY ./nginx.conf /etc/nginx/conf.d/default.conf
#COPY --from=build app/dist/Familytree/browser /usr/share/nginx/html #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 artifact build from the 'build environment' old is not in browser folder
COPY --from=build /app/dist/Familytree/browser /usr/share/nginx/html COPY --from=build /app/dist/Familytree/browser /usr/share/nginx/html
#CMD ["nginx", "-g", "daemon off;"] #CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80 EXPOSE 80
#docker build -t my_angular_app:latest . #docker build -t my_angular_app:latest .
#docker run --name carval -d -p 4200:80 varlidate_ui #docker run --name carval -d -p 4200:80 varlidate_ui
#npm error code ENOENT #npm error code ENOENT
+59 -59
View File
@@ -1,59 +1,59 @@
# FamilyTreeUI # FamilyTreeUI
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.2. This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.2.
## Development server ## Development server
To start a local development server, run: To start a local development server, run:
```bash ```bash
ng serve 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. 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 ## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run: Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash ```bash
ng generate component component-name ng generate component component-name
``` ```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash ```bash
ng generate --help ng generate --help
``` ```
## Building ## Building
To build the project run: To build the project run:
```bash ```bash
ng build 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. 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 ## Running unit tests
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command: To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
```bash ```bash
ng test ng test
``` ```
## Running end-to-end tests ## Running end-to-end tests
For end-to-end (e2e) testing, run: For end-to-end (e2e) testing, run:
```bash ```bash
ng e2e ng e2e
``` ```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources ## 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. 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", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"cli": { "cli": {
"packageManager": "npm", "packageManager": "npm",
"analytics": false "analytics": false
}, },
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"FamilyTreeUI": { "FamilyTreeUI": {
"projectType": "application", "projectType": "application",
"schematics": {}, "schematics": {},
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"prefix": "app", "prefix": "app",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular/build:application", "builder": "@angular/build:application",
"options": { "options": {
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"moment", "file-saver", "xlsx" "moment", "file-saver", "xlsx"
], ],
"browser": "src/main.ts", "browser": "src/main.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"assets": [ "assets": [
{ {
"glob": "**/*", "glob": "**/*",
"input": "public" "input": "public"
} }
], ],
"styles": [ "styles": [
"src/styles.css" "src/styles.css"
] ]
}, },
"configurations": { "configurations": {
"production": { "production": {
"budgets": [ "budgets": [
{ {
"type": "initial", "type": "initial",
"maximumWarning": "5MB", "maximumWarning": "5MB",
"maximumError": "6MB" "maximumError": "6MB"
}, },
{ {
"type": "anyComponentStyle", "type": "anyComponentStyle",
"maximumWarning": "4kB", "maximumWarning": "4kB",
"maximumError": "8kB" "maximumError": "8kB"
} }
], ],
"outputHashing": "all" "outputHashing": "all"
}, },
"development": { "development": {
"optimization": false, "optimization": false,
"extractLicenses": false, "extractLicenses": false,
"sourceMap": true "sourceMap": true
} }
}, },
"defaultConfiguration": "production" "defaultConfiguration": "production"
}, },
"serve": { "serve": {
"builder": "@angular/build:dev-server", "builder": "@angular/build:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"buildTarget": "FamilyTreeUI:build:production" "buildTarget": "FamilyTreeUI:build:production"
}, },
"development": { "development": {
"buildTarget": "FamilyTreeUI:build:development" "buildTarget": "FamilyTreeUI:build:development"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"test": { "test": {
"builder": "@angular/build:unit-test" "builder": "@angular/build:unit-test"
} }
} }
} }
} }
} }
+19 -19
View File
@@ -1,20 +1,20 @@
server { server {
listen 80; listen 80;
sendfile on; sendfile on;
default_type application/octet-stream; default_type application/octet-stream;
gzip on; gzip on;
gzip_http_version 1.1; gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\."; gzip_disable "MSIE [1-6]\.";
gzip_min_length 256; gzip_min_length 256;
gzip_vary on; gzip_vary on;
gzip_proxied expired no-cache no-store private auth; 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_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; gzip_comp_level 9;
root /usr/share/nginx/html; root /usr/share/nginx/html;
location / { location / {
try_files $uri $uri/ /index.html =404; try_files $uri $uri/ /index.html =404;
} }
} }
+3194 -3674
View File
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -23,35 +23,35 @@
"private": true, "private": true,
"packageManager": "npm@11.6.4", "packageManager": "npm@11.6.4",
"dependencies": { "dependencies": {
"@angular/common": "^21.0.0", "@angular/common": "^22.0.0",
"@angular/compiler": "^21.0.0", "@angular/compiler": "^22.0.0",
"@angular/core": "^21.0.0", "@angular/core": "^22.0.0",
"@angular/forms": "^21.0.0", "@angular/forms": "^22.0.0",
"@angular/platform-browser": "^21.0.0", "@angular/platform-browser": "^22.0.0",
"@angular/router": "^21.0.0", "@angular/router": "^22.0.0",
"@ngrx/effects": "^21.0.1", "@ngrx/effects": "^22.0.0",
"@ngrx/store": "^21.0.1", "@ngrx/store": "^22.0.0",
"@ngrx/store-devtools": "^21.0.1", "@ngrx/store-devtools": "^22.0.0",
"@primeuix/themes": "^2.0.2", "@openng/optimus-ui": "^2.0.2",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"moment": "^2.30.1", "moment": "^2.30.1",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"primeicons": "^7.0.0",
"primeng": "^21.0.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"tailwindcss-primeui": "^0.6.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"xlsx": "^0.18.5" "xlsx": "^0.18.5",
"@openng/icons": "^1.0.0",
"@openng/optimus-ui-themes": "^2.0.2",
"@openng/optimus-ui-tailwindcss": "^2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "^21.2.7", "@angular/build": "^22.0.0",
"@angular/cli": "^21.2.7", "@angular/cli": "^22.0.0",
"@angular/compiler-cli": "^21.0.0", "@angular/compiler-cli": "^22.0.0",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"jsdom": "^27.1.0", "jsdom": "^27.1.0",
"typescript": "~5.9.2", "typescript": "~6.0.0",
"vitest": "^4.0.8" "vitest": "^4.0.8"
} }
} }
+7 -5
View File
@@ -1,6 +1,8 @@
{ {
"baseUrl1": "http://192.168.8.188:5015", "baseUrl1": "http://192.168.8.188:5015",
"baseUrl": "http://localhost:5016", "baseUrl": "http://localhost:5016",
"baseUrl_docker": "http://localhost:8080", "baseUrl_docker": "http://localhost:8080",
"attachment": "http://localhost/document/family" "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"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<system.webServer> <system.webServer>
<rewrite> <rewrite>
<rules> <rules>
<rule name="Angular Routes" stopProcessing="true"> <rule name="Angular Routes" stopProcessing="true">
<match url=".*" /> <match url=".*" />
<conditions logicalGrouping="MatchAll"> <conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions> </conditions>
<action type="Rewrite" url="./index.html" /> <action type="Rewrite" url="./index.html" />
</rule> </rule>
</rules> </rules>
</rewrite> </rewrite>
</system.webServer> </system.webServer>
</configuration> </configuration>
+66 -66
View File
@@ -1,67 +1,67 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners , import { ApplicationConfig, provideBrowserGlobalErrorListeners ,
provideAppInitializer} from '@angular/core'; provideAppInitializer} from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router'; import { provideRouter, withComponentInputBinding } from '@angular/router';
import { providePrimeNG } from 'primeng/config'; import { provideOptimus } from '@openng/optimus-ui/config';
import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { routes } from './app.routes'; import { routes } from './app.routes';
import MyPreset from './mythem'; import MyPreset from './mythem';
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares'; import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
import { provideStore } from '@ngrx/store'; import { provideStore } from '@ngrx/store';
import { provideStoreDevtools } from '@ngrx/store-devtools'; import { provideStoreDevtools } from '@ngrx/store-devtools';
import { isDevMode } from '@angular/core'; import { isDevMode } from '@angular/core';
import { provideEffects } from '@ngrx/effects'; import { provideEffects } from '@ngrx/effects';
import { staffEffects,staffsReducer } from './state'; import { staffEffects,staffsReducer } from './state';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideStore({ provideStore({
myStaffs: staffsReducer, myStaffs: staffsReducer,
}), }),
provideEffects([staffEffects]), provideEffects([staffEffects]),
provideStoreDevtools({ provideStoreDevtools({
maxAge: 30, maxAge: 30,
logOnly: !isDevMode() logOnly: !isDevMode()
}), }),
provideBrowserGlobalErrorListeners(), provideBrowserGlobalErrorListeners(),
MessageService, ConfirmationService, MessageService, ConfirmationService,
provideAppInitializer(initializeApp()), provideAppInitializer(initializeApp()),
provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])), provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
provideRouter(routes, withComponentInputBinding()), provideRouter(routes, withComponentInputBinding()),
providePrimeNG({ provideOptimus({
theme: { theme: {
preset: MyPreset, preset: MyPreset,
options: { options: {
cssLayer: { cssLayer: {
name: 'primeng', name: 'optimus',
order: 'theme, base, primeng' order: 'theme, base, optimus'
} }
} }
} }
}), }),
] ]
}; };
/* /*
$) ng build --base-href "/familytreeui/" -c production $) ng build --base-href "/familytreeui/" -c production
*/ */
/* /*
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
private readonly store = inject(Store); private readonly store = inject(Store);
protected books = this.store.selectSignal(selectBooks); protected books = this.store.selectSignal(selectBooks);
protected onAdd(id: number) { protected onAdd(id: number) {
this.store.dispatch(StaffsActions.addStaff({ id })); this.store.dispatch(StaffsActions.addStaff({ id }));
} }
protected onRemove(id: number) { protected onRemove(id: number) {
this.store.dispatch(StaffsActions.removeStaff({ id })); this.store.dispatch(StaffsActions.removeStaff({ id }));
} }
on ngOnInit() { on ngOnInit() {
use it normal get from API first use it normal get from API first
and subscribe( (x) => this.store.dispatch(StaffApiAction.LoadStaffList({x}))) and subscribe( (x) => this.store.dispatch(StaffApiAction.LoadStaffList({x})))
} }
*/ */
+4 -4
View File
@@ -1,5 +1,5 @@
<p-toast position="bottom-center" /> <p-toast position="bottom-center" />
<app-toolbar> </app-toolbar> <app-toolbar> </app-toolbar>
<router-outlet /> <router-outlet />
<p-confirmDialog [style]="{width: '50vw'}" <p-confirmDialog [style]="{width: '50vw'}"
rejectButtonStyleClass="p-button-text"> </p-confirmDialog> rejectButtonStyleClass="p-button-text"> </p-confirmDialog>
+19 -19
View File
@@ -1,19 +1,19 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { Login } from './login'; import { Login } from './login';
import { StaffComponent, StaffEditComponent } from './staff'; import { StaffComponent, StaffEditComponent } from './staff';
import { AuthGuard } from './route-guard'; import { AuthGuard } from './route-guard';
import { FamilyTree, FamilyList} from './person'; import { FamilyTree, FamilyList} from './person';
import { ImportCom } from './import.com/import.com'; import { ImportCom } from './import.com/import.com';
import { ImageDisplayComponent } from './pickperson/image.display'; import { ImageDisplayComponent } from './pickperson/image.display';
export const routes: Routes = [ export const routes: Routes = [
{ path: '', redirectTo: 'login', pathMatch: 'full' }, { path: '', redirectTo: 'login', pathMatch: 'full' },
// { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},}, // { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},},
{ path: 'login', component: Login}, { path: 'login', component: Login},
{ path: 'staff', component: StaffComponent}, { path: 'staff', component: StaffComponent},
{ path: 'person', component: FamilyList}, { path: 'person', component: FamilyList},
{ path: 'imagedisplay/:id', component: ImageDisplayComponent}, { path: 'imagedisplay/:id', component: ImageDisplayComponent},
{ path: 'import', component: ImportCom}, { path: 'import', component: ImportCom},
{ path: 'familytree', component: FamilyTree}, { path: 'familytree', component: FamilyTree},
{ path: 'staff/:id', component: StaffEditComponent}, { path: 'staff/:id', component: StaffEditComponent},
]; ];
+15 -15
View File
@@ -1,15 +1,15 @@
import { Component, signal } from '@angular/core'; import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { ToolbarComponent } from './toolbar/toolbar.component'; import { ToolbarComponent } from './toolbar/toolbar.component';
import { ToastModule } from 'primeng/toast'; import { ToastModule } from '@openng/optimus-ui/toast';
import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { ConfirmDialogModule } from '@openng/optimus-ui/confirmdialog';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule], imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule],
templateUrl: './app.html', templateUrl: './app.html',
styleUrl: './app.css' styleUrl: './app.css'
}) })
export class App { export class App {
protected readonly title = signal('FamilyTree'); protected readonly title = signal('FamilyTree');
} }
+20 -20
View File
@@ -1,20 +1,20 @@
<div class="card gap-2"> <div class="card gap-2">
<div class="mt-4"> <div class="mt-4">
<input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload multiple> <input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload multiple>
<div class="file-upload"> <div class="file-upload">
<label>{{fileName || "Photo upload:"}}</label> <label>{{fileName || "Photo upload:"}}</label>
<button pButton type="button" icon="pi pi-paperclip" <button pButton type="button" icon="pi pi-paperclip"
class="p-button-rounded p-button-text p-button-raised ml-2" pTooltip="attach file" class="p-button-rounded p-button-text p-button-raised ml-2" pTooltip="attach file"
(click)="fileUpload.click()"></button> (click)="fileUpload.click()"></button>
</div> </div>
<div class="flex items-end justify-end mt-4"> <div class="flex items-end justify-end mt-4">
@if (files) @if (files)
{ {
<button pButton pRipple type="button" icon="pi pi-upload" label="OK" class="p-button-sm mr-2" <button pButton pRipple type="button" icon="pi pi-upload" label="OK" class="p-button-sm mr-2"
(click)="upload($event)"></button> (click)="upload($event)"></button>
} }
<button pButton pRipple type="button" icon="pi pi-times" label="Cancel" class="p-button-sm" <button pButton pRipple type="button" icon="pi pi-times" label="Cancel" class="p-button-sm"
(click)="cancel($event)"></button> (click)="cancel($event)"></button>
</div> </div>
</div> </div>
</div> </div>
+178 -178
View File
@@ -1,178 +1,178 @@
import { ChangeDetectorRef, Component, inject, Inject, OnDestroy, OnInit, signal } from '@angular/core'; import { ChangeDetectorRef, Component, inject, Inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DynamicDialogModule, DynamicDialogRef ,DynamicDialogConfig} from 'primeng/dynamicdialog'; import { DynamicDialogModule, DynamicDialogRef ,DynamicDialogConfig} from '@openng/optimus-ui/dynamicdialog';
import { LookupEdit } from '../models'; import { LookupEdit } from '../models';
import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { LookupService } from '../shares'; import { LookupService } from '../shares';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CheckboxModule } from 'primeng/checkbox'; import { CheckboxModule } from '@openng/optimus-ui/checkbox';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
@Component({ @Component({
selector: 'add-photo', selector: 'add-photo',
imports: [CommonModule, ReactiveFormsModule, imports: [CommonModule, ReactiveFormsModule,
ButtonModule, InputTextModule, ButtonModule, InputTextModule,
DynamicDialogModule, CheckboxModule], DynamicDialogModule, CheckboxModule],
templateUrl: './add.photo.html', templateUrl: './add.photo.html',
styleUrls: ['./add.photo.css'], styleUrls: ['./add.photo.css'],
providers: [ConfirmationService] providers: [ConfirmationService]
}) })
export class AddPhoto implements OnInit, OnDestroy { export class AddPhoto implements OnInit, OnDestroy {
isChange = true; // disable use false//true for not disable. make sure it true is disable button. isChange = true; // disable use false//true for not disable. make sure it true is disable button.
selType = ""; selType = "";
_error = ""; _error = "";
downloadFile = signal(false); downloadFile = signal(false);
files: File[] = []; files: File[] = [];
fileName = ""; fileName = "";
disabled: boolean = true; disabled: boolean = true;
subChanged$ = new Subject<boolean>(); subChanged$ = new Subject<boolean>();
private formBuilder = inject(UntypedFormBuilder); private formBuilder = inject(UntypedFormBuilder);
private cdr = inject(ChangeDetectorRef); private cdr = inject(ChangeDetectorRef);
private subscription: Subscription = new Subscription(); private subscription: Subscription = new Subscription();
lookupForm = this.formBuilder.group({ lookupForm = this.formBuilder.group({
id: [0], //Validators.required id: [0], //Validators.required
description: ['', [Validators.required, Validators.maxLength(50)]], description: ['', [Validators.required, Validators.maxLength(50)]],
codeId: ['', [Validators.required]], codeId: ['', [Validators.required]],
parentId: [0], parentId: [0],
active: [false], //Validators.required active: [false], //Validators.required
}); });
constructor( constructor(
private lookupService: LookupService, private lookupService: LookupService,
private messageService: MessageService, private messageService: MessageService,
private confirmationService: ConfirmationService, private confirmationService: ConfirmationService,
public ref: DynamicDialogRef, public config: DynamicDialogConfig) { } public ref: DynamicDialogRef, public config: DynamicDialogConfig) { }
ngOnInit(): void { ngOnInit(): void {
const id = this.config.data.id; const id = this.config.data.id;
this.selType = this.config.data.type; this.selType = this.config.data.type;
const maxcodeId = this.config.data.maxCodeId; const maxcodeId = this.config.data.maxCodeId;
if (id > 0) { if (id > 0) {
this.lookupService.loadLookupById(id, this.selType).subscribe({ this.lookupService.loadLookupById(id, this.selType).subscribe({
next: x => { next: x => {
this.assignValue(x.data); this.assignValue(x.data);
this.cdr.markForCheck(); this.cdr.markForCheck();
this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false)); this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
}, },
error: e => { error: e => {
console.error("error", e); console.error("error", e);
this.messageService.add({ severity: 'error', summary: 'Error on load cleaner ', detail: e.message }); this.messageService.add({ severity: 'error', summary: 'Error on load cleaner ', detail: e.message });
} }
}); });
} }
else { else {
//new photo //new photo
const ward:LookupEdit = { const ward:LookupEdit = {
id, description: '', codeId: maxcodeId, type: this.selType, active: true, id, description: '', codeId: maxcodeId, type: this.selType, active: true,
}; };
this.assignValue(ward); this.assignValue(ward);
this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false)); this.subscription.add(this.lookupForm.valueChanges.subscribe(x => this.isChange = false));
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
} }
} }
getClassForRequire(prev: string, name: string) { getClassForRequire(prev: string, name: string) {
const notok = !this.lookupForm.controls[name].valid && const notok = !this.lookupForm.controls[name].valid &&
this.lookupForm.controls[name].touched; this.lookupForm.controls[name].touched;
let str = prev; let str = prev;
if (notok) if (notok)
str += " ng-invalid ng-dirty"; str += " ng-invalid ng-dirty";
return str; return str;
} }
get isFieldsChange() { get isFieldsChange() {
const chan = this.isChange || !this.lookupForm.valid; // this disable so need true valid = true not const chan = this.isChange || !this.lookupForm.valid; // this disable so need true valid = true not
//console.log(this.msg + 'is fields change', chan); //console.log(this.msg + 'is fields change', chan);
return chan; return chan;
} }
assignValue(item: LookupEdit): void { assignValue(item: LookupEdit): void {
this.lookupForm.patchValue({ this.lookupForm.patchValue({
id: item.id, id: item.id,
codeId: item.codeId, codeId: item.codeId,
description: item.description, description: item.description,
active: item.active, active: item.active,
}); });
// disable use false//true for not disable. // disable use false//true for not disable.
this.subChanged$.next(true); this.subChanged$.next(true);
} }
validate(item: LookupEdit): boolean { validate(item: LookupEdit): boolean {
let result = true; let result = true;
if (item.description!.trim() == "") { if (item.description!.trim() == "") {
this._error = "Description is empty or blank"; this._error = "Description is empty or blank";
result = false; result = false;
} }
return result; return result;
} }
deleteFile() : void { deleteFile() : void {
//const filename = this.requestForm.value.attachmentFile; //const filename = this.requestForm.value.attachmentFile;
// const data = { filename }; // const data = { filename };
this.fileName =""; this.fileName ="";
//this.requestForm.patchValue({ attachmentFile: "" }); //this.requestForm.patchValue({ attachmentFile: "" });
/* /*
const delete$ = this.tradePersonService.deleteUploadFile(data); const delete$ = this.tradePersonService.deleteUploadFile(data);
this.subscription.add(delete$.subscribe( this.subscription.add(delete$.subscribe(
{ {
next: x => { next: x => {
if (x.statusCode == 1) { if (x.statusCode == 1) {
{ {
this.requestForm.patchValue({ attachmentFile: "" }); this.requestForm.patchValue({ attachmentFile: "" });
} }
} }
else { else {
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message }); this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message });
} }
}, },
error: e => { error: e => {
this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e }); this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e });
} }
} }
)); ));
*/ */
} }
onFileSelected(event: any) { onFileSelected(event: any) {
let i =0; let i =0;
for (i = 0; i < event.target.files.length; i++) for (i = 0; i < event.target.files.length; i++)
{ {
const file: File = event.target.files[i]; const file: File = event.target.files[i];
this.files.push(file); this.files.push(file);
if (this.fileName != "") if (this.fileName != "")
this.fileName = this.fileName + "," + file.name; this.fileName = this.fileName + "," + file.name;
else else
this.fileName = file.name; this.fileName = file.name;
this.subChanged$.next(false); this.subChanged$.next(false);
} }
} }
upload(e: Event): void { upload(e: Event): void {
e.preventDefault(); e.preventDefault();
if (this.files.length > 0) if (this.files.length > 0)
{ {
this.ref.close(this.files); this.ref.close(this.files);
} }
else else
{ {
this.messageService.add({severity: 'error', summary: 'No File to upload file', detail: 'select file first then upload file'}); this.messageService.add({severity: 'error', summary: 'No File to upload file', detail: 'select file first then upload file'});
} }
} }
cancel(e: Event): void { cancel(e: Event): void {
e.preventDefault(); e.preventDefault();
this.ref.close(null); this.ref.close(null);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }
+1 -1
View File
@@ -1,2 +1,2 @@
export * from './photolist'; export * from './photolist';
export * from './add.photo'; export * from './add.photo';
+49 -48
View File
@@ -1,49 +1,50 @@
<div class="shadow-xl rounded-md p-2"> <div class="shadow-xl rounded-md p-2">
<div class="flex fex-row justify-between mb-2"> <div class="flex fex-row justify-between mb-2">
<p> please click save at the end</p> <p> please click save at the end</p>
<div class="flex items-end self-end"> <div class="flex items-end self-end">
<button pButton pRipple type="button" icon="pi pi-file" label="Attach Photo" class="p-button-sm" <button pButton pRipple type="button" icon="pi pi-file" label="Attach Photo" class="p-button-sm"
(click)="newSupportDoc($event)"></button> (click)="newSupportDoc($event)"></button>
</div> </div>
</div> </div>
<div class="p-4"> <div class="p-4">
<p-table [value]="FileList" sortMode="multiple" class="p-datatable-sm" <p-table [value]="FileList" sortMode="multiple" class="p-datatable-sm"
[loading]="loading"> [loading]="loading">
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th>Id</th> <th>Id</th>
<th pSortableColumn="description">Description<p-sortIcon field="description"></p-sortIcon> <th pSortableColumn="description">Description<p-sortIcon field="description"></p-sortIcon>
</th> </th>
<th>Photo Type</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
</ng-template> </ng-template>
<ng-template pTemplate="body" let-item> <ng-template pTemplate="body" let-item>
<tr> <tr>
<td>{{item.id}}</td> <td>{{item.id}}</td>
<td >{{item.photo}}</td> <td >{{item.photo}}</td>
<td> <td>{{item.photoType}}</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" <button pButton type="button" icon="pi pi-times"
(click)="remove(item.id)"></button> 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" @if (item.id > 0) {
pTooltip="Download Photo" <button pButton type="button" icon="pi pi-file"
class="p-button-rounded p-button-text p-button-raised" pTooltip="Download Photo"
(click)="downloadAttachment(item.id)"></button> class="p-button-rounded p-button-text p-button-raised"
} (click)="downloadAttachment(item.id)"></button>
</td> }
</tr> </td>
</ng-template> </tr>
</p-table> </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" <div class="flex items-end justify-end mt-4">
(click)="onClose($event)"></button> <button pButton pRipple type="button" icon="pi pi-times" label="Close" class="p-button-sm"
</div> (click)="onClose($event)"></button>
</div>
</div> </div>
+183 -183
View File
@@ -1,183 +1,183 @@
//import { Component, OnInit, OnDestroy } from '@angular/core'; //import { Component, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core';
import { catchError, EMPTY, finalize, Subscription } from 'rxjs'; import { catchError, EMPTY, finalize, Subscription } from 'rxjs';
import { CommonUtilities, HttpUtility, LookupService } from '../shares'; import { CommonUtilities, HttpUtility, LookupService } from '../shares';
import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from 'primeng/dynamicdialog'; import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from '@openng/optimus-ui/dynamicdialog';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { AddPhoto } from './add.photo'; import { AddPhoto } from './add.photo';
import { PersonPhotoDto } from '../models'; import { PersonPhotoDto } from '../models';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { TooltipModule } from 'primeng/tooltip'; import { TooltipModule } from '@openng/optimus-ui/tooltip';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { HttpResponse } from '@angular/common/http'; import { HttpResponse } from '@angular/common/http';
import { PersonService } from '../person'; import { PersonService } from '../person';
@Component({ @Component({
selector: 'photo-list', selector: 'photo-list',
imports: [CommonModule, FormsModule, DynamicDialogModule,TooltipModule, imports: [CommonModule, FormsModule, DynamicDialogModule,TooltipModule,
ButtonModule, TableModule], ButtonModule, TableModule],
templateUrl: './photolist.html', templateUrl: './photolist.html',
styleUrls: ['./photolist.css'], styleUrls: ['./photolist.css'],
providers: [DialogService] providers: [DialogService]
}) })
export class PhotoList implements OnInit, OnDestroy { export class PhotoList implements OnInit, OnDestroy {
FileList: PersonPhotoDto[] =[] FileList: PersonPhotoDto[] =[]
loading = false; loading = false;
_id = -1; _id = -1;
deletePersonPhoto: number[] = []; deletePersonPhoto: number[] = [];
private authenticationService = inject(AuthenticationService); private authenticationService = inject(AuthenticationService);
private cdr = inject(ChangeDetectorRef); private cdr = inject(ChangeDetectorRef);
private confirmationService = inject(ConfirmationService); private confirmationService = inject(ConfirmationService);
private messageService = inject(MessageService); private messageService = inject(MessageService);
private subscription: Subscription = new Subscription(); private subscription: Subscription = new Subscription();
downloadFile = signal(false); downloadFile = signal(false);
constructor( constructor(
private http: HttpUtility, private http: HttpUtility,
private personService: PersonService, private personService: PersonService,
public ref: DynamicDialogRef, public config: DynamicDialogConfig, public ref: DynamicDialogRef, public config: DynamicDialogConfig,
public dialogService: DialogService public dialogService: DialogService
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
const id = this.config.data.id; const id = this.config.data.id;
const olist = this.config.data.personPhotos; const olist = this.config.data.personPhotos;
if (olist && olist.length > 0) if (olist && olist.length > 0)
{ {
this.FileList = olist; this.FileList = olist;
this.assignFileId(olist); this.assignFileId(olist);
} }
} }
assignFileId(files: any[]): void { assignFileId(files: any[]): void {
let i = 0; let i = 0;
let id = -1; let id = -1;
let mx = 1; let mx = 1;
for (i = 0; i < files.length; i++) for (i = 0; i < files.length; i++)
{ {
id = files[i].id; id = files[i].id;
if (id < mx) if (id < mx)
{ {
mx = id; mx = id;
} }
} }
this._id = -1 * mx; this._id = -1 * mx;
} }
downloadAttachment(id: number): void { downloadAttachment(id: number): void {
//GetReportFile //GetReportFile
let typeofCall = "personId_" + id.toString(); let typeofCall = "personPhotoId_" + id.toString();
let criteria:any ={id, fileName:''}; let criteria: any = { id };
this.downloadFile.set(false); this.downloadFile.set(false);
this.http.getFileResponse("api/FileUpload/downloadPersonPhoto", criteria).pipe( this.http.getFileResponse("api/FileUpload/DownloadPersonPhotoById", criteria).pipe(
catchError(err => { catchError(err => {
console.error(err.message); console.error(err.message);
return EMPTY; return EMPTY;
}), }),
finalize(() => { finalize(() => {
// this.toastr.success("download completed ok to view now"); // this.toastr.success("download completed ok to view now");
this.downloadFile.set(false); this.downloadFile.set(false);
})).subscribe((response: HttpResponse<Blob>) => { })).subscribe((response: HttpResponse<Blob>) => {
if (response) { if (response) {
console.log("the download report response", response); console.log("the download report response", response);
CommonUtilities.downloadFile(response, typeofCall); CommonUtilities.downloadFile(response, typeofCall);
} }
}); });
} }
onClose(event: Event): void { onClose(event: Event): void {
const nlist = this.FileList.filter( x => x.id < 1); const nlist = this.FileList.filter( x => x.id < 1);
this.ref.close({list: nlist, deleteIds: this.deletePersonPhoto}); this.ref.close({list: nlist, deleteIds: this.deletePersonPhoto});
} }
newSupportDoc(event: Event): void { newSupportDoc(event: Event): void {
this.showEdit(this._id--); this.showEdit(this._id--);
} }
remove(id: number): void { remove(id: number): void {
this.confirmationService.confirm({ this.confirmationService.confirm({
message: 'Do you want to delete this record?', message: 'Do you want to delete this record?',
header: 'Confirmation Delete', header: 'Confirmation Delete',
icon: 'pi pi-info-circle', icon: 'pi pi-info-circle',
rejectLabel: 'Cancel', rejectLabel: 'Cancel',
rejectButtonProps: { rejectButtonProps: {
label: 'Cancel', label: 'Cancel',
severity: 'secondary', severity: 'secondary',
outlined: true, outlined: true,
}, },
acceptButtonProps: { acceptButtonProps: {
label: 'Delete', label: 'Delete',
severity: 'danger', severity: 'danger',
}, },
accept: () => { accept: () => {
this.doRemove(id); this.doRemove(id);
} }
}); });
} }
doRemove(id: number): void { doRemove(id: number): void {
if (id < 0) if (id < 0)
{ {
const nlist = this.FileList.filter(x => x.id != id); const nlist = this.FileList.filter(x => x.id != id);
this.FileList = [...nlist]; this.FileList = [...nlist];
this.cdr.markForCheck(); this.cdr.markForCheck();
} }
else else
{ {
const deletepersonPhoto$ = this.personService.deletePersonPhotoFile(id); const deletepersonPhoto$ = this.personService.deletePersonPhotoFileById(id);
this.subscription.add(deletepersonPhoto$.subscribe( this.subscription.add(deletepersonPhoto$.subscribe(
{ {
next: x => { next: x => {
if (x.statusCode == 1) if (x.statusCode == 1)
{ {
const nlist = this.FileList.filter(x => x.id != id); const nlist = this.FileList.filter(x => x.id != id);
this.FileList = [...nlist]; this.FileList = [...nlist];
this.deletePersonPhoto.push(id); this.deletePersonPhoto.push(id);
this.cdr.markForCheck(); this.cdr.markForCheck();
this.messageService.add({severity:'success', summary: 'delete person photo', detail: "person photo Id " + id }); 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) error:e => console.error("error in client delete person photo", e)
} }
)); ));
} }
} }
showEdit(id: number) { showEdit(id: number) {
const ref = this.dialogService.open(AddPhoto, { const ref = this.dialogService.open(AddPhoto, {
data: { data: {
id, id,
}, },
header: 'Add File', header: 'Add File',
width: '70%', width: '70%',
maximizable: true, maximizable: true,
draggable: true draggable: true
}); });
if (ref) if (ref)
{ {
ref.onClose.subscribe((list: File[]) => { ref.onClose.subscribe((list: File[]) => {
if (list) { if (list) {
//console.log("after close ward edit", item); //console.log("after close ward edit", item);
this.messageService.add({ severity: 'success', summary: 'Attact File', detail: list.length.toString() }); this.messageService.add({ severity: 'success', summary: 'Attact File', detail: list.length.toString() });
let it = id; let it = id;
for (let i = 0; i < list.length; i++) for (let i = 0; i < list.length; i++)
{ {
const item = list[i]; const item = list[i];
this.FileList.push({id: it--, photo: item.name, photoType:'', file: item}); this.FileList.push({id: it--, photo: item.name, photoType:'', file: item});
} }
this.cdr.markForCheck(); this.cdr.markForCheck();
} }
}); });
} }
} }
ngOnDestroy() { ngOnDestroy() {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }
+10 -10
View File
@@ -1,10 +1,10 @@
<div> <div>
<label class="flex mb-2"> Import Excel File</label> <label class="flex mb-2"> Import Excel File</label>
<p-fileupload #fu mode="basic" chooseLabel="Choose" <p-fileupload #fu mode="basic" chooseLabel="Choose"
chooseIcon="pi pi-upload" name="file" chooseIcon="pi pi-upload" name="file"
url='{{baseUrl}}' url='{{baseUrl}}'
maxFileSize="10000000000" (onUpload)="onUpload($event)" /> maxFileSize="10000000000" (onUpload)="onUpload($event)" />
<p-button styleClass="mt-4" label="Upload" (onClick)="fu.upload()" severity="primary" /> <p-button styleClass="mt-4" label="Upload" (onClick)="fu.upload()" severity="primary" />
</div> </div>
+38 -38
View File
@@ -1,38 +1,38 @@
import { Component, inject, OnInit } from '@angular/core'; import { Component, inject, OnInit } from '@angular/core';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload'; import { FileUploadEvent, FileUploadModule } from '@openng/optimus-ui/fileupload';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import { FileUpload } from 'primeng/fileupload'; import { FileUpload } from '@openng/optimus-ui/fileupload';
import { ToastModule } from 'primeng/toast'; import { ToastModule } from '@openng/optimus-ui/toast';
import { AppSettingService } from '../shares'; import { AppSettingService } from '../shares';
/* /*
interface UploadEvent { interface UploadEvent {
originalEvent: Event; originalEvent: Event;
files: File[]; files: File[];
}*/ }*/
@Component({ @Component({
selector: 'app-import.com', selector: 'app-import.com',
imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload], imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload],
templateUrl: './import.com.html', templateUrl: './import.com.html',
styleUrl: './import.com.css' styleUrl: './import.com.css'
}) })
export class ImportCom implements OnInit { export class ImportCom implements OnInit {
uploadedFiles: any[] = []; uploadedFiles: any[] = [];
baseUrl = ""; baseUrl = "";
private appSetting = inject(AppSettingService); private appSetting = inject(AppSettingService);
constructor(private messageService: MessageService) {} constructor(private messageService: MessageService) {}
ngOnInit(): void ngOnInit(): void
{ {
const cbaseUrl = this.appSetting.appSetting.baseUrl; const cbaseUrl = this.appSetting.appSetting.baseUrl;
//http://localhost:5015/api/FileUpload/UploadFile" //http://localhost:5015/api/FileUpload/UploadFile"
this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile"; this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile";
} }
onUpload(event:FileUploadEvent) { onUpload(event:FileUploadEvent) {
for(let file of event.files) { for(let file of event.files) {
this.uploadedFiles.push(file); this.uploadedFiles.push(file);
} }
this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''}); this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''});
} }
} }
+12 -12
View File
@@ -1,12 +1,12 @@
<form runat="server"> <form runat="server">
<input accept="image/*" type='file' id="imgInp" /> <input accept="image/*" type='file' id="imgInp" />
<img id="blah" src="#" alt="your image" /> <img id="blah" src="#" alt="your image" />
</form> </form>
imgInp.onchange = evt => { imgInp.onchange = evt => {
const [file] = imgInp.files const [file] = imgInp.files
if (file) { if (file) {
blah.src = URL.createObjectURL(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 { .btnloginRigh {
display: flex; display: flex;
flex-direction:reverse; flex-direction:reverse;
justify-content:flex-end; justify-content:flex-end;
width: 100%; width: 100%;
} }
.myerror { .myerror {
background-color: red; background-color: red;
color: white; color: white;
} }
+44 -44
View File
@@ -1,45 +1,45 @@
<div class="flex justify-center items-center h-full"> <div class="flex justify-center items-center h-full">
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" style="max-width: 600px;min-width: 300px;"> <form [formGroup]="loginForm" (ngSubmit)="onSubmit()" style="max-width: 600px;min-width: 300px;">
<p-card> <p-card>
<div> <div>
<img class="border-round" style="width:100%;height:100px;" alt="Logo" src="images/application_image_login.png"> <img class="border-round" style="width:100%;height:100px;" alt="Logo" src="images/application_image_login.png">
</div> </div>
<div class="mt-4"> <div class="mt-4">
<div class="flex flex-col"> <div class="flex flex-col">
<label for="email" class="w-full">Email<strong class="validateStar">*</strong></label> <label for="email" class="w-full">Email<strong class="validateStar">*</strong></label>
<input type="text" pInputText formControlName="username" name="email" <input type="text" pInputText formControlName="username" name="email"
[ngClass]="{ 'is-invalid': submitted && f.username.errors }" class="p-inputtext-sm inputfield"> [ngClass]="{ 'is-invalid': submitted && f.username.errors }" class="p-inputtext-sm inputfield">
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<label for="ppassword" class="w-full">Password <strong class="validateStar">*</strong></label> <label for="ppassword" class="w-full">Password <strong class="validateStar">*</strong></label>
<input type="password" formControlName="password" name="ppassword" pInputText <input type="password" formControlName="password" name="ppassword" pInputText
[ngClass]="{ 'is-invalid': submitted && f.password.errors }" class="p-inputtext-sm inputfield"> [ngClass]="{ 'is-invalid': submitted && f.password.errors }" class="p-inputtext-sm inputfield">
</div> </div>
</div> </div>
<!--p-footer --> <!--p-footer -->
<div class="btnloginRigh"> <div class="btnloginRigh">
<div class="mt-4 pt-2"> <div class="mt-4 pt-2">
<button pButton type="submit" label="Login" class="p-button-sm" [disabled]="isFieldsChange" <button pButton type="submit" label="Login" class="p-button-sm" [disabled]="isFieldsChange"
[loading]="loading"></button> [loading]="loading"></button>
</div> </div>
</div> </div>
<!--/p-footer--> <!--/p-footer-->
</p-card> </p-card>
<div class="myerror">{{error}}</div> <div class="myerror">{{error}}</div>
<div style="padding-top: 10px;"></div> <div style="padding-top: 10px;"></div>
<!--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;"> 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 > <p >
For management access to this application please email For management access to this application please email
</p> </p>
<a href="mailto:NBMLHD-fleetteamreceipt@health.nsw.gov.au">NBMLHD-fleetteamreceipt@health.nsw.gov.au <a href="mailto:NBMLHD-fleetteamreceipt@health.nsw.gov.au">NBMLHD-fleetteamreceipt@health.nsw.gov.au
</a> </a>
</div--> </div-->
<!--pre> {{loginForm.value|json}} </pre--> <!--pre> {{loginForm.value|json}} </pre-->
</form> </form>
</div> </div>
+137 -135
View File
@@ -1,135 +1,137 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { Router, ActivatedRoute, RouterModule } from '@angular/router'; import { Router, ActivatedRoute, RouterModule } from '@angular/router';
import { FormBuilder, Validators, FormGroup } from '@angular/forms'; import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { ConfirmationService } from 'primeng/api'; import { ConfirmationService } from '@openng/optimus-ui/api';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CardModule } from 'primeng/card'; import { CardModule } from '@openng/optimus-ui/card';
import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { Utils } from '../shares'; import { AppSettingService, Utils } from '../shares';
@Component({ @Component({
templateUrl: './login.html', templateUrl: './login.html',
styleUrl: './login.css', styleUrl: './login.css',
selector: 'login', selector: 'login',
imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule, imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule,
RouterModule, CommonModule, CardModule], RouterModule, CommonModule, CardModule],
}) })
export class Login implements OnInit, OnDestroy { export class Login implements OnInit, OnDestroy {
loginForm: FormGroup; loginForm: FormGroup;
confirmationService = inject(ConfirmationService); confirmationService = inject(ConfirmationService);
route = inject(ActivatedRoute); route = inject(ActivatedRoute);
router = inject(Router); router = inject(Router);
authenticationService = inject(AuthenticationService); authenticationService = inject(AuthenticationService);
formBuilder = inject(FormBuilder); formBuilder = inject(FormBuilder);
loading = false; private appSetting =inject(AppSettingService);
homeUrl = "/person"; loading = false;
submitted = false; homeUrl = "/person";
isChange = true; // disable use false//true for not disable. make sure it true is disable button. submitted = false;
subChanged$ = new Subject<boolean>(); isChange = true; // disable use false//true for not disable. make sure it true is disable button.
// get return url from route parameters or default to '/' subChanged$ = new Subject<boolean>();
returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl; // get return url from route parameters or default to '/'
error = ''; returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl;
private subscription: Subscription = new Subscription(); error = '';
private subscription: Subscription = new Subscription();
constructor() {
constructor() {
this.loginForm = this.formBuilder.group({ const default_us = this.appSetting.appSetting.username;
username: ['kham.vilaythong@gmail.com', Validators.required], const pass = this.appSetting.appSetting.prefill_ps;
password: ['password', Validators.required], this.loginForm = this.formBuilder.group({
username: [default_us, Validators.required],
}); password: [pass, Validators.required],
}
});
ngOnInit() { }
// reset login status
this.loading = false; ngOnInit() {
this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false)); // reset login status
this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); 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);
} updateData($event: Event) {
console.log("onChange click", $event);
// convenience getter for easy access to form fields }
get f() { return this.loginForm.value; }
get isFieldsChange() { // convenience getter for easy access to form fields
const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not get f() { return this.loginForm.value; }
//console.log(this.msg + 'is fields change', chan); get isFieldsChange() {
return chan; const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not
} //console.log(this.msg + 'is fields change', chan);
onSubmit() { return chan;
}
//this.playAudio(); onSubmit() {
// stop here if form is invalid //this.playAudio();
if (this.loginForm.invalid) {
return; // stop here if form is invalid
} if (this.loginForm.invalid) {
const fvalue = this.loginForm.value; return;
this.loading = true; }
this.error = ""; const fvalue = this.loginForm.value;
let username = ""; this.loading = true;
let password = ""; this.error = "";
if (fvalue.username != null) let username = "";
username = fvalue.username; let password = "";
if (fvalue.password != null) if (fvalue.username != null)
password = fvalue.password; username = fvalue.username;
this.authenticationService.login(username, password) if (fvalue.password != null)
.pipe(first()) password = fvalue.password;
.subscribe({ this.authenticationService.login(username, password)
next: (x: any) => { .pipe(first())
this.loading = false; .subscribe({
console.log("login result ", x.data, this.returnUrl); next: (x: any) => {
if (x.statusCode == 1) { this.loading = false;
// if (x.data.role == 1) { console.log("login result ", x.data, this.returnUrl);
// this.homeService.reportDate = Utils.getLastMonth(); if (x.statusCode == 1) {
// this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } }); // 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 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
else { this.router.navigate([this.homeUrl]);
this.loginForm.patchValue({ password: '' }); }
// alert("Invalid Username or Password. Please try again."); else {
console.error("error in login", x); this.loginForm.patchValue({ password: '' });
this.confirmationService.confirm({ // alert("Invalid Username or Password. Please try again.");
message: 'Error in Login: Invalid username or password. May be your password is expired', console.error("error in login", x);
header: 'Error Login', this.confirmationService.confirm({
icon: 'pi pi-info-circle', message: 'Error in Login: Invalid username or password. May be your password is expired',
rejectVisible: false, header: 'Error Login',
acceptLabel: 'OK', icon: 'pi pi-info-circle',
rejectVisible: false,
}); acceptLabel: 'OK',
}
}, });
error: er => { }
console.log("error in login", er); },
this.error = er.message; error: er => {
this.loading = false; console.log("error in login", er);
} this.error = er.message;
}); this.loading = false;
}
} });
ngOnDestroy(): void { }
this.subscription.unsubscribe();
this.loading = false; ngOnDestroy(): void {
} this.subscription.unsubscribe();
this.loading = false;
} }
}
+6 -6
View File
@@ -1,6 +1,6 @@
export interface Code { export interface Code {
id:number; id:number;
name:string; name:string;
status?:string; status?:string;
active?:boolean; active?:boolean;
} }
+16 -16
View File
@@ -1,17 +1,17 @@
export enum ConfigureUrl export enum ConfigureUrl
{ {
//baseUrl = "http://localhost:61744", //baseUrl = "http://localhost:61744",
loginApiUrl = "api/Users/Login", loginApiUrl = "api/Users/Login",
searchStaffUrl = "api/Users/SearchADStaff", searchStaffUrl = "api/Users/SearchADStaff",
staffUrl = "api/Staff", staffUrl = "api/Staff",
personUrl = "api/Person", personUrl = "api/Person",
staffWorkUrl = "api/StaffWork", staffWorkUrl = "api/StaffWork",
jobUrl ="api/Job", jobUrl ="api/Job",
clientUrl ="api/Client", clientUrl ="api/Client",
relationShipUrl ="api/RelationShip", relationShipUrl ="api/RelationShip",
logoutUrl ="api/Users/Logout", logoutUrl ="api/Users/Logout",
adminUserUrl = "api/Staff", adminUserUrl = "api/Staff",
lookupUrl = "api/Lookup", lookupUrl = "api/Lookup",
FileUploadUrl = "api/FileUpload", FileUploadUrl = "api/FileUpload",
} }
+40 -37
View File
@@ -1,37 +1,40 @@
export const MIMEType = { export const MIMEType = {
png: 'image/png', png: 'image/png',
jpg: 'image/jpg', jpg: 'image/jpg',
jpeg: 'image/jpeg', jpeg: 'image/jpeg',
gif: 'image/gif', gif: 'image/gif',
txt: 'text/plain', txt: 'text/plain',
pdf: 'application/pdf' pdf: 'application/pdf'
}; };
//for status of object 0 no change, 1 changed , -1 is deleted. //for status of object 0 no change, 1 changed , -1 is deleted.
export enum mState { export enum mState {
Delete = -1, Delete = -1,
NoChange = 0, NoChange = 0,
Modified = 1, Modified = 1,
New =2, New =2,
}; };
export enum enumPhoto {
export enum userRole { Profile = 1,
Normal = 1, Other = 2
Admin = 2, }
ServiceManager =3, export enum userRole {
Accounting = 4, Normal = 1,
WorkShop = 5, Admin = 2,
ServiceManager =3,
}; Accounting = 4,
WorkShop = 5,
export enum enumReqStatus {
Canel = -1, };
Decline = -2,
NewRequest = 0, export enum enumReqStatus {
Allocate = 1, Canel = -1,
Arrival = 2, Decline = -2,
Completed = 3, NewRequest = 0,
OnHold = 4, Allocate = 1,
OffHold = 5 Arrival = 2,
}; Completed = 3,
OnHold = 4,
OffHold = 5
};
+10 -10
View File
@@ -1,11 +1,11 @@
export * from './code'; export * from './code';
export * from './configureUrl'; export * from './configureUrl';
export * from './user'; export * from './user';
export * from './resultmodel'; export * from './resultmodel';
export * from './enum'; export * from './enum';
export * from './lookup'; export * from './lookup';
export * from './staff'; export * from './staff';
export * from './person'; export * from './person';
export * from './job'; export * from './job';
export * from './relationship'; export * from './relationship';
export * from './personphotodto'; export * from './personphotodto';
+12 -12
View File
@@ -1,12 +1,12 @@
export interface JobSearch { export interface JobSearch {
code:string; code:string;
description:string; description:string;
} }
export interface Job { export interface Job {
id:number; id:number;
code:string|null|undefined; code:string|null|undefined;
description:string |null|undefined; description:string |null|undefined;
active:boolean |null|undefined; active:boolean |null|undefined;
} }
+15 -15
View File
@@ -1,16 +1,16 @@
// using for priority and infectionType // using for priority and infectionType
export interface Lookup { export interface Lookup {
id:number; id:number;
codeId:string; codeId:string;
description:string; description:string;
} }
export interface LookupEdit { export interface LookupEdit {
id:number; id:number;
codeId:string; codeId:string;
description:string; description:string;
active:boolean; active:boolean;
type:string; type:string;
} }
+6 -6
View File
@@ -1,7 +1,7 @@
export interface MyDetail{ export interface MyDetail{
id:number, id:number,
login:string, login:string,
firstname:string, firstname:string,
surname:string, surname:string,
jobTitle:string, jobTitle:string,
} }
+37 -37
View File
@@ -1,37 +1,37 @@
import { PersonPhotoDto } from "./personphotodto"; import { PersonPhotoDto } from "./personphotodto";
import { RelationShip } from "./relationship"; import { RelationShip } from "./relationship";
export interface FamilySearch { export interface FamilySearch {
email:string|null; email:string|null;
phone:string |null; phone:string |null;
clientname:string |null; clientname:string |null;
} }
export interface Person { export interface Person {
id: number; id: number;
title?:string| null| undefined; title?:string| null| undefined;
firstName: string | null|undefined; firstName: string | null|undefined;
lastName: string | null|undefined; lastName: string | null|undefined;
email: string | null|undefined; email: string | null|undefined;
phone: string | null|undefined; phone: string | null|undefined;
address?: string | null|undefined; address?: string | null|undefined;
alive: boolean | null|undefined; alive: boolean | null|undefined;
dob? : string | null|undefined; dob? : string | null|undefined;
fatherId?: number| null|undefined; fatherId?: number| null|undefined;
motherId?: number| null|undefined; motherId?: number| null|undefined;
image?: string|null|undefined; image?: string|null|undefined;
sex?: string|null|undefined; sex?: string|null|undefined;
fatherName?:string |null; fatherName?:string |null;
motherName?:string |null; motherName?:string |null;
relationShips?: RelationShip[]; relationShips?: RelationShip[];
personPhotos?: PersonPhotoDto[]; personPhotos?: PersonPhotoDto[];
} }
export interface PersonContainer export interface PersonContainer
{ {
person: Person; person: Person;
formData?:FormData; formData?:FormData;
} }
+5 -5
View File
@@ -1,6 +1,6 @@
export interface PersonPhotoDto { export interface PersonPhotoDto {
id: number; id: number;
photo:string|null | undefined; photo:string|null | undefined;
photoType:string |null | undefined; photoType:string |null | undefined;
file: File | null | undefined; file: File | null | undefined;
} }
+26 -26
View File
@@ -1,27 +1,27 @@
import { mState } from "./enum"; import { mState } from "./enum";
export interface RelationShip { export interface RelationShip {
id: number; id: number;
relatePersonId: number; relatePersonId: number;
personId: number; personId: number;
state: mState; state: mState;
} }
//relationship id this relatePersonId person has relation with other person //relationship id this relatePersonId person has relation with other person
// also other person has also has relation with you. look in two way // also other person has also has relation with you. look in two way
// person id = 1 has to relation id = 90 // person id = 1 has to relation id = 90
// that means when you edit person 90 their relation ship also be there. // 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. // so personid = 1 and relation id = 90 we need to show other way around.
//example first name = Smith has partner Jennifer //example first name = Smith has partner Jennifer
// when we edit Jennifer it should show Smith as her partner too. // when we edit Jennifer it should show Smith as her partner too.
export interface RelationShipView { export interface RelationShipView {
id: number; id: number;
relatePersonId:number; relatePersonId:number;
personId : number; personId : number;
pfirstName: string |null |undefined; pfirstName: string |null |undefined;
plastName: string |null |undefined; plastName: string |null |undefined;
sex:string |null |undefined; sex:string |null |undefined;
state: mState; state: mState;
} }
+4 -4
View File
@@ -1,5 +1,5 @@
export interface ResultModel<T> { export interface ResultModel<T> {
data: T; data: T;
message: string; message: string;
statusCode:number; statusCode:number;
} }
+39 -39
View File
@@ -1,39 +1,39 @@
export interface StaffView { export interface StaffView {
id:number, id:number,
email:string, email:string,
firstname:string, firstname:string,
lastname:string, lastname:string,
active:boolean, active:boolean,
} }
export interface ResetPassword{ export interface ResetPassword{
id:number; id:number;
password:string; password:string;
} }
export interface Staff { export interface Staff {
id:number, id:number,
email:string, email:string,
firstname:string, firstname:string,
lastname:string, lastname:string,
phone:string, phone:string,
type:number; type:number;
active:boolean; active:boolean;
roleType: number; roleType: number;
password?:string, password?:string,
} }
export interface StaffSearch { export interface StaffSearch {
email:string; email:string;
firstName:string; firstName:string;
lastName:string; lastName:string;
} }
//this use for new user only. //this use for new user only.
export interface AdminUserNew { export interface AdminUserNew {
loginId:number; loginId:number;
user: Staff;// first get userid from this table and user: Staff;// first get userid from this table and
} }
+36 -36
View File
@@ -1,36 +1,36 @@
export class User { export class User {
id: number = 0; id: number = 0;
username: string = ''; username: string = '';
role: number = -1; role: number = -1;
firstName: string = ''; firstName: string = '';
lastName: string = ''; lastName: string = '';
email:string = ''; email:string = '';
token:string =''; token:string ='';
position: string =''; position: string ='';
department: string = ''; department: string = '';
managerEmail:string =''; managerEmail:string ='';
phone:string=''; phone:string='';
} }
export interface UserAD export interface UserAD
{ {
id: number, id: number,
firstName: string, firstName: string,
lastName: string, lastName: string,
email: string, email: string,
username: string, username: string,
department: string, department: string,
position: string, position: string,
role: number, role: number,
phone:string, phone:string,
managerEmail:string managerEmail:string
} }
export class SecAccessLevel { export class SecAccessLevel {
accessName:string=''; accessName:string='';
request:number=0; request:number=0;
report:number=0; report:number=0;
admin:number=0; admin:number=0;
allocation:number=0; allocation:number=0;
} }
+29 -38
View File
@@ -1,51 +1,41 @@
//mypreset.ts //mypreset.ts
import { definePreset } from '@primeuix/themes'; import { definePreset } from '@openng/optimus-ui-themes';
import Aura from '@primeuix/themes/aura'; import Aura from '@openng/optimus-ui-themes/aura';
import { primitive } from '@primeuix/themes/aura/base';
const MyPreset = definePreset(Aura, { const MyPreset = definePreset(Aura, {
semantic: { semantic: {
colorScheme: { primary: {
primitive: { 50: '{blue.50}',
cyan: { 100: '{blue.100}',
50: '{cyan.50}', 200: '{blue.200}',
100: '{cyan.100}', 300: '{blue.300}',
200: '{cyan.200}', 400: '{blue.400}',
300: '{cyan.300}', 500: '{blue.500}',
400: '{cyan.400}', 600: '{blue.600}',
500: '{cyan.500}', 700: '{blue.700}',
} 800: '{blue.800}',
900: '{blue.900}',
}, 950: '{blue.950}'
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}'
}, },
colorScheme: {
light: { light: {
surface: { surface: {
0: '#ffffff', 0: '#ffffff',
50: '{zinc.50}', 50: '{blue.50}',
100: '{zinc.100}', 100: '{blue.100}',
200: '{zinc.200}', 200: '{blue.200}',
300: '{zinc.300}', 300: '{blue.300}',
400: '{zinc.400}', 400: '{blue.400}',
500: '{zinc.500}', 500: '{blue.500}',
600: '{zinc.600}', 600: '{blue.600}',
700: '{zinc.700}', 700: '{blue.700}',
800: '{zinc.800}', 800: '{blue.800}',
900: '{zinc.900}', 900: '{blue.900}',
950: '{zinc.950}' 950: '{blue.950}'
} }
}, },
dark: { dark: {
@@ -65,6 +55,7 @@ const MyPreset = definePreset(Aura, {
} }
} }
} }
} }
}); });
+12 -12
View File
@@ -1,12 +1,12 @@
table { table {
width: 100%; width: 100%;
} }
.mat-form-field { .mat-form-field {
font-size: 14px; font-size: 14px;
width: 100%; width: 100%;
} }
td.mat-column-edit, .mat-column-delete { td.mat-column-edit, .mat-column-delete {
width: 35px; width: 35px;
padding-right: 2px; padding-right: 2px;
} }
+39 -39
View File
@@ -1,39 +1,39 @@
<div class="shadow-2xl rounded p-2 mt-2"> <div class="shadow-2xl rounded p-2 mt-2">
<h3>Family Tree </h3> <h3>Family Tree </h3>
<div> <div>
<label>link from partner and Father and Mother Id</label> <label>link from partner and Father and Mother Id</label>
<div > <div >
<!--this styleclass all logic in api one not here. background color --> <!--this styleclass all logic in api one not here. background color -->
<!--the treeNode.Data has treeData image and sex--> <!--the treeNode.Data has treeData image and sex-->
<!--image is 64 string so to show 'data:image/png;base64,'imageStr' here --> <!--image is 64 string so to show 'data:image/png;base64,'imageStr' here -->
<!-- selectionMode="multiple" [(selection)]="selectedNodes" --> <!-- selectionMode="multiple" [(selection)]="selectedNodes" -->
<p-organization-chart [value]="familyTree()" [collapsible]="true"> <p-organization-chart [value]="familyTree()" [collapsible]="true">
<ng-template let-node pTemplate="default"> <ng-template let-node pTemplate="default">
<div #targetEl class="flex flex-col items-center"> <div #targetEl class="flex flex-col items-center">
@if (node.data.image != "") @if (node.data.image != "")
{ {
<img (mouseover)="op.show($event, targetEl)" [src]= "node.data.image" class="mb-2 w-12 h-12 rounded-full " /> <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" /--> <!--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 class="mt-2 font-medium text-lg">{{ node.label }}</div>
<div>{{ node.data.sex }} </div> <div>{{ node.data.sex }} </div>
<p-popover #op class="flex w-2/5 h-2/5"> <p-popover #op class="flex w-2/5 h-2/5">
@if (node.data.image != "") @if (node.data.image != "")
{ {
<div> <div>
<p-button (onClick)="op.hide()" icon="pi pi-times" [rounded]="true" [text]="true" severity="danger" class="flex justify-end mb-2" /> <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" /> <img [src]= "node.data.image" alt="person profile" />
</div> </div>
} }
</p-popover> </p-popover>
</div> </div>
</ng-template> </ng-template>
</p-organization-chart> </p-organization-chart>
</div> </div>
<div class="flex justify-end"> <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" <button pButton pRipple class="p-button-sm mr-2 p-button-success" type="button" icon="pi pi-sign-in" label="Close"
(click)="close()"></button> (click)="close()"></button>
</div> </div>
</div> </div>
+256 -256
View File
@@ -1,256 +1,256 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person, RelationShip } from '../models'; import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart'; import { OrganizationChartModule } from '@openng/optimus-ui/organizationchart';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { TreeNode } from 'primeng/api'; import { TreeNode } from '@openng/optimus-ui/api';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DialogService, DynamicDialogConfig, DynamicDialogRef } from '@openng/optimus-ui/dynamicdialog';
import { PersonEdit } from './person.edit'; import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from '@openng/optimus-ui/tree';
import { Utils } from '../shares'; import { Utils } from '../shares';
import { PopoverModule } from 'primeng/popover'; import { PopoverModule } from '@openng/optimus-ui/popover';
@Component({ @Component({
selector: 'family-orga', selector: 'family-orga',
templateUrl: './family.orga.html', templateUrl: './family.orga.html',
imports:[TableModule,FormsModule,TreeModule,PopoverModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule], imports:[TableModule,FormsModule,TreeModule,PopoverModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
styleUrls: ['./family.orga.css'], styleUrls: ['./family.orga.css'],
providers: [DialogService] providers: [DialogService]
}) })
export class FamilyOrga implements OnInit, OnDestroy{ export class FamilyOrga implements OnInit, OnDestroy{
private subscription:Subscription = new Subscription(); private subscription:Subscription = new Subscription();
person!: Person; person!: Person;
selectedNode!: TreeNode; selectedNode!: TreeNode;
selectedNodes!: TreeNode[]; selectedNodes!: TreeNode[];
private cd = inject(ChangeDetectorRef); private cd = inject(ChangeDetectorRef);
familyTree = signal<TreeNode[]>([]); familyTree = signal<TreeNode[]>([]);
relations: RelationShip[] =[]; relations: RelationShip[] =[];
_id = -10; _id = -10;
loading = false; loading = false;
familyList:Person[] = []; familyList:Person[] = [];
msg ="[Person organise component]"; msg ="[Person organise component]";
private messageService = inject(MessageService); private messageService = inject(MessageService);
public ref = inject(DynamicDialogRef); public ref = inject(DynamicDialogRef);
public config = inject(DynamicDialogConfig); public config = inject(DynamicDialogConfig);
/* /*
private store: Store<appValidationState> private store: Store<appValidationState>
*/ */
private personService = inject(PersonService); private personService = inject(PersonService);
public dialogService = inject(DialogService); public dialogService = inject(DialogService);
ngOnInit(): void ngOnInit(): void
{ {
const id = this.config.data.id; const id = this.config.data.id;
this.familyList = this.config.data.familyList; this.familyList = this.config.data.familyList;
const item = this.familyList.find(x => x.id == id); const item = this.familyList.find(x => x.id == id);
if (item != undefined) if (item != undefined)
this.person = item; this.person = item;
this.loadPersonFamilyTree(id); this.loadPersonFamilyTree(id);
//this.populateTree(); //this.populateTree();
} }
populateTree() : void { populateTree() : void {
const data: TreeNode[] = [ const data: TreeNode[] = [
{ {
expanded: true, expanded: true,
type: 'person', type: 'person',
styleClass: 'bg-indigo-500 text-white', styleClass: 'bg-blue-500 text-white',
data: { data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png', image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png',
name: 'Amy Elsner', name: 'Amy Elsner',
title: 'CEO' title: 'CEO'
}, },
children: [ children: [
{ {
expanded: true, expanded: true,
type: 'person', type: 'person',
styleClass: 'bg-purple-500 text-white', styleClass: 'bg-purple-500 text-white',
data: { data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png', image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png',
name: 'Anna Fali', name: 'Anna Fali',
title: 'CMO' title: 'CMO'
}, },
children: [ children: [
{ {
label: 'Sales', label: 'Sales',
styleClass: 'bg-purple-500 text-white', styleClass: 'bg-purple-500 text-white',
style: ' border-radius: 12px' style: ' border-radius: 12px'
}, },
{ {
label: 'Marketing', label: 'Marketing',
styleClass: 'bg-purple-500 text-white', styleClass: 'bg-purple-500 text-white',
style: ' border-radius: 12px' style: ' border-radius: 12px'
} }
] ]
}, },
{ {
expanded: true, expanded: true,
type: 'person', type: 'person',
styleClass: 'bg-teal-500 text-white', styleClass: 'bg-teal-500 text-white',
data: { data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png', image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png',
name: 'Stephen Shaw', name: 'Stephen Shaw',
title: 'CTO' title: 'CTO'
}, },
children: [ children: [
{ {
label: 'Development', label: 'Development',
styleClass: 'bg-teal-500 text-white' styleClass: 'bg-teal-500 text-white'
}, },
{ {
label: 'UI/UX Design', label: 'UI/UX Design',
styleClass: 'bg-teal-500 text-white' styleClass: 'bg-teal-500 text-white'
} }
] ]
} }
] ]
} }
]; ];
this.familyTree.set(data); this.familyTree.set(data);
} }
loadPersonFamilyTree(id: number): void { loadPersonFamilyTree(id: number): void {
const relationShip$ = this.personService.loadPersonFamily(id); const relationShip$ = this.personService.loadPersonFamily(id);
this.subscription.add(relationShip$.subscribe( this.subscription.add(relationShip$.subscribe(
{ {
next: x => { next: x => {
if (x.statusCode == 1) if (x.statusCode == 1)
{ {
let tree : TreeNode[] =[]; let tree : TreeNode[] =[];
tree.push(x.data); tree.push(x.data);
this.familyTree.set(tree); this.familyTree.set(tree);
console.log("load person family", this.familyTree()); console.log("load person family", this.familyTree());
} }
}, },
error: e => { error: e => {
console.error("error load loadPersonFamily by personId", id); console.error("error load loadPersonFamily by personId", id);
this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message}); this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message});
} }
} }
)); ));
} }
nodeSelect(event: TreeNodeSelectEvent) { nodeSelect(event: TreeNodeSelectEvent) {
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label }); // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
} }
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void { nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
// console.log("double click node", event.node.key); // console.log("double click node", event.node.key);
const id = event.node.key; const id = event.node.key;
this.edit(Number(id)); this.edit(Number(id));
} }
getName(id:number): string getName(id:number): string
{ {
let result =""; let result ="";
const item = this.familyList.find(x => x.id == id); const item = this.familyList.find(x => x.id == id);
if (item) if (item)
result = item.lastName + " " + item.firstName; result = item.lastName + " " + item.firstName;
return result; return result;
} }
updateParent(list:Person[]):void { updateParent(list:Person[]):void {
let i = 0; let i = 0;
let item:Person; let item:Person;
for (i = 0; i< list.length; i++) for (i = 0; i< list.length; i++)
{ {
item = list[i]; item = list[i];
if (item.fatherId && item.fatherId > 0) if (item.fatherId && item.fatherId > 0)
{ {
item.fatherName = this.getName(item.fatherId); item.fatherName = this.getName(item.fatherId);
} }
} }
} }
newFamily():void { newFamily():void {
//console.log("add new employee"); //console.log("add new employee");
this.personService.parentList = this.familyList; this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } }); // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--); this.showEdit(this._id--);
} }
edit(id: number) : void { edit(id: number) : void {
//console.log("edit family", id); //console.log("edit family", id);
this.personService.parentList = this.familyList; this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } }); // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id); this.showEdit(id);
} }
showEdit(id:number) { showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, { const ref = this.dialogService.open(PersonEdit, {
data: { data: {
id, id,
familyList: this.familyList, familyList: this.familyList,
}, },
header: 'Family', header: 'Family',
width: '80%', width: '80%',
maximizable: true maximizable: true
}); });
if (ref) if (ref)
{ {
ref.onClose.subscribe((item: Person) => { ref.onClose.subscribe((item: Person) => {
if (item) { if (item) {
//console.log("after close ward edit", item); //console.log("after close ward edit", item);
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list //update the current list
this.updateList(item); this.updateList(item);
} }
}); });
} }
} }
updateList(item: Person) :void { updateList(item: Person) :void {
const idx = this.familyList.findIndex( x => x.id == item.id); const idx = this.familyList.findIndex( x => x.id == item.id);
if (item.fatherId && item.fatherId > 0) if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId); item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0) if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId); item.motherName = this.getName(item.motherId);
if (idx < 0) if (idx < 0)
{ {
const olist = [... this.familyList, item]; const olist = [... this.familyList, item];
this.familyList = olist; this.familyList = olist;
} }
else else
{ {
const oitem = this.familyList[idx]; const oitem = this.familyList[idx];
oitem.firstName = item.firstName; oitem.firstName = item.firstName;
oitem.lastName = item.lastName; oitem.lastName = item.lastName;
oitem.address = item.address; oitem.address = item.address;
oitem.alive = item.alive; oitem.alive = item.alive;
oitem.dob = item.dob; oitem.dob = item.dob;
oitem.email = item.email; oitem.email = item.email;
oitem.fatherId = item.fatherId; oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId; oitem.motherId = item.motherId;
oitem.motherName = item.motherName; oitem.motherName = item.motherName;
oitem.fatherName = item.fatherName; oitem.fatherName = item.fatherName;
} }
} }
deleteItem(id: number): void { deleteItem(id: number): void {
this.personService.deletePerson(id) this.personService.deletePerson(id)
.pipe(take(1)) .pipe(take(1))
.subscribe({ next: result => { .subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result); console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList.filter(d => d.id !== id); const nlist = this.familyList.filter(d => d.id !== id);
this.familyList = nlist; this.familyList = nlist;
}, },
error: e => console.error(e) error: e => console.error(e)
}); });
//console.log(this.msg + "click button to delete"); //console.log(this.msg + "click button to delete");
} }
close() :void { close() :void {
this.ref.close(null); this.ref.close(null);
} }
ngOnDestroy() { ngOnDestroy() {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }
+12 -12
View File
@@ -1,12 +1,12 @@
table { table {
width: 100%; width: 100%;
} }
.mat-form-field { .mat-form-field {
font-size: 14px; font-size: 14px;
width: 100%; width: 100%;
} }
td.mat-column-edit, .mat-column-delete { td.mat-column-edit, .mat-column-delete {
width: 35px; width: 35px;
padding-right: 2px; padding-right: 2px;
} }
+35 -35
View File
@@ -1,35 +1,35 @@
<div class="shadow-2xl rounded p-2 mt-2"> <div class="shadow-2xl rounded p-2 mt-2">
<h3>Family Tree by Person has Father</h3> <h3>Family Tree by Person has Father</h3>
<div> <div>
<form> <form>
<div class="grid grid-cols-12 gap-2"> <div class="grid grid-cols-12 gap-2">
<div class="flex items-center"> <div class="flex items-center">
<p-checkbox inputId="isFather" [binary]="true" name="isFather" [(ngModel)]="isFather" /> <p-checkbox inputId="isFather" [binary]="true" name="isFather" [(ngModel)]="isFather" />
<label for="isFather" class="ml-2"> Is Father </label> <label for="isFather" class="ml-2"> Is Father </label>
</div> </div>
<div class="flex items-end"> <div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-sitemap" label="Load" <button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-sitemap" label="Load"
(click)="load()"></button> (click)="load()"></button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div> <div>
<p-tree [value]="familyTree" class="w-full md:w-[30rem]" <p-tree [value]="familyTree" class="w-full md:w-[30rem]"
(onNodeDoubleClick)="nodeDoubleSelect($event)" (onNodeDoubleClick)="nodeDoubleSelect($event)"
selectionMode="single" [(selection)]="selectedNode" (onNodeSelect)="nodeSelect($event)" /> selectionMode="single" [(selection)]="selectedNode" (onNodeSelect)="nodeSelect($event)" />
<!--div class="card flex justify-center overflow-x-auto"> <!--div class="card flex justify-center overflow-x-auto">
<p-organization-chart [value]="familyTree" selectionMode="multiple" [(selection)]="selectedNodes" [collapsible]="true"> <p-organization-chart [value]="familyTree" selectionMode="multiple" [(selection)]="selectedNodes" [collapsible]="true">
<ng-template let-node pTemplate="person"> <ng-template let-node pTemplate="person">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">
<img [src]="node.data.image" class="mb-4 w-12 h-12" /> <img [src]="node.data.image" class="mb-4 w-12 h-12" />
<div class="font-bold mb-2">{{ node.data.name }}</div> <div class="font-bold mb-2">{{ node.data.name }}</div>
<div>{{ node.data.title }}</div> <div>{{ node.data.title }}</div>
</div> </div>
</div> </div>
</ng-template> </ng-template>
</p-organization-chart> </p-organization-chart>
</div--> </div-->
</div> </div>
+227 -227
View File
@@ -1,227 +1,227 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models'; import { StaffView ,StaffSearch, Person } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart'; import { OrganizationChartModule } from '@openng/optimus-ui/organizationchart';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { TreeNode } from 'primeng/api'; import { TreeNode } from '@openng/optimus-ui/api';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from '@openng/optimus-ui/dynamicdialog';
import { PersonEdit } from './person.edit'; import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import { CheckboxModule } from 'primeng/checkbox'; import { CheckboxModule } from '@openng/optimus-ui/checkbox';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from '@openng/optimus-ui/tree';
import { Utils } from '../shares'; import { Utils } from '../shares';
@Component({ @Component({
selector: 'family-tree', selector: 'family-tree',
templateUrl: './family.tree.html', templateUrl: './family.tree.html',
imports:[TableModule,FormsModule,TreeModule,CheckboxModule, imports:[TableModule,FormsModule,TreeModule,CheckboxModule,
OrganizationChartModule,CommonModule, OrganizationChartModule,CommonModule,
FormsModule, FormsModule,
ButtonModule,InputTextModule], ButtonModule,InputTextModule],
styleUrls: ['./family.tree.css'], styleUrls: ['./family.tree.css'],
providers: [DialogService] providers: [DialogService]
}) })
export class FamilyTree implements OnInit, OnDestroy{ export class FamilyTree implements OnInit, OnDestroy{
isFather = signal(true); isFather = signal(true);
private subscription:Subscription = new Subscription(); private subscription:Subscription = new Subscription();
firstname = ''; firstname = '';
selectedNode!: TreeNode; selectedNode!: TreeNode;
private cd = inject(ChangeDetectorRef); private cd = inject(ChangeDetectorRef);
familyTree: TreeNode[] = []; familyTree: TreeNode[] = [];
email = ''; email = '';
lastname = ''; lastname = '';
_id = -10; _id = -10;
loading = false; loading = false;
familyList:Person[] = []; familyList:Person[] = [];
msg ="[Person tree component]"; msg ="[Person tree component]";
private messageService = inject(MessageService); private messageService = inject(MessageService);
/* /*
private store: Store<appValidationState> private store: Store<appValidationState>
*/ */
constructor( constructor(
public dialogService: DialogService, public dialogService: DialogService,
private personService: PersonService, private personService: PersonService,
private authenticationService: AuthenticationService, private authenticationService: AuthenticationService,
private router: Router private router: Router
) {} ) {}
getSearchCiteria(): StaffSearch { getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = { let criteria:StaffSearch = {
email: this.email, email: this.email,
firstName: this.firstname, firstName: this.firstname,
lastName: this.lastname, lastName: this.lastname,
}; };
return criteria; return criteria;
} }
canSearch():boolean { canSearch():boolean {
let result = false; let result = false;
result = this.email !== ""; result = this.email !== "";
result = result || this.lastname !== ""; result = result || this.lastname !== "";
result = result || this.firstname !== ""; result = result || this.firstname !== "";
return result; return result;
} }
ngOnInit(): void ngOnInit(): void
{ {
this.authenticationService.isHome = false; this.authenticationService.isHome = false;
this.authenticationService.isReport = false; this.authenticationService.isReport = false;
const prev = this.personService.searchCriteria; const prev = this.personService.searchCriteria;
let goload = true; let goload = true;
this.load(); this.load();
} }
getActive(active:boolean):string { getActive(active:boolean):string {
let result = 'false-icon pi-times-circle'; let result = 'false-icon pi-times-circle';
if (active) if (active)
result = 'true-icon pi-check-circle'; result = 'true-icon pi-check-circle';
return result; return result;
} }
load():void { load():void {
let father = this.isFather(); let father = this.isFather();
this.loading = true; this.loading = true;
//const criteria = this.getSearchCiteria(); //const criteria = this.getSearchCiteria();
// this.personService.searchCriteria = criteria; // this.personService.searchCriteria = criteria;
this.subscription.add( this.subscription.add(
this.personService.loadPersonFamilyTree(father, !father).subscribe( this.personService.loadPersonFamilyTree(father, !father).subscribe(
{ {
next: x => { next: x => {
if (x.statusCode == 1) if (x.statusCode == 1)
{ {
this.familyTree = x.data; this.familyTree = x.data;
this.loading = false; this.loading = false;
this.cd.detectChanges(); this.cd.detectChanges();
console.log("the family tree", this.familyTree); console.log("the family tree", this.familyTree);
} }
}, },
error: e => { error: e => {
const message = e || e.message; const message = e || e.message;
// this.toastr.error(message); // this.toastr.error(message);
this.loading = false; this.loading = false;
console.log("error ", e); console.log("error ", e);
} }
}) })
); );
} }
nodeSelect(event: TreeNodeSelectEvent) { nodeSelect(event: TreeNodeSelectEvent) {
// this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label }); // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
} }
nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void { nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
// console.log("double click node", event.node.key); // console.log("double click node", event.node.key);
const id = event.node.key; const id = event.node.key;
this.edit(Number(id)); this.edit(Number(id));
} }
getName(id:number): string getName(id:number): string
{ {
let result =""; let result ="";
const item = this.familyList.find(x => x.id == id); const item = this.familyList.find(x => x.id == id);
if (item) if (item)
result = item.lastName + " " + item.firstName; result = item.lastName + " " + item.firstName;
return result; return result;
} }
updateParent(list:Person[]):void { updateParent(list:Person[]):void {
let i = 0; let i = 0;
let item:Person; let item:Person;
for (i = 0; i< list.length; i++) for (i = 0; i< list.length; i++)
{ {
item = list[i]; item = list[i];
if (item.fatherId && item.fatherId > 0) if (item.fatherId && item.fatherId > 0)
{ {
item.fatherName = this.getName(item.fatherId); item.fatherName = this.getName(item.fatherId);
} }
} }
} }
newFamily():void { newFamily():void {
//console.log("add new employee"); //console.log("add new employee");
this.personService.parentList = this.familyList; this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } }); // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--); this.showEdit(this._id--);
} }
edit(id: number) : void { edit(id: number) : void {
//console.log("edit family", id); //console.log("edit family", id);
this.personService.parentList = this.familyList; this.personService.parentList = this.familyList;
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } }); // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id); this.showEdit(id);
} }
showEdit(id:number) { showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, { const ref = this.dialogService.open(PersonEdit, {
data: { data: {
id, id,
familyList: this.familyList, familyList: this.familyList,
}, },
header: 'Family', header: 'Family',
width: '80%', width: '80%',
maximizable: true maximizable: true
}); });
if (ref) if (ref)
{ {
ref.onClose.subscribe((item: Person) => { ref.onClose.subscribe((item: Person) => {
if (item) { if (item) {
//console.log("after close ward edit", item); //console.log("after close ward edit", item);
this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list //update the current list
this.updateList(item); this.updateList(item);
} }
}); });
} }
} }
updateList(item: Person) :void { updateList(item: Person) :void {
const idx = this.familyList.findIndex( x => x.id == item.id); const idx = this.familyList.findIndex( x => x.id == item.id);
if (item.fatherId && item.fatherId > 0) if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId); item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0) if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId); item.motherName = this.getName(item.motherId);
if (idx < 0) if (idx < 0)
{ {
const olist = [... this.familyList, item]; const olist = [... this.familyList, item];
this.familyList = olist; this.familyList = olist;
} }
else else
{ {
const oitem = this.familyList[idx]; const oitem = this.familyList[idx];
oitem.firstName = item.firstName; oitem.firstName = item.firstName;
oitem.lastName = item.lastName; oitem.lastName = item.lastName;
oitem.address = item.address; oitem.address = item.address;
oitem.alive = item.alive; oitem.alive = item.alive;
oitem.dob = item.dob; oitem.dob = item.dob;
oitem.email = item.email; oitem.email = item.email;
oitem.fatherId = item.fatherId; oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId; oitem.motherId = item.motherId;
oitem.motherName = item.motherName; oitem.motherName = item.motherName;
oitem.fatherName = item.fatherName; oitem.fatherName = item.fatherName;
} }
} }
deleteItem(id: number): void { deleteItem(id: number): void {
this.personService.deletePerson(id) this.personService.deletePerson(id)
.pipe(take(1)) .pipe(take(1))
.subscribe({ next: result => { .subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result); console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList.filter(d => d.id !== id); const nlist = this.familyList.filter(d => d.id !== id);
this.familyList = nlist; this.familyList = nlist;
}, },
error: e => console.error(e) error: e => console.error(e)
}); });
//console.log(this.msg + "click button to delete"); //console.log(this.msg + "click button to delete");
} }
ngOnDestroy() { ngOnDestroy() {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }
+12 -12
View File
@@ -1,12 +1,12 @@
table { table {
width: 100%; width: 100%;
} }
.mat-form-field { .mat-form-field {
font-size: 14px; font-size: 14px;
width: 100%; width: 100%;
} }
td.mat-column-edit, .mat-column-delete { td.mat-column-edit, .mat-column-delete {
width: 35px; width: 35px;
padding-right: 2px; padding-right: 2px;
} }
+86 -86
View File
@@ -1,86 +1,86 @@
<div class="shadow-2xl rounded p-2 mt-2"> <div class="shadow-2xl rounded p-2 mt-2">
<h3>Person List</h3> <h3>Person List</h3>
<div> <div>
<form> <form>
<div class="grid md:grid-cols-4 gap-2"> <div class="grid md:grid-cols-4 gap-2">
<div class=""> <div class="">
<label for="login">Email</label> <label for="login">Email</label>
<input id="login" pInputText name="login" type="text" [(ngModel)]="email" <input id="login" pInputText name="login" type="text" [(ngModel)]="email"
class="inputfield w-full p-inputtext-sm"> class="inputfield w-full p-inputtext-sm">
</div> </div>
<div class=""> <div class="">
<label for="lastname1">Surname</label> <label for="lastname1">Surname</label>
<input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname" <input id="lastname1" pInputText name="lastname" type="text" [(ngModel)]="lastname"
class="inputfield w-full p-inputtext-sm"> class="inputfield w-full p-inputtext-sm">
</div> </div>
<div class=""> <div class="">
<label for="firstname">First Name</label> <label for="firstname">First Name</label>
<input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname" <input id="firstname" name="firstname" pInputText type="text" [(ngModel)]="firstname"
class="inputfield w-full p-inputtext-sm"> class="inputfield w-full p-inputtext-sm">
</div> </div>
<div class="flex items-end"> <div class="flex items-end">
<button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search" <button pButton pRipple class="p-button-sm mr-2 " type="submit" icon="pi pi-search" iconPos="left" label="Search"
(click)="search()"></button> (click)="search()"></button>
<button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" label="New Person" <button pButton pRipple class="p-button-sm " type="button" icon="pi pi-user-plus" label="New Person"
(click)="newFamily()"></button> (click)="newFamily()"></button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div> <div>
<p-table [value]="familyList()" sortMode="multiple" dataKey="id" class="p-datatable-sm" #dt <p-table [value]="familyList()" sortMode="multiple" dataKey="id" class="p-datatable-sm" #dt
class="p-datatable-sm" selectionMode="single" [(selection)]="selectedPerson" class="p-datatable-sm" selectionMode="single" [(selection)]="selectedPerson"
[paginator]="true" rowHover="true" [globalFilterFields]="['lastName', 'firstName', 'email', 'fatherName', 'motherName']" [paginator]="true" rowHover="true" [globalFilterFields]="['lastName', 'firstName', 'email', 'fatherName', 'motherName']"
[rows]="20" [rowsPerPageOptions]="[10,20,50,100]" [rows]="20" [rowsPerPageOptions]="[10,20,50,100]"
[loading]="loading"> [loading]="loading">
<ng-template #caption> <ng-template #caption>
<div class="flex"> <div class="flex">
<p-button icon="pi pi-external-link" label="Export" (click)="exportExport()" <p-button icon="pi pi-external-link" label="Export" (click)="exportExport()"
/> />
<p-iconfield iconPosition="left" class="ml-auto"> <p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon> <p-inputicon>
<i class="pi pi-search"></i> <i class="pi pi-search"></i>
</p-inputicon> </p-inputicon>
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" /> <input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
</p-iconfield> </p-iconfield>
</div> </div>
</ng-template> </ng-template>
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon> <th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
</th> </th>
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon> <th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
</th> </th>
<th pSortableColumn="sex">Sex <p-sortIcon field="sex"></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 pSortableColumn="fatherName">Father<p-sortIcon field="fatherName"></p-sortIcon>
</th> </th>
<th pSortableColumn="motherName">Mother<p-sortIcon field="motherName"></p-sortIcon> <th pSortableColumn="motherName">Mother<p-sortIcon field="motherName"></p-sortIcon>
</th> </th>
<th>Edit</th> <th>Edit</th>
</tr> </tr>
</ng-template> </ng-template>
<ng-template pTemplate="body" let-user> <ng-template pTemplate="body" let-user>
<tr> <tr>
<td>{{user.lastName}}</td> <td>{{user.lastName}}</td>
<td>{{user.firstName}}</td> <td>{{user.firstName}}</td>
<td>{{user.sex}}</td> <td>{{user.sex}}</td>
<td>{{user.fatherName}}</td> <td>{{user.fatherName}}</td>
<td>{{user.motherName}}</td> <td>{{user.motherName}}</td>
<td> <td>
<p-button type="button" icon="pi pi-ellipsis-v" <p-button type="button" icon="pi pi-ellipsis-v"
[rounded]="true" [text]="true" [raised]="true" [rounded]="true" [text]="true" [raised]="true"
class="p-button-rounded p-button-text" class="p-button-rounded p-button-text"
(onClick)="actionClick(user.id,$event)"></p-button> (onClick)="actionClick(user.id,$event)"></p-button>
</td> </td>
</tr> </tr>
</ng-template> </ng-template>
</p-table> </p-table>
<p-menu #rowmenu [model]="items" [popup]="true" (onShow)="onMenuShow()" /> <p-menu #rowmenu [model]="items" [popup]="true" (onShow)="onMenuShow()" />
</div> </div>
+359 -359
View File
@@ -1,359 +1,359 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models'; import { StaffView ,StaffSearch, Person } from '../models';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { Table, TableModule } from 'primeng/table'; import { Table, TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from '@openng/optimus-ui/dynamicdialog';
import { PersonEdit } from './person.edit'; import { PersonEdit } from './person.edit';
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api'; import { ConfirmationService, MenuItem, MessageService } from '@openng/optimus-ui/api';
import { IconFieldModule } from 'primeng/iconfield'; import { IconFieldModule } from '@openng/optimus-ui/iconfield';
import { InputIconModule } from 'primeng/inputicon'; import { InputIconModule } from '@openng/optimus-ui/inputicon';
import { Menu, MenuModule } from 'primeng/menu'; import { Menu, MenuModule } from '@openng/optimus-ui/menu';
import { FamilyOrga } from './family.orga'; import { FamilyOrga } from './family.orga';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx'; import * as XLSX from 'xlsx';
@Component({ @Component({
selector: 'family-list', selector: 'family-list',
templateUrl: './familylist.html', templateUrl: './familylist.html',
imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule, imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule,
InputTextModule,IconFieldModule,InputIconModule], InputTextModule,IconFieldModule,InputIconModule],
styleUrls: ['./familylist.css'], styleUrls: ['./familylist.css'],
providers: [DialogService] providers: [DialogService]
}) })
export class FamilyList implements OnInit, OnDestroy{ export class FamilyList implements OnInit, OnDestroy{
private subscription:Subscription = new Subscription(); private subscription:Subscription = new Subscription();
//private cd = inject(ChangeDetectorRef); //private cd = inject(ChangeDetectorRef);
items: MenuItem[] | undefined; items: MenuItem[] | undefined;
selectedPerson!: Person; selectedPerson!: Person;
firstname = ''; firstname = '';
email = ''; email = '';
lastname = ''; lastname = '';
_id = -10; _id = -10;
selectId = -1; selectId = -1;
loading = false; loading = false;
familyList = signal<Person[]>([]); familyList = signal<Person[]>([]);
msg ="[Person component]"; msg ="[Person component]";
@ViewChild(Table) dt2!: Table; @ViewChild(Table) dt2!: Table;
@ViewChild('rowmenu') popMenu?: Menu; @ViewChild('rowmenu') popMenu?: Menu;
private messageService = inject(MessageService); private messageService = inject(MessageService);
public dialogService= inject( DialogService); public dialogService= inject( DialogService);
private personService= inject( PersonService); private personService= inject( PersonService);
private confirmationService = inject(ConfirmationService); private confirmationService = inject(ConfirmationService);
private cdr = inject(ChangeDetectorRef); private cdr = inject(ChangeDetectorRef);
private authenticationService= inject( AuthenticationService); private authenticationService= inject( AuthenticationService);
private router= inject( Router); private router= inject( Router);
getSearchCiteria(): StaffSearch { getSearchCiteria(): StaffSearch {
let criteria:StaffSearch = { let criteria:StaffSearch = {
email: this.email, email: this.email,
firstName: this.firstname, firstName: this.firstname,
lastName: this.lastname, lastName: this.lastname,
}; };
console.log("get search citeria", criteria); console.log("get search citeria", criteria);
return criteria; return criteria;
} }
initMenu(): void { initMenu(): void {
this.items = [ this.items = [
{ {
label: 'Edit', label: 'Edit',
icon: 'pi pi-pencil', icon: 'pi pi-pencil',
command: () => { command: () => {
this.edit(this.selectId); this.edit(this.selectId);
} }
}, },
{ {
label: 'Delete', label: 'Delete',
iconClass:'text-red-500', iconClass:'text-red-500',
styleClass:'text-red-500', styleClass:'text-red-500',
icon: 'pi pi-times', icon: 'pi pi-times',
command: () => { command: () => {
this.delete(this.selectId); this.delete(this.selectId);
} }
}, },
{ {
label: 'Show Family', label: 'Show Family',
icon: 'pi pi-sitemap', icon: 'pi pi-sitemap',
command: () => { command: () => {
this.showChildren(this.selectId); this.showChildren(this.selectId);
} }
} }
]; ];
} }
exportExport() : void { exportExport() : void {
this.ExcelExport(this.familyList(), 'family_export'); this.ExcelExport(this.familyList(), 'family_export');
} }
ExcelExport(data: any, fileName:string) :void ExcelExport(data: any, fileName:string) :void
{ {
const worksheet = XLSX.utils.json_to_sheet(data); const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new(); const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([excelBuffer], { type: 'application/octet-stream' }); const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
saveAs(blob, `${fileName}.xlsx`); saveAs(blob, `${fileName}.xlsx`);
} }
actionClick(id: number, event:Event): void { actionClick(id: number, event:Event): void {
// console.log("action edit "+ id); // console.log("action edit "+ id);
this.selectId = id; this.selectId = id;
this.popMenu!.toggle(event); this.popMenu!.toggle(event);
} }
showChildren(id: number): void { showChildren(id: number): void {
console.log("show children of id", id); console.log("show children of id", id);
this.showOrganise(id); this.showOrganise(id);
} }
handleInput(event: Event) { handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value; const value = (event.target as HTMLInputElement).value;
this.dt2.filterGlobal(value, 'contains'); this.dt2.filterGlobal(value, 'contains');
} }
canSearch():boolean { canSearch():boolean {
let result = false; let result = false;
result = this.email !== ""; result = this.email !== "";
result = result || this.lastname !== ""; result = result || this.lastname !== "";
result = result || this.firstname !== ""; result = result || this.firstname !== "";
return result; return result;
} }
onMenuShow(): void { onMenuShow(): void {
console.log("this is show", this.selectedPerson); console.log("this is show", this.selectedPerson);
} }
ngOnInit(): void ngOnInit(): void
{ {
this.initMenu(); this.initMenu();
this.authenticationService.isHome = false; this.authenticationService.isHome = false;
this.authenticationService.isReport = false; this.authenticationService.isReport = false;
const prev = this.personService.searchCriteria; const prev = this.personService.searchCriteria;
let goload = true; let goload = true;
if (prev.lastName !== '') if (prev.lastName !== '')
{ {
this.lastname = prev.lastName; this.lastname = prev.lastName;
goload = true; goload = true;
} }
if (prev.firstName !== '') if (prev.firstName !== '')
{ {
this.firstname = prev.firstName; this.firstname = prev.firstName;
goload = true; goload = true;
} }
if (prev.email !== '') if (prev.email !== '')
{ {
this.email = prev.email; this.email = prev.email;
goload = true; goload = true;
} }
if (goload) if (goload)
{ {
this.search(); this.search();
} }
} }
getActive(active:boolean):string { getActive(active:boolean):string {
let result = 'false-icon pi-times-circle'; let result = 'false-icon pi-times-circle';
if (active) if (active)
result = 'true-icon pi-check-circle'; result = 'true-icon pi-check-circle';
return result; return result;
} }
search():void { search():void {
const canSearch = true; // this.canSearch(); const canSearch = true; // this.canSearch();
if (canSearch) if (canSearch)
{ {
this.loading = true; this.loading = true;
const criteria = this.getSearchCiteria(); const criteria = this.getSearchCiteria();
this.personService.searchCriteria = criteria; this.personService.searchCriteria = criteria;
this.subscription.add( this.subscription.add(
this.personService.searchPersons(criteria).subscribe( { this.personService.searchPersons(criteria).subscribe( {
next: result => { next: result => {
// console.log(this.msg + "search load Data", result); // console.log(this.msg + "search load Data", result);
const familyList = result.data; const familyList = result.data;
this.familyList.set(familyList); this.familyList.set(familyList);
this.updateParent( this.familyList()); this.updateParent( this.familyList());
//this.familyList.set(familyList); //this.familyList.set(familyList);
console.log("the person from load", this.familyList()); console.log("the person from load", this.familyList());
this.loading = false; this.loading = false;
this.cdr.detectChanges(); this.cdr.detectChanges();
}, },
error: e => { error: e => {
const message = e || e.message; const message = e || e.message;
// this.toastr.error(message); // this.toastr.error(message);
this.loading = false; this.loading = false;
console.log("error ", e); console.log("error ", e);
} }
}) })
); );
} }
} }
getName(id:number): string getName(id:number): string
{ {
let result =""; let result ="";
const item = this.familyList().find(x => x.id == id); const item = this.familyList().find(x => x.id == id);
if (item) if (item)
result = item.lastName + " " + item.firstName; result = item.lastName + " " + item.firstName;
return result; return result;
} }
updateParent(list:Person[]):void { updateParent(list:Person[]):void {
let i = 0; let i = 0;
let item:Person; let item:Person;
for (i = 0; i< list.length; i++) for (i = 0; i< list.length; i++)
{ {
item = list[i]; item = list[i];
if (item.fatherId && item.fatherId > 0) if (item.fatherId && item.fatherId > 0)
{ {
item.fatherName = this.getName(item.fatherId); item.fatherName = this.getName(item.fatherId);
} }
if (item.motherId && item.motherId > 0) if (item.motherId && item.motherId > 0)
{ {
item.motherName = this.getName(item.motherId); item.motherName = this.getName(item.motherId);
} }
} }
} }
delete(id: number): void { delete(id: number): void {
this.confirmationService.confirm({ this.confirmationService.confirm({
message: 'Do you want to delete this record?', message: 'Do you want to delete this record?',
header: 'Confirmation Delete', header: 'Confirmation Delete',
icon: 'pi pi-info-circle', icon: 'pi pi-info-circle',
rejectLabel: 'Cancel', rejectLabel: 'Cancel',
rejectButtonProps: { rejectButtonProps: {
label: 'Cancel', label: 'Cancel',
severity: 'secondary', severity: 'secondary',
outlined: true, outlined: true,
}, },
acceptButtonProps: { acceptButtonProps: {
label: 'Delete', label: 'Delete',
severity: 'danger', severity: 'danger',
}, },
accept: () => { accept: () => {
this.deleteItem(id); this.deleteItem(id);
} }
}); });
} }
deleteItem(id: number): void { deleteItem(id: number): void {
this.personService.deletePerson(id) this.personService.deletePerson(id)
.pipe(take(1)) .pipe(take(1))
.subscribe({ next: result => { .subscribe({ next: result => {
console.log(this.msg + " deleteItem success", result); console.log(this.msg + " deleteItem success", result);
const nlist = this.familyList().filter(d => d.id !== id); const nlist = this.familyList().filter(d => d.id !== id);
this.familyList.set(nlist); this.familyList.set(nlist);
this.cdr.detectChanges(); this.cdr.detectChanges();
}, },
error: e => console.error(e) error: e => console.error(e)
}); });
//console.log(this.msg + "click button to delete"); //console.log(this.msg + "click button to delete");
} }
newFamily():void { newFamily():void {
//console.log("add new employee"); //console.log("add new employee");
this.personService.parentList = this.familyList(); this.personService.parentList = this.familyList();
// this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } }); // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
this.showEdit(this._id--); this.showEdit(this._id--);
} }
edit(id: number) : void { edit(id: number) : void {
//console.log("edit family", id); //console.log("edit family", id);
this.personService.parentList = this.familyList(); this.personService.parentList = this.familyList();
// this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } }); // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
this.showEdit(id); this.showEdit(id);
} }
showEdit(id:number) { showEdit(id:number) {
const ref = this.dialogService.open(PersonEdit, { const ref = this.dialogService.open(PersonEdit, {
data: { data: {
id, id,
familyList: this.familyList(), familyList: this.familyList(),
}, },
header: 'Person', header: 'Person',
width: '80%', width: '80%',
draggable: true, draggable: true,
maximizable: true maximizable: true
}); });
if (ref) if (ref)
{ {
ref.onClose.subscribe((item: Person) => { ref.onClose.subscribe((item: Person) => {
if (item) { if (item) {
//console.log("after close ward edit", item); //console.log("after close ward edit", item);
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list //update the current list
this.updateList(item); this.updateList(item);
} }
}); });
} }
} }
showOrganise(id:number) { showOrganise(id:number) {
const ref = this.dialogService.open(FamilyOrga, { const ref = this.dialogService.open(FamilyOrga, {
data: { data: {
id, id,
familyList: this.familyList(), familyList: this.familyList(),
}, },
header: 'Family Tree and Children', header: 'Family Tree and Children',
width: '80%', width: '80%',
maximizable: true, maximizable: true,
closable: true closable: true
}); });
if (ref) if (ref)
{ {
ref.onClose.subscribe((item: Person) => { ref.onClose.subscribe((item: Person) => {
if (item) { if (item) {
//console.log("after close ward edit", item); //console.log("after close ward edit", item);
// this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
//update the current list //update the current list
//this.updateList(item); //this.updateList(item);
} }
}); });
} }
} }
updateList(item: Person) :void { updateList(item: Person) :void {
const list = this.familyList(); const list = this.familyList();
const idx = list.findIndex( x => x.id === item.id); const idx = list.findIndex( x => x.id === item.id);
if (item.fatherId && item.fatherId > 0) if (item.fatherId && item.fatherId > 0)
item.fatherName = this.getName(item.fatherId); item.fatherName = this.getName(item.fatherId);
if (item.motherId && item.motherId > 0) if (item.motherId && item.motherId > 0)
item.motherName = this.getName(item.motherId); item.motherName = this.getName(item.motherId);
if (idx < 0) if (idx < 0)
{ {
const olist = [... list, item]; const olist = [... list, item];
this.familyList.set(olist); this.familyList.set(olist);
} }
else else
{ {
const oitem = list[idx]; const oitem = list[idx];
oitem.firstName = item.firstName; oitem.firstName = item.firstName;
oitem.lastName = item.lastName; oitem.lastName = item.lastName;
oitem.address = item.address; oitem.address = item.address;
oitem.alive = item.alive; oitem.alive = item.alive;
oitem.dob = item.dob; oitem.dob = item.dob;
oitem.sex = item.sex; oitem.sex = item.sex;
oitem.email = item.email; oitem.email = item.email;
oitem.fatherId = item.fatherId; oitem.fatherId = item.fatherId;
oitem.motherId = item.motherId; oitem.motherId = item.motherId;
oitem.fatherName = item.fatherName; oitem.fatherName = item.fatherName;
oitem.motherName = item.motherName; oitem.motherName = item.motherName;
this.familyList.set(list); this.familyList.set(list);
} }
this.cdr.markForCheck(); this.cdr.markForCheck();
} }
ngOnDestroy() { ngOnDestroy() {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }
+5 -5
View File
@@ -1,5 +1,5 @@
export * from './familylist'; export * from './familylist';
export * from './person.edit'; export * from './person.edit';
export * from './family.tree'; export * from './family.tree';
export * from './person.service'; export * from './person.service';
+12 -12
View File
@@ -1,13 +1,13 @@
.profilePhotoBorder .profilePhotoBorder
{ {
border-color: gray; border-color: gray;
border-width: 2px; border-width: 2px;
border-radius: 15%; border-radius: 15%;
} }
.profilePhotoWH .profilePhotoWH
{ {
width: 150px; width: 150px;
height: 100px; height: 100px;
} }
+133 -133
View File
@@ -1,134 +1,134 @@
<div class=" mt-2"> <div class=" mt-2">
<form [formGroup]="adminuserForm" (ngSubmit)="onSubmit($event)" > <form [formGroup]="adminuserForm" (ngSubmit)="onSubmit($event)" >
<div class="ml-2 grid md:grid-cols-2 gap-3 p-2"> <div class="ml-2 grid md:grid-cols-2 gap-3 p-2">
<div> <div>
<div class=""> <div class="">
<label for="lastname1">Surname<strong class="app-require">*</strong></label> <label for="lastname1">Surname<strong class="app-require">*</strong></label>
<input id="lastname1" pInputText formControlName="lastname" type="text" [pAutoFocus]="true" <input id="lastname1" pInputText formControlName="lastname" type="text" [pAutoFocus]="true"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')"> [class]="getClassForRequire('inputfield w-full p-inputtext-sm','lastname')">
</div> </div>
<div class=""> <div class="">
<label for="firstname">First Name<strong class="app-require">*</strong></label> <label for="firstname">First Name<strong class="app-require">*</strong></label>
<input id="firstname" pInputText formControlName="firstname" type="text" <input id="firstname" pInputText formControlName="firstname" type="text"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')"> [class]="getClassForRequire('inputfield w-full p-inputtext-sm','firstname')">
</div> </div>
</div> </div>
<!--put here photo of person--> <!--put here photo of person-->
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<div> <div>
<div class=""> <div class="">
<label for="dob" class="flex w-full">DOB</label> <label for="dob" class="flex w-full">DOB</label>
<p-datepicker ariaLabelledBy="dob" formControlName="dob" [iconDisplay]="'input'" [showIcon]="true" <p-datepicker ariaLabelledBy="dob" formControlName="dob" [iconDisplay]="'input'" [showIcon]="true"
dateFormat="dd/mm/yy" ></p-datepicker> dateFormat="dd/mm/yy" ></p-datepicker>
</div> </div>
<div class=""> <div class="">
<label class="flex w-full">Sex <strong class="app-require">*</strong></label> <label class="flex w-full">Sex <strong class="app-require">*</strong></label>
<p-select [options]="sexList" optionLabel="name" optionValue="status" placeholder="Select Gender" <p-select [options]="sexList" optionLabel="name" optionValue="status" placeholder="Select Gender"
formControlName="sex" formControlName="sex"
[class]="getClassForRequire('inputfield w-full p-inputtext-sm','sex')" [class]="getClassForRequire('inputfield w-full p-inputtext-sm','sex')"
class="w-full p-inputtext-sm mr-1"></p-select > class="w-full p-inputtext-sm mr-1"></p-select >
</div> </div>
</div> </div>
<div class="flex flex-row shadow-md rounded-xl border-gray-100 border-1 p-4"> <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 href="{{hostsite}}/{{adminuserForm.value.image}}" target="_blank" class="text-blue-400">View Attachment
</a--> </a-->
<input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload> <input type="file" class="file-input hidden" (change)="onFileSelected($event)" #fileUpload>
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null) @if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
{ {
<img id="blah" [src]= "dislayImage()" alt="your image" <img id="blah" [src]= "dislayImage()" alt="your image"
class="profilePhotoWH profilePhotoBorder" (click)="doViewImage(adminuserForm.value.image)" /> class="profilePhotoWH profilePhotoBorder" (click)="doViewImage(adminuserForm.value.image)" />
} }
@else @else
{ {
<div class="file-upload profilePhotoBorder profilePhotoWH text-center flex justify-center items-center "> <div class="file-upload profilePhotoBorder profilePhotoWH text-center flex justify-center items-center ">
<label class="">{{ adminuserForm.value.image || "No Profile Photo."}}</label> <label class="">{{ adminuserForm.value.image || "No Profile Photo."}}</label>
</div> </div>
} }
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
@if (adminuserForm.value.image != "" && adminuserForm.value.image != null) @if (adminuserForm.value.image != "" && adminuserForm.value.image != null)
{ {
<button pButton type="button" icon="pi pi-times" pTooltip="remove profile photo" <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" class="p-button-rounded p-button-text text-red-500 p-button-raised ml-2"
(click)="deleteFile()"></button> (click)="deleteFile()"></button>
} }
<button pButton type="button" icon="pi pi-paperclip" <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" class="self-end p-button-rounded p-button-text p-button-raised ml-2" pTooltip="load photo"
(click)="fileUpload.click()"></button> (click)="fileUpload.click()"></button>
</div> </div>
</div> </div>
</div> </div>
<!----> <!---->
<div class=""> <div class="">
<label for="login">Email</label> <label for="login">Email</label>
<input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email" <input id="login" [attr.disabled]="!isNew?true:null" pInputText formControlName="email"
type="text" class="inputfield w-full"> type="text" class="inputfield w-full">
</div> </div>
<div class=""> <div class="">
<label for="phone" class="w-full">Phone</label> <label for="phone" class="w-full">Phone</label>
<input class="w-full" id="phone" pInputText formControlName="phone" type="text" > <input class="w-full" id="phone" pInputText formControlName="phone" type="text" >
</div> </div>
<div class=""> <div class="">
<label class="flex w-full">Father</label> <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" <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 > formControlName="fatherId" class="w-full p-inputtext-sm mr-1"></p-select >
</div> </div>
<div class=""> <div class="">
<label class="flex w-full">Mother</label> <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" <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 > formControlName="motherId" class="w-full p-inputtext-sm mr-1"></p-select >
</div> </div>
<div class="grid align-end"> <div class="grid align-end">
<div class="p-field-checkbox"> <div class="p-field-checkbox">
<p-checkbox inputId="alive" formControlName="alive" [binary]="true"></p-checkbox> <p-checkbox inputId="alive" formControlName="alive" [binary]="true"></p-checkbox>
<label for="alive" class="ml-2 w-full">Alive</label> <label for="alive" class="ml-2 w-full">Alive</label>
</div> </div>
</div> </div>
</div> </div>
<div class="mt-2 w-full"> <div class="mt-2 w-full">
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between">
<p-button icon="pi pi-images" [raised]="true" <p-button icon="pi pi-images" [raised]="true"
badge="{{photoList.length}}" badge="{{photoList.length}}"
severity="success" label="Attach Photos" severity="success" label="Attach Photos"
(onClick)="viewAttachment()"/> (onClick)="viewAttachment()"/>
<p-button icon="pi pi-user" [raised]="true" severity="info" label="Add Partner" <p-button icon="pi pi-user" [raised]="true" severity="info" label="Add Partner"
(onClick)="addPartner()"/> (onClick)="addPartner()"/>
</div> </div>
<div class="shadow rounded mt-2 mb-2"> <div class="shadow rounded mt-2 mb-2">
<p-table [value]="partners()"> <p-table [value]="partners()">
<ng-template #header> <ng-template #header>
<tr> <tr>
<th>First Name</th> <th>First Name</th>
<th>Last Name</th> <th>Last Name</th>
<th>Sex</th> <th>Sex</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
</ng-template> </ng-template>
<ng-template #body let-user> <ng-template #body let-user>
<tr> <tr>
<td>{{ user.pfirstName }}</td> <td>{{ user.pfirstName }}</td>
<td>{{ user.plastName }}</td> <td>{{ user.plastName }}</td>
<td>{{ user.sex }}</td> <td>{{ user.sex }}</td>
<td> <td>
<button pButton type="button" icon="pi pi-times" class="p-button-rounded p-button-text p-button-danger" <button pButton type="button" icon="pi pi-times" class="p-button-rounded p-button-text p-button-danger"
(click)="onDeletePartner(user)"></button> (click)="onDeletePartner(user)"></button>
</td> </td>
</tr> </tr>
</ng-template> </ng-template>
</p-table> </p-table>
</div> </div>
</div> </div>
<div class="flex justify-end mr-2 mb-2 p-2"> <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 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> <button pButton pRipple class="p-button-sm p-button-secondary" type="button" icon="pi pi-times" label="Cancel" (click)="cancel($event)"></button>
</div> </div>
</form> </form>
<!--pre>{{ adminuserForm.value|json}}</pre--> <!--pre>{{ adminuserForm.value|json}}</pre-->
</div> </div>
File diff suppressed because it is too large Load Diff
+136 -109
View File
@@ -1,109 +1,136 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models'; import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
import { AppSettingService } from '../shares'; import { AppSettingService } from '../shares';
import { TreeNode } from 'primeng/api'; import { TreeNode } from '@openng/optimus-ui/api';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class PersonService { export class PersonService {
public searchCriteria: StaffSearch; public searchCriteria: StaffSearch;
public parentList: Person[] =[]; public parentList: Person[] =[];
constructor(private http: HttpClient, constructor(private http: HttpClient,
private appSetting :AppSettingService private appSetting :AppSettingService
) { ) {
this.searchCriteria = { this.searchCriteria = {
email:'', email:'',
firstName: '', firstName: '',
lastName:'' lastName:''
}; };
} }
searchPersons(criteria: StaffSearch): Observable<ResultModel<Person[]>> { searchPersons(criteria: StaffSearch): Observable<ResultModel<Person[]>> {
let config = { headers : { 'Content-Type': 'application/json' } }; let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson";
return this.http.post<ResultModel<Person[]>>(baseUrl, criteria, config); return this.http.post<ResultModel<Person[]>>(baseUrl, criteria, config);
} }
loadPersonById(id:number): Observable<ResultModel<Person>> { loadPersonById(id:number): Observable<ResultModel<Person>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
/* /*
const params = new HttpParams().set("id", ""+id); const params = new HttpParams().set("id", ""+id);
const headers = new HttpHeaders().set('Content-Type', 'application/json'); const headers = new HttpHeaders().set('Content-Type', 'application/json');
const options = { const options = {
headers: headers, headers: headers,
params: params params: params
}; };
*/ */
return this.http.get<ResultModel<Person>>(baseUrl + "/GetById/" + id); return this.http.get<ResultModel<Person>>(baseUrl + "/GetById/" + id);
} }
loadPersonFamily(id: number): Observable<ResultModel<TreeNode>> { loadPersonFamily(id: number): Observable<ResultModel<TreeNode>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
return this.http.get<ResultModel<TreeNode>>(baseUrl + "/GetByPersonFamily/" + id); return this.http.get<ResultModel<TreeNode>>(baseUrl + "/GetByPersonFamily/" + id);
} }
loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable<ResultModel<TreeNode[]>> { loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable<ResultModel<TreeNode[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {useFather,useMother}; const data = {useFather,useMother};
return this.http.post<ResultModel<TreeNode[]>>(baseUrl + "/GetFamilyTreeBy", data); return this.http.post<ResultModel<TreeNode[]>>(baseUrl + "/GetFamilyTreeBy", data);
} }
loadChildrenById(fatherId:number, motherId: number): Observable<ResultModel<Person[]>> { loadChildrenById(fatherId:number, motherId: number): Observable<ResultModel<Person[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {fatherId,motherId}; const data = {fatherId,motherId};
return this.http.post<ResultModel<Person[]>>(baseUrl + "/GetChildress/" ,data); return this.http.post<ResultModel<Person[]>>(baseUrl + "/GetChildress/" ,data);
} }
loadRelationshipById(personId:number): Observable<ResultModel<RelationShip[]>> { loadRelationshipById(personId:number): Observable<ResultModel<RelationShip[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl;
return this.http.get<ResultModel<RelationShip[]>>(baseUrl + "/GetByPersonId/" +personId); return this.http.get<ResultModel<RelationShip[]>>(baseUrl + "/GetByPersonId/" +personId);
} }
savePerson(data:PersonContainer): Observable<ResultModel<number>> { //insert Adminuser savePerson(data:PersonContainer): Observable<ResultModel<number>> { //insert Adminuser
let config = { headers : { 'Content-Type': 'application/json' } }; let config = { headers : { 'Content-Type': 'application/json' } };
console.log("save family", data); console.log("save family", data);
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson";
return this.http.post<ResultModel<number>>(baseUrl, data, config); return this.http.post<ResultModel<number>>(baseUrl, data, config);
} }
deletePerson(id:number): Observable<ResultModel<number>>{ deletePerson(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
const data = {id}; const data = {id};
return this.http.post<ResultModel<number>>(baseUrl + "/DeleteById" ,data); return this.http.post<ResultModel<number>>(baseUrl + "/DeleteById" ,data);
} }
uploadFile(data: any): Observable<ResultModel<string>> { uploadFile(data: any): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage"; const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
return this.http.post<ResultModel<string>>(baseUrl, data); return this.http.post<ResultModel<string>>(baseUrl, data);
} }
downloadFile(imageName: string): Observable<ResultModel<string>> { downloadFile_dontusethis(imageName: string, personId: number,
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile"; photoType: number): Observable<ResultModel<string>> {
const data = { const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile";
fileName: imageName const data = {
}; fileName: imageName,
return this.http.post<ResultModel<string>>(baseUrl, data); id: personId,
} photoType
deleteUploadFile(data: any): Observable<ResultModel<number>> { };
//data ={filename}; return this.http.post<ResultModel<string>>(baseUrl, data);
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile"; }
return this.http.post<ResultModel<number>>(baseUrl, data); deleteUploadFile_dotnotuse(data: any): Observable<ResultModel<number>> {
} //data ={filename};
deletePersonPhotoFile(id: number): Observable<ResultModel<number>> { const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
const data ={id, fileName:''}; return this.http.post<ResultModel<number>>(baseUrl, data);
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto"; }
return this.http.post<ResultModel<number>>(baseUrl, data); deletePersonPhotoFile(personId: number, photoType: number): Observable<ResultModel<number>> {
} const data = {
savePersonPhotoList(data:FormData): Observable<ResultModel<number>> { id: personId,
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto"; fileName: '',
return this.http.post<ResultModel<number>>(baseUrl, data); photoType
} };
downloadPersonPhoto(id: number): Observable<ResultModel<string>> { const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto";
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto"; return this.http.post<ResultModel<number>>(baseUrl, data);
const data = { }
id, deletePersonPhotoFileById(id: number): Observable<ResultModel<number>> {
fileName: '' const data = {
}; id
return this.http.post<ResultModel<string>>(baseUrl, data); };
} const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhotoById";
} 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);
}
downloadPersonPhoto64(imageName: string, personId: number,
photoType: number): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto64";
const data = {
id: personId,
photoType,
fileName: imageName
};
return this.http.post<ResultModel<string>>(baseUrl, data);
}
downloadPersonPhoto(imageName: string, personId: number,
photoType: number): Observable<any> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto";
const data = {
id: personId,
photoType,
fileName: imageName
};
return this.http.post<any>(baseUrl, data);
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
<img [src]="imageDataUrl()" alt="Person Image" width="400" height="300"/> <img [src]="imageDataUrl()" alt="Person Image" width="400" height="300"/>
<div class="flex justify-end"> <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" <button pButton pRipple class="p-button-sm mr-2 p-button-secondary" type="button" icon="pi pi-sign-in" label="Close"
(click)="close()"></button> (click)="close()"></button>
</div> </div>
+65 -63
View File
@@ -1,64 +1,66 @@
import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core'; import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { AppSettingService } from '../shares'; import { AppSettingService } from '../shares';
import { PersonService } from '../person'; import { PersonService } from '../person';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DynamicDialogConfig, DynamicDialogRef } from '@openng/optimus-ui/dynamicdialog';
@Component({ @Component({
selector: 'app-image-display', selector: 'app-image-display',
imports:[CommonModule, ButtonModule], imports:[CommonModule, ButtonModule],
templateUrl: './image.display.html', templateUrl: './image.display.html',
styleUrls: ['./image.display.css'] styleUrls: ['./image.display.css']
}) })
export class ImageDisplayComponent implements OnInit, OnDestroy { export class ImageDisplayComponent implements OnInit, OnDestroy {
//base64ImageString: string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // Example Base64 (a tiny red dot PNG) //base64ImageString: string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // Example Base64 (a tiny red dot PNG)
//imageDataUrl: SafeResourceUrl ={}; //imageDataUrl: SafeResourceUrl ={};
imageDataUrl = signal<string>(""); imageDataUrl = signal<string>("");
private personService = inject(PersonService); private personService = inject(PersonService);
private sanitizer = inject(DomSanitizer); private sanitizer = inject(DomSanitizer);
//private route = inject(ActivatedRoute); //private route = inject(ActivatedRoute);
public ref = inject(DynamicDialogRef); public ref = inject(DynamicDialogRef);
public config = inject(DynamicDialogConfig); public config = inject(DynamicDialogConfig);
private subscription:Subscription = new Subscription(); private subscription:Subscription = new Subscription();
ngOnInit(): void { ngOnInit(): void {
const imageName = this.config.data.imageName; const imageName = this.config.data.imageName;
this.loadImage(imageName); const personId = this.config.data.personId;
const photoType = this.config.data.photyType;
} this.loadImage(imageName, personId, photoType);
loadImage(fileName: string| null): void {
const download = this.personService.downloadFile(fileName!); }
this.subscription.add(download.subscribe({ loadImage(fileName: string | null, personId: number, photoType: number): void {
next: x => { const download = this.personService.downloadPersonPhoto64(fileName!, personId, photoType);
if (x.statusCode == 1) this.subscription.add(download.subscribe({
{ next: x => {
this.display(x.data); if (x.statusCode == 1)
console.log("this is show image" , this.imageDataUrl(), x); {
} this.display(x.data);
else console.log("this is show image" , this.imageDataUrl(), x);
{ }
console.log("error in download in api ", x.message); else
} {
}, console.log("error in download in api ", x.message);
error: e => console.error("error in download image", e) }
})); },
} error: e => console.error("error in download image", e)
display(baseImage: string): void }));
{ }
const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity; display(baseImage: string): void
console.log('this is bypassSecurityTrustResourceUrl', changeUrl); {
const fullDataUri = `data:image/png;base64,${changeUrl}`; const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity;
this.imageDataUrl.set(fullDataUri); console.log('this is bypassSecurityTrustResourceUrl', changeUrl);
} const fullDataUri = `data:image/png;base64,${changeUrl}`;
ngOnDestroy(): void { this.imageDataUrl.set(fullDataUri);
this.subscription.unsubscribe(); }
} ngOnDestroy(): void {
close() :void { this.subscription.unsubscribe();
this.ref.close(null); }
} close() :void {
this.ref.close(null);
}
} }
+47 -47
View File
@@ -1,47 +1,47 @@
<div class="flex-row w-full h-full"> <div class="flex-row w-full h-full">
<p-table #dt2 [value]="familyList()" sortMode="multiple" <p-table #dt2 [value]="familyList()" sortMode="multiple"
class="p-datatable-sm" paginatorDropdownAppendTo="body" class="p-datatable-sm" paginatorDropdownAppendTo="body"
dataKey="id" selectionMode="single" [(selection)]="selectedPerson" dataKey="id" selectionMode="single" [(selection)]="selectedPerson"
[paginator]="true" [globalFilterFields]="['lastName', 'firstName', 'sex']" [paginator]="true" [globalFilterFields]="['lastName', 'firstName', 'sex']"
[rows]="10" [rowsPerPageOptions]="[5,10, 20,50]" [rows]="10" [rowsPerPageOptions]="[5,10, 20,50]"
[loading]="loading"> [loading]="loading">
<ng-template #caption> <ng-template #caption>
<div class="flex"> <div class="flex">
<p-iconfield iconPosition="left" class="ml-auto"> <p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon> <p-inputicon>
<i class="pi pi-search"></i> <i class="pi pi-search"></i>
</p-inputicon> </p-inputicon>
<input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" /> <input pInputText type="text" (input)="handleInput($event)" placeholder="Search keyword" />
</p-iconfield> </p-iconfield>
</div> </div>
</ng-template> </ng-template>
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon> <th pSortableColumn="lastName">Last Name <p-sortIcon field="lastName"></p-sortIcon>
</th> </th>
<th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon> <th pSortableColumn="firstName">First Name <p-sortIcon field="firstName"></p-sortIcon>
</th> </th>
<th pSortableColumn="sex">Sex<p-sortIcon field="sex"></p-sortIcon> <th pSortableColumn="sex">Sex<p-sortIcon field="sex"></p-sortIcon>
</th> </th>
<th></th> <th></th>
</tr> </tr>
</ng-template> </ng-template>
<ng-template pTemplate="body" let-user> <ng-template pTemplate="body" let-user>
<tr [pSelectableRow]="user"> <tr [pSelectableRow]="user">
<td>{{user.lastName}}</td> <td>{{user.lastName}}</td>
<td>{{user.firstName}}</td> <td>{{user.firstName}}</td>
<td>{{user.sex}}</td> <td>{{user.sex}}</td>
<td></td> <td></td>
</tr> </tr>
</ng-template> </ng-template>
</p-table> </p-table>
<div class="flex justify-end mr-2 mb-2 p-2"> <div class="flex justify-end mr-2 mb-2 p-2">
<button pButton type="submit" class="p-button-sm mr-2" <button pButton type="submit" class="p-button-sm mr-2"
icon="pi pi-check" iconPos="left" label="Select" icon="pi pi-check" iconPos="left" label="Select"
(click)="select($event)" [disabled]="selectedPerson == null" ></button> (click)="select($event)" [disabled]="selectedPerson == null" ></button>
<button pButton pRipple class="p-button-sm" type="button" <button pButton pRipple class="p-button-sm" type="button"
icon="pi pi-times" iconPos="left" icon="pi pi-times" iconPos="left"
label="Cancel" (click)="cancel($event)"></button> label="Cancel" (click)="cancel($event)"></button>
</div> </div>
</div> </div>
+53 -53
View File
@@ -1,53 +1,53 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Table, TableModule } from 'primeng/table'; import { Table, TableModule } from '@openng/optimus-ui/table';
import { Person } from '../models'; import { Person } from '../models';
import { IconFieldModule } from 'primeng/iconfield'; import { IconFieldModule } from '@openng/optimus-ui/iconfield';
import { InputIconModule } from 'primeng/inputicon'; import { InputIconModule } from '@openng/optimus-ui/inputicon';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DynamicDialogConfig, DynamicDialogRef } from '@openng/optimus-ui/dynamicdialog';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
@Component({ @Component({
selector: 'app-pickperson', selector: 'app-pickperson',
imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule], imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule],
templateUrl: './pickperson.html', templateUrl: './pickperson.html',
styleUrl: './pickperson.css' styleUrl: './pickperson.css'
}) })
export class Pickperson implements OnInit, OnDestroy{ export class Pickperson implements OnInit, OnDestroy{
@ViewChild(Table) dt2!: Table; @ViewChild(Table) dt2!: Table;
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig) constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig)
{ {
} }
loading = false; loading = false;
familyList = signal<Person[]>([]); familyList = signal<Person[]>([]);
selectedPerson!: Person; selectedPerson!: Person;
handleInput(event: Event) { handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value; const value = (event.target as HTMLInputElement).value;
this.dt2.filterGlobal(value, 'contains'); this.dt2.filterGlobal(value, 'contains');
} }
edit(id: number): void { edit(id: number): void {
} }
cancel(e:Event):void { cancel(e:Event):void {
e.preventDefault(); e.preventDefault();
this.ref.close(null); this.ref.close(null);
} }
select(e:Event):void { select(e:Event):void {
e.preventDefault(); e.preventDefault();
this.ref.close(this.selectedPerson); this.ref.close(this.selectedPerson);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
} }
ngOnInit(): void { ngOnInit(): void {
console.log("pick person the familyList", this.config); console.log("pick person the familyList", this.config);
this.familyList.set(this.config.data.familyList); this.familyList.set(this.config.data.familyList);
} }
} }
+52 -52
View File
@@ -1,52 +1,52 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Utils } from '../shares'; import { Utils } from '../shares';
import {AuthenticationService} from '../user-services/authentication.service'; import {AuthenticationService} from '../user-services/authentication.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthGuard { export class AuthGuard {
msg ="[AuthGuard] "; msg ="[AuthGuard] ";
constructor(private router: Router, constructor(private router: Router,
private authService :AuthenticationService ) { } private authService :AuthenticationService ) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const roleAllowed = route.data['roleAllowed']; const roleAllowed = route.data['roleAllowed'];
const user = Utils.getCurrentUser(); const user = Utils.getCurrentUser();
const userRole = Utils.getUserRole(user); const userRole = Utils.getUserRole(user);
let hasLoggedIn = Utils.getIsAuth(); let hasLoggedIn = Utils.getIsAuth();
//console.log(this.msg + " the userlogin ", user, hasLoggedIn); //console.log(this.msg + " the userlogin ", user, hasLoggedIn);
if (hasLoggedIn) { if (hasLoggedIn) {
if (roleAllowed != undefined) if (roleAllowed != undefined)
{ {
//it number toString //it number toString
// console.log("[auth-guard] ther roleAllow ", roleAllowed); // console.log("[auth-guard] ther roleAllow ", roleAllowed);
// console.log("[auth-guard] before roleAllow ", roleAllowed,userRole); // console.log("[auth-guard] before roleAllow ", roleAllowed,userRole);
if (roleAllowed.indexOf(userRole.toString()) != -1) { if (roleAllowed.indexOf(userRole.toString()) != -1) {
// console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole); // console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole);
return true; return true;
} }
else { else {
this.authService.logout(); this.authService.logout();
this.router.navigate(['/login']); this.router.navigate(['/login']);
return false; return false;
} }
} }
else //if no role define just return hasLoggedIn else //if no role define just return hasLoggedIn
{ {
return hasLoggedIn; return hasLoggedIn;
} }
} }
else else
{ {
// this.authService.logout(); // this.authService.logout();
this.router.navigate(['/login']); this.router.navigate(['/login']);
return false; return false;
} }
// not logged in so redirect to login page with the return url // not logged in so redirect to login page with the return url
//this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); //this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
this.authService.logout(); this.authService.logout();
return false; 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'; import { FormControl, FormGroup } from '@angular/forms';
export class CommonUtilities { export class CommonUtilities {
/** Display the blob */ /** Display the blob */
static displayFileInNewTab(response:any, defaultFileName:string) { static displayFileInNewTab(response:any, defaultFileName:string) {
const contentType = response.headers.get('content-type'); const contentType = response.headers.get('content-type');
const blob = new Blob([response.body], { type: contentType }); const blob = new Blob([response.body], { type: contentType });
const fileName = this.getFileName(response,defaultFileName); const fileName = this.getFileName(response,defaultFileName);
const nav = (window.navigator as any); const nav = (window.navigator as any);
if (nav.msSaveOrOpenBlob) { if (nav.msSaveOrOpenBlob) {
nav.msSaveOrOpenBlob(blob, fileName); nav.msSaveOrOpenBlob(blob, fileName);
} else { } else {
const data = window.URL.createObjectURL(blob); const data = window.URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = data; link.href = data;
link.target = '_blank'; link.target = '_blank';
link.click(); link.click();
} }
} }
/*** Download the blob ***/ /*** Download the blob ***/
static downloadFile(response:any, defaultFileName:string) { static downloadFile(response:any, defaultFileName:string) {
const contentType = response.headers.get('content-type'); const contentType = response.headers.get('content-type');
const blob = new Blob([response.body], { type: contentType }); const blob = new Blob([response.body], { type: contentType });
const fileName = this.getFileName(response,defaultFileName); const fileName = this.getFileName(response,defaultFileName);
const nav = (window.navigator as any); const nav = (window.navigator as any);
if (typeof nav.msSaveBlob === 'function') if (typeof nav.msSaveBlob === 'function')
{ {
console.log("download the report saveblob"); console.log("download the report saveblob");
nav.msSaveBlob(blob, fileName); nav.msSaveBlob(blob, fileName);
console.log("download the report saveblob done"); console.log("download the report saveblob done");
} }
else { else {
const link = document.createElement('a'); const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob); link.href = window.URL.createObjectURL(blob);
console.log("no msSaveBlob download the report saveblob done"); console.log("no msSaveBlob download the report saveblob done");
//link.download? = fileName; //link.download? = fileName;
link.download = fileName; link.download = fileName;
link.click(); link.click();
} }
} }
/** Get the attachment file name */ /** Get the attachment file name */
static getFileName(response:any, defaultFileName:string) { static getFileName(response:any, defaultFileName:string) {
const contentDis = response.headers.get('content-disposition'); const contentDis = response.headers.get('content-disposition');
let fileName =defaultFileName; let fileName =defaultFileName;
if (contentDis && contentDis.indexOf('attachment') !== -1) { if (contentDis && contentDis.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(contentDis); const matches = filenameRegex.exec(contentDis);
if (matches != null && matches[1]) { if (matches != null && matches[1]) {
fileName = matches[1].replace(/['"]/g, ''); fileName = matches[1].replace(/['"]/g, '');
} }
} }
return fileName; return fileName;
} }
} }
+76 -76
View File
@@ -1,77 +1,77 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { firstValueFrom, Observable } from 'rxjs'; import { firstValueFrom, Observable } from 'rxjs';
export function initializeApp_old(appInitService: AppSettingService) { export function initializeApp_old(appInitService: AppSettingService) {
return (): Promise<any> => { return (): Promise<any> => {
return appInitService.loadAppSetting_p(); return appInitService.loadAppSetting_p();
} }
}; };
export function initializeApp() { export function initializeApp() {
return (): Promise<any> => { return (): Promise<any> => {
return inject(AppSettingService).loadAppSetting_p(); return inject(AppSettingService).loadAppSetting_p();
} }
// return () => inject(AppSettingService).loadAppSetting(); // return () => inject(AppSettingService).loadAppSetting();
}; };
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AppSettingService { export class AppSettingService {
private _appsetting: any; private _appsetting: any;
constructor(private http: HttpClient constructor(private http: HttpClient
) { } ) { }
public loadAppSetting_p(): Promise<any> { public loadAppSetting_p(): Promise<any> {
const source$ = this.http.get("config/appsetting.json") const source$ = this.http.get("config/appsetting.json")
const rs$ = firstValueFrom(source$) const rs$ = firstValueFrom(source$)
.then(x => { .then(x => {
this._appsetting = x; this._appsetting = x;
console.log("[AppSettingService] assign", x); console.log("[AppSettingService] assign", x);
}); });
return rs$; return rs$;
} }
public loadAppSetting(): Observable<any> { public loadAppSetting(): Observable<any> {
const source$ = this.http.get("config/appsetting.json") const source$ = this.http.get("config/appsetting.json")
return source$; return source$;
} }
set appSetting(value: any) { set appSetting(value: any) {
this._appsetting = value; this._appsetting = value;
} }
get appSetting(): any { get appSetting(): any {
return this._appsetting; return this._appsetting;
} }
} }
/* angular 19 new one in app.config.ts /* angular 19 new one in app.config.ts
provideAppInitializer(initializeApp()), provideAppInitializer(initializeApp()),
/* module put this /* module put this
to use it to use it
in app.module.ts in app.module.ts
add add
1) 1)
providers: [ providers: [
{ provide : APP_INITIALIZER, multi : true, deps : [AppSettingService], { provide : APP_INITIALIZER, multi : true, deps : [AppSettingService],
useFactory: initializeApp useFactory: initializeApp
}, },
] ]
2) 2)
other this one work too. other this one work too.
providers: [ providers: [
{ {
provide : APP_INITIALIZER, provide : APP_INITIALIZER,
multi : true, multi : true,
deps : [AppSettingService], deps : [AppSettingService],
useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting() useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting()
} }
] ]
to use it calling something like this to use it calling something like this
export class TestComponent { export class TestComponent {
public test1ServiceUrl: string; public test1ServiceUrl: string;
constructor(public configService: AppConfigService) { constructor(public configService: AppConfigService) {
this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl; this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl;
} }
} }
*/ */
+97 -97
View File
@@ -1,97 +1,97 @@
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { Observable, throwError } from 'rxjs'; import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
//import { ToastrService } from 'ngx-toastr'; //import { ToastrService } from 'ngx-toastr';
import { NavigationExtras, Router } from '@angular/router'; import { NavigationExtras, Router } from '@angular/router';
/* /*
@Injectable() @Injectable()
export class ErrorInterceptor implements HttpInterceptor { export class ErrorInterceptor implements HttpInterceptor {
//private authenticationService: AuthenticationService; //private authenticationService: AuthenticationService;
constructor( constructor(
private router: Router, private router: Router,
// private toastr: ToastrService, // private toastr: ToastrService,
) { } ) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// console.log("I am intercept to catch error status = 401...", request); // console.log("I am intercept to catch error status = 401...", request);
return next.handle(request).pipe(catchError(err => { return next.handle(request).pipe(catchError(err => {
/* no use yet /* no use yet
if (err) { if (err) {
switch(err.status) { switch(err.status) {
case 400: case 400:
if (err.error.errors) { if (err.error.errors) {
const modalStateErrors = []; const modalStateErrors = [];
for (const key in err.error.errors) { for (const key in err.error.errors) {
if (err.error.errors[key]) { if (err.error.errors[key]) {
modalStateErrors.push(err.error.errors[key]); modalStateErrors.push(err.error.errors[key]);
} }
} }
throw modalStateErrors; throw modalStateErrors;
} else { } else {
this.toastr.error(err.statusText, err.status); this.toastr.error(err.statusText, err.status);
} }
break; break;
case 401: case 401:
this.toastr.error(err.statusText, err.status); this.toastr.error(err.statusText, err.status);
break; break;
case 404: case 404:
this.router.navigateByUrl('/not-found'); this.router.navigateByUrl('/not-found');
break; break;
case 500: case 500:
const navigationExtras: NavigationExtras = {state:{error:err.error}}; const navigationExtras: NavigationExtras = {state:{error:err.error}};
this.router.navigateByUrl('/server-error', navigationExtras); this.router.navigateByUrl('/server-error', navigationExtras);
break; break;
default: default:
this.toastr.error('Something unexpected went wrong'); this.toastr.error('Something unexpected went wrong');
console.log("this error interceptor",err); console.log("this error interceptor",err);
break; break;
} }
} }
*/ */
/* /*
if (err.status === 401) { if (err.status === 401) {
// auto logout if 401 response returned from api Unauthorized // auto logout if 401 response returned from api Unauthorized
// this.authenticationService.logout(); // this.authenticationService.logout();
//location.reload(); //location.reload();
const error = err.error.message || err.statusText; const error = err.error.message || err.statusText;
console.log("After I Get Error status = 401...", error, err); console.log("After I Get Error status = 401...", error, err);
return throwError(error); return throwError(error);
} }
else { else {
console.log("I Get Error .", err); console.log("I Get Error .", err);
return throwError(err); return throwError(err);
} }
})) }))
} }
} }
*/ */
//new one //new one
//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/ //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>> => { export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const authenticationService = inject(AuthenticationService); const authenticationService = inject(AuthenticationService);
// logger.log(`Request is on its way to ${req.url}`); // logger.log(`Request is on its way to ${req.url}`);
return next(req).pipe(catchError(err => { return next(req).pipe(catchError(err => {
if (err.status === 401) { if (err.status === 401) {
// auto logout if 401 response returned from api Unauthorized // auto logout if 401 response returned from api Unauthorized
// this.authenticationService.logout(); // this.authenticationService.logout();
//location.reload(); //location.reload();
const error = err.error.message || err.statusText; const error = err.error.message || err.statusText;
// console.log("After I Get Error status = 401...", error, err); // console.log("After I Get Error status = 401...", error, err);
return throwError(() => error); return throwError(() => error);
} }
else { else {
console.log("I Get Error .", err); console.log("I Get Error .", err);
return throwError(() => err); return throwError(() => err);
} }
})); }));
} }
//////////////// ////////////////
+114 -114
View File
@@ -1,115 +1,115 @@
import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable, throwError } from 'rxjs'; import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { ConfigureUrl } from '../models'; import { ConfigureUrl } from '../models';
import { AppSettingService } from './appsetting'; import { AppSettingService } from './appsetting';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class HttpUtility { export class HttpUtility {
private baseApiUrl: string = ""; private baseApiUrl: string = "";
constructor(private http: HttpClient, constructor(private http: HttpClient,
private appSetting :AppSettingService, private appSetting :AppSettingService,
private router: Router) { private router: Router) {
} }
/** /**
* getFile * getFile
* @param url * @param url
*/ */
public getFileAsText(url: string): Observable<any> { public getFileAsText(url: string): Observable<any> {
return this.http return this.http
.get(this.getApiUrl(url), { responseType: 'blob' as 'text' }) .get(this.getApiUrl(url), { responseType: 'blob' as 'text' })
.pipe(catchError(e => this.handleError(e))); .pipe(catchError(e => this.handleError(e)));
} }
/** /**
* getFile * getFile
* @param url * @param url
*/ */
public getFile(url: string): Observable<any> { public getFile(url: string): Observable<any> {
return this.http return this.http
.get(this.getApiUrl(url), { responseType: 'blob' as 'json' }) .get(this.getApiUrl(url), { responseType: 'blob' as 'json' })
.pipe(catchError(e => this.handleError(e))); .pipe(catchError(e => this.handleError(e)));
} }
/** /**
* handleError * handleError
* @param response * @param response
*/ */
private handleError(errorResponse: HttpErrorResponse) { private handleError(errorResponse: HttpErrorResponse) {
// in a real world app, we may send the server to some remote logging infrastructure // in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console // instead of just logging it to the console
return throwError(errorResponse); return throwError(errorResponse);
} }
//get excel file zip file etc .. //get excel file zip file etc ..
/* /*
allowedToDisplay = [ allowedToDisplay = [
this.mimeConstant.png, this.mimeConstant.png,
this.mimeConstant.jpeg, this.mimeConstant.jpeg,
this.mimeConstant.jpg, this.mimeConstant.jpg,
this.mimeConstant.gif, this.mimeConstant.gif,
this.mimeConstant.txt, this.mimeConstant.txt,
this.mimeConstant.pdf this.mimeConstant.pdf
]; ];
*/ */
/* how to use it /* how to use it
this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe( this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe(
catchError(err => { catchError(err => {
this.snackBar.error(err || this.constants.error_Getting_photo); this.snackBar.error(err || this.constants.error_Getting_photo);
return EMPTY; return EMPTY;
}), }),
finalize(() => { finalize(() => {
this.spinner.hide(); this.spinner.hide();
})).subscribe(response => { })).subscribe(response => {
if (response) { if (response) {
if (isDownload) { if (isDownload) {
CommonUtilities.downloadFile(response); CommonUtilities.downloadFile(response);
} else { } else {
if (response.body && response.body.type) { if (response.body && response.body.type) {
if (this.allowedToDisplay.includes(response.body.type)) { if (this.allowedToDisplay.includes(response.body.type)) {
CommonUtilities.displayFileInNewTab(response); CommonUtilities.displayFileInNewTab(response);
} else { } else {
this.temporaryFile = response; this.temporaryFile = response;
this.downloadWarning = true; this.downloadWarning = true;
} }
} }
} }
} }
}); });
*/ */
public getFileResponse(url: string, data?: any): Observable<any> { public getFileResponse(url: string, data?: any): Observable<any> {
if (data) { if (data) {
return this.http.post(this.getApiUrl(url), return this.http.post(this.getApiUrl(url),
data, data,
{ responseType: 'blob', observe: 'response' }) { responseType: 'blob', observe: 'response' })
.pipe(catchError(e => this.handleError(e))); .pipe(catchError(e => this.handleError(e)));
} }
return this.http return this.http
.get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' }) .get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' })
.pipe(catchError(e => this.handleError(e))); .pipe(catchError(e => this.handleError(e)));
} }
public getFileAsPDF(url: string, data: any): Observable<any> { public getFileAsPDF(url: string, data: any): Observable<any> {
let updateURL; let updateURL;
if (data) { if (data) {
updateURL = url + data; updateURL = url + data;
} else { } else {
updateURL = url; updateURL = url;
} }
return this.http return this.http
.get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' }) .get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' })
.pipe(catchError(e => this.handleError(e))); .pipe(catchError(e => this.handleError(e)));
} }
/** /**
* getApiUrl * getApiUrl
* @param url * @param url
*/ */
private getApiUrl(url:string) { private getApiUrl(url:string) {
const baseUrl = this.appSetting.appSetting.baseUrl; const baseUrl = this.appSetting.appSetting.baseUrl;
return baseUrl + "/"+ url; return baseUrl + "/"+ url;
} }
} }
+7 -7
View File
@@ -1,8 +1,8 @@
export * from './utils'; export * from './utils';
export {initializeApp, AppSettingService} from './appsetting'; export {initializeApp, AppSettingService} from './appsetting';
export * from './error.interceptor'; export * from './error.interceptor';
export * from './jwt.interceptor'; export * from './jwt.interceptor';
export * from './lookup.service'; export * from './lookup.service';
export * from './httpfile.service'; export * from './httpfile.service';
export * from './CommonUtilities'; export * from './CommonUtilities';
export * from './timeinput'; 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 { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Utils } from './utils'; import { Utils } from './utils';
/* /*
@Injectable() @Injectable()
export class JwtInterceptor implements HttpInterceptor { export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available // add authorization header with jwt token if available
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!; const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
console.log('I am interceptor debug for currentUser obj', currentUser); console.log('I am interceptor debug for currentUser obj', currentUser);
if (currentUser && currentUser.token) { if (currentUser && currentUser.token) {
//console.log('I am interceptor add authorization header with jwt token'); //console.log('I am interceptor add authorization header with jwt token');
request = request.clone({ request = request.clone({
setHeaders: { setHeaders: {
Authorization: `Bearer ${currentUser.token}` Authorization: `Bearer ${currentUser.token}`
} }
}); });
} }
return next.handle(request); return next.handle(request);
} }
} }
/* /*
new one new one
//////////////// ////////////////
*/ */
export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => { export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
//const logger = inject(Logger); //const logger = inject(Logger);
//logger.log(`Request is on its way to ${req.url}`); //logger.log(`Request is on its way to ${req.url}`);
const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!; const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
// console.log('I am interceptor debug for currentUser obj', currentUser); // console.log('I am interceptor debug for currentUser obj', currentUser);
if (currentUser && currentUser.token && currentUser.token != "") { if (currentUser && currentUser.token && currentUser.token != "") {
// console.log('I am interceptor add authorization header with jwt token'); // console.log('I am interceptor add authorization header with jwt token');
request = request.clone({ request = request.clone({
setHeaders: { setHeaders: {
Authorization: `Bearer ${currentUser.token}` Authorization: `Bearer ${currentUser.token}`
} }
}); });
} }
return next(request); return next(request);
} }
//////////////// ////////////////
/* /*
providers: [provideHttpClient(withInterceptors([loggerInterceptor]))] providers: [provideHttpClient(withInterceptors([loggerInterceptor]))]
*/ */
+136 -136
View File
@@ -1,136 +1,136 @@
import { Injectable, Inject , Output} from '@angular/core'; import { Injectable, Inject , Output} from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http'; import { HttpClient, HttpParams } from '@angular/common/http';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { ConfigureUrl,Lookup,LookupEdit,ResultModel import { ConfigureUrl,Lookup,LookupEdit,ResultModel
} from '../models'; } from '../models';
import { Utils,AppSettingService } from '../shares'; import { Utils,AppSettingService } from '../shares';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class LookupService { export class LookupService {
msg: string = "[lookupService] "; msg: string = "[lookupService] ";
_lookupByStatus:any; _lookupByStatus:any;
_staffList:any; _staffList:any;
_clientList:any; _clientList:any;
_jobList:any; _jobList:any;
constructor(private http: HttpClient, constructor(private http: HttpClient,
private appSetting :AppSettingService private appSetting :AppSettingService
) { ) {
// this default when load look for previous login information // this default when load look for previous login information
// if no need then comment out downthere // if no need then comment out downthere
this.setAllNull(); this.setAllNull();
} }
setAllNull() :void { setAllNull() :void {
// this._lhdList = undefined; // this._lhdList = undefined;
this._lookupByStatus = {}; this._lookupByStatus = {};
this._staffList = null; this._staffList = null;
this._clientList = null; this._clientList = null;
this._jobList = null; this._jobList = null;
} }
loadLookupEditBystatus(type:string): Observable<ResultModel<LookupEdit[]>> { loadLookupEditBystatus(type:string): Observable<ResultModel<LookupEdit[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit";
const params = new HttpParams().append('type', type); const params = new HttpParams().append('type', type);
console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type); console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type);
return this.http.get<ResultModel<LookupEdit[]>>(baseUrl, {params}); return this.http.get<ResultModel<LookupEdit[]>>(baseUrl, {params});
} }
loadLookupById(id:number, type:string): Observable<ResultModel<LookupEdit>> { loadLookupById(id:number, type:string): Observable<ResultModel<LookupEdit>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
const params = new HttpParams() const params = new HttpParams()
.append('id', id) .append('id', id)
.append('type', type); .append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params}); return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
} }
saveLookup(item:LookupEdit): Observable<ResultModel<number>> { //insert Lookup saveLookup(item:LookupEdit): Observable<ResultModel<number>> { //insert Lookup
this.setAllNull(); this.setAllNull();
let config = { headers : { 'Content-Type': 'application/json' } }; let config = { headers : { 'Content-Type': 'application/json' } };
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
return this.http.post<ResultModel<number>>(baseUrl, item, config); return this.http.post<ResultModel<number>>(baseUrl, item, config);
} }
deleteLookup(id:number): Observable<ResultModel<number>>{ deleteLookup(id:number): Observable<ResultModel<number>>{
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
return this.http.delete<ResultModel<number>>(baseUrl + "/" + id ); return this.http.delete<ResultModel<number>>(baseUrl + "/" + id );
} }
loadLookupBystatus(type:string): Observable<ResultModel<Lookup[]>> { loadLookupBystatus(type:string): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs";
console.log(this.msg + "loadLookupBystatus", this._lookupByStatus); console.log(this.msg + "loadLookupBystatus", this._lookupByStatus);
if (this._lookupByStatus[type]) if (this._lookupByStatus[type])
{ {
console.log(this.msg + "Not go server this LoadJobs is in cache ",type); console.log(this.msg + "Not go server this LoadJobs is in cache ",type);
return of(this._lookupByStatus[type]); return of(this._lookupByStatus[type]);
} }
else else
{ {
const params = new HttpParams().append('type', type); const params = new HttpParams().append('type', type);
console.log(this.msg + "go server this loadLookupBystatus is in cache ", type); console.log(this.msg + "go server this loadLookupBystatus is in cache ", type);
return this.http.get<ResultModel<Lookup[]>>(baseUrl, {params}) return this.http.get<ResultModel<Lookup[]>>(baseUrl, {params})
.pipe(map(resultModel => { .pipe(map(resultModel => {
this._lookupByStatus[type] = resultModel; this._lookupByStatus[type] = resultModel;
return resultModel; return resultModel;
}) })
); );
} }
} }
loadStaffs(): Observable<ResultModel<Lookup[]>> { loadStaffs(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs";
/* const params = new HttpParams() /* const params = new HttpParams()
.append('id', id) .append('id', id)
.append('type', type); .append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params}); return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/ */
if (this._staffList != null) if (this._staffList != null)
{ {
return of(this._staffList); return of(this._staffList);
} }
else else
{ {
return this.http.get<ResultModel<Lookup[]>>(baseUrl) return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map( x => { .pipe(map( x => {
this._staffList = x; this._staffList = x;
return x; return x;
})); }));
} }
} }
loadClients(): Observable<ResultModel<Lookup[]>> { loadClients(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients";
/* const params = new HttpParams() /* const params = new HttpParams()
.append('id', id) .append('id', id)
.append('type', type); .append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params}); return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/ */
if (this._clientList != null) if (this._clientList != null)
{ {
return of(this._clientList); return of(this._clientList);
} }
else else
{ {
return this.http.get<ResultModel<Lookup[]>>(baseUrl) return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map(resultModel => { .pipe(map(resultModel => {
this._clientList = resultModel; this._clientList = resultModel;
return resultModel; return resultModel;
}) })
); );
} }
} }
loadJobs(): Observable<ResultModel<Lookup[]>> { loadJobs(): Observable<ResultModel<Lookup[]>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs"; const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs";
/* const params = new HttpParams() /* const params = new HttpParams()
.append('id', id) .append('id', id)
.append('type', type); .append('type', type);
return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params}); return this.http.get<ResultModel<LookupEdit>>(baseUrl,{params});
*/ */
if (this._jobList != null) if (this._jobList != null)
{ {
return of(this._jobList); return of(this._jobList);
} }
else else
{ {
return this.http.get<ResultModel<Lookup[]>>(baseUrl) return this.http.get<ResultModel<Lookup[]>>(baseUrl)
.pipe(map(x => { .pipe(map(x => {
this._jobList = x; this._jobList = x;
return x; return x;
})) }))
} }
} }
} }
+149 -149
View File
@@ -1,149 +1,149 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core'; import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@Component({ @Component({
selector: 'time-input', selector: 'time-input',
imports:[ReactiveFormsModule,CommonModule], imports:[ReactiveFormsModule,CommonModule],
template: ` template: `
<input <input
type="text" type="text"
[formControl]="timeControl" [formControl]="timeControl"
(input)="onInput($event)" (input)="onInput($event)"
(blur)="onBlur()" (blur)="onBlur()"
class="time-input" class="time-input"
style="width: 80px; text-align: center;" style="width: 80px; text-align: center;"
/> />
`, `,
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeInputComponent), useExisting: forwardRef(() => TimeInputComponent),
multi: true, multi: true,
}, },
], ],
}) })
export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy { export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
@Input() placeholder: string = 'HH:MM'; @Input() placeholder: string = 'HH:MM';
@Input() initialValue: string = ''; // Added initialValue input @Input() initialValue: string = ''; // Added initialValue input
@Input() disabled: boolean = false; @Input() disabled: boolean = false;
timeControl = new FormControl('', [ timeControl = new FormControl('', [
Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
]); ]);
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
private _value: string = ''; private _value: string = '';
private onChange: (value: string) => void = () => {}; private onChange: (value: string) => void = () => {};
private onTouched: () => void = () => {}; private onTouched: () => void = () => {};
constructor() {} constructor() {}
ngOnInit(): void { ngOnInit(): void {
this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => { this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.onChange(value || ''); // Pass the value to the parent form this.onChange(value || ''); // Pass the value to the parent form
}); });
if (this.initialValue) { if (this.initialValue) {
this.writeValue(this.initialValue); this.writeValue(this.initialValue);
} }
this.timeControl.disable(); this.timeControl.disable();
if(!this.disabled){ if(!this.disabled){
this.timeControl.enable(); this.timeControl.enable();
} }
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
} }
get value(): string { get value(): string {
return this._value; return this._value;
} }
set value(val: string) { set value(val: string) {
this._value = val; this._value = val;
this.onChange(val); this.onChange(val);
this.onTouched(); this.onTouched();
} }
writeValue(value: string): void { writeValue(value: string): void {
this._value = value || ''; this._value = value || '';
this.formatInput(this._value); // Use the formatting function this.formatInput(this._value); // Use the formatting function
} }
registerOnChange(fn: (value: string) => void): void { registerOnChange(fn: (value: string) => void): void {
this.onChange = fn; this.onChange = fn;
} }
registerOnTouched(fn: () => void): void { registerOnTouched(fn: () => void): void {
this.onTouched = fn; this.onTouched = fn;
} }
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (isDisabled) { if (isDisabled) {
this.timeControl.disable(); this.timeControl.disable();
} else { } else {
this.timeControl.enable(); this.timeControl.enable();
} }
} }
onInput(event: Event): void { onInput(event: Event): void {
let input = (event.target as HTMLInputElement).value; let input = (event.target as HTMLInputElement).value;
input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters. input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
if (input.length > 4) { if (input.length > 4) {
input = input.slice(0, 4); // Limit to 4 digits input = input.slice(0, 4); // Limit to 4 digits
} }
let formatted = ''; let formatted = '';
if (input.length > 2) { if (input.length > 2) {
formatted = input.slice(0, 2) + ':' + input.slice(2); formatted = input.slice(0, 2) + ':' + input.slice(2);
} else { } else {
formatted = input; formatted = input;
} }
this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
this.value = formatted; this.value = formatted;
} }
onBlur(): void { onBlur(): void {
this.onTouched(); this.onTouched();
if (this.timeControl.valid) { if (this.timeControl.valid) {
return; return;
} }
if (this.timeControl.value?.length === 0) { if (this.timeControl.value?.length === 0) {
this.timeControl.setValue('', {emitEvent: false}); this.timeControl.setValue('', {emitEvent: false});
this.value = ''; this.value = '';
return return
} }
this.formatInput(this.timeControl.value); this.formatInput(this.timeControl.value);
} }
private formatInput(val: string | null): void { private formatInput(val: string | null): void {
if (!val) { if (!val) {
this.timeControl.setValue('', {emitEvent: false}); this.timeControl.setValue('', {emitEvent: false});
this.value = ''; this.value = '';
return; return;
} }
let numbersOnly = val.replace(/[^0-9]/g, ''); let numbersOnly = val.replace(/[^0-9]/g, '');
let formatted = ''; let formatted = '';
if (numbersOnly.length > 2) { if (numbersOnly.length > 2) {
formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4); formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
} else { } else {
formatted = numbersOnly; formatted = numbersOnly;
} }
if (formatted.length === 5 && this.timeControl.valid) { if (formatted.length === 5 && this.timeControl.valid) {
this.timeControl.setValue(formatted, {emitEvent: false}); this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted; this.value = formatted;
} else if (numbersOnly.length <= 2) { } else if (numbersOnly.length <= 2) {
this.timeControl.setValue(formatted, {emitEvent: false}); this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted; this.value = formatted;
} else { } else {
this.timeControl.setValue('00:00', {emitEvent: false}); this.timeControl.setValue('00:00', {emitEvent: false});
this.value = '00:00' this.value = '00:00'
} }
} }
} }
+218 -218
View File
@@ -1,219 +1,219 @@
import moment from "moment"; import moment from "moment";
import { Person, User, userRole } from "../models" import { Person, User, userRole } from "../models"
import { TreeNode } from "primeng/api"; import { TreeNode } from "@openng/optimus-ui/api";
type MyProName = "fatherId" | "motherId"; type MyProName = "fatherId" | "motherId";
export class Utils { export class Utils {
static toBase64 (file:any) static toBase64 (file:any)
{ {
let promise = new Promise((resolve, reject) => { let promise = new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result); reader.onload = () => resolve(reader.result);
reader.onerror = reject; reader.onerror = reject;
}); });
return promise; return promise;
} }
static getBase64(file: any) { static getBase64(file: any) {
let result: any; let result: any;
var reader = new FileReader(); var reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
reader.onload = function () { reader.onload = function () {
result = reader.result; result = reader.result;
console.log('tobase64 ', result); console.log('tobase64 ', result);
}; };
reader.onerror = function (error) { reader.onerror = function (error) {
console.log('Error: ', error); console.log('Error: ', error);
}; };
} }
static getCurrentUser(): User { static getCurrentUser(): User {
let myUser = new User(); let myUser = new User();
myUser.username = ""; myUser.username = "";
const obj = sessionStorage.getItem('currentUser'); const obj = sessionStorage.getItem('currentUser');
//const obj = localStorage.getItem('currentUser'); //const obj = localStorage.getItem('currentUser');
if (obj != undefined) { if (obj != undefined) {
let user = JSON.parse(obj); let user = JSON.parse(obj);
myUser.firstName = user.firstName; myUser.firstName = user.firstName;
myUser.id = user.id; myUser.id = user.id;
myUser.lastName = user.lastName; myUser.lastName = user.lastName;
myUser.role = user.role; myUser.role = user.role;
myUser.username = user.username; myUser.username = user.username;
myUser.email = user.email; myUser.email = user.email;
myUser.token = user.token; myUser.token = user.token;
myUser.phone = user.phone; myUser.phone = user.phone;
myUser.department = user.department; myUser.department = user.department;
myUser.position = user.position; myUser.position = user.position;
myUser.managerEmail = user.managerEmail; myUser.managerEmail = user.managerEmail;
} }
// console.log("[util] getcurrentUser",myUser); // console.log("[util] getcurrentUser",myUser);
return myUser; return myUser;
} }
static setLocalStore(user: User): void { static setLocalStore(user: User): void {
if (user && user.token) { if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes // store user details and jwt token in local storage to keep user logged in between page refreshes
// localStorage.setItem('currentUser', JSON.stringify(user)); // localStorage.setItem('currentUser', JSON.stringify(user));
sessionStorage.setItem('currentUser', JSON.stringify(user)); sessionStorage.setItem('currentUser', JSON.stringify(user));
} }
} }
static getLastMonth(): Date { static getLastMonth(): Date {
let result = moment().subtract(1, 'months').toDate(); let result = moment().subtract(1, 'months').toDate();
result.setDate(1); result.setDate(1);
return result; return result;
} }
static getHome(): string { static getHome(): string {
let result =""; let result ="";
const user = this.getCurrentUser(); const user = this.getCurrentUser();
const urole = user.role; const urole = user.role;
if (urole == userRole.Accounting) if (urole == userRole.Accounting)
{ {
result = "staffworkw"; result = "staffworkw";
} }
else if (urole == userRole.Admin) else if (urole == userRole.Admin)
{ {
result = "staff"; result = "staff";
} }
else if (urole == userRole.ServiceManager) else if (urole == userRole.ServiceManager)
{ {
result = "servicetask"; result = "servicetask";
} }
else if (urole == userRole.WorkShop) else if (urole == userRole.WorkShop)
{ {
result = "staffwork"; result = "staffwork";
} }
else if (urole == userRole.Normal) else if (urole == userRole.Normal)
{ {
result = "staff"; result = "staff";
} }
return result; return result;
} }
static getTimeStr(val:Date): string { static getTimeStr(val:Date): string {
let result = "00:00"; let result = "00:00";
if (val) if (val)
{ {
const hh = val.getHours(); const hh = val.getHours();
const min = val.getMinutes(); const min = val.getMinutes();
if (hh < 10) if (hh < 10)
result = '0' + hh.toString(); result = '0' + hh.toString();
else else
result = hh.toString(); result = hh.toString();
if (min < 10) if (min < 10)
result = result + ":0" + min.toString(); result = result + ":0" + min.toString();
else else
result = result + ":" + min.toString(); result = result + ":" + min.toString();
} }
return result; return result;
} }
static canRunReport(role: number): boolean { static canRunReport(role: number): boolean {
let result = false; let result = false;
if (role == userRole.Admin) if (role == userRole.Admin)
result = true; result = true;
return result; return result;
} }
static getUserRole(user: User): number { static getUserRole(user: User): number {
/* /*
sysadmin sysadmin
request_manager request_manager
ward_user ward_user
*/ */
let ret = 0; let ret = 0;
if (user) { if (user) {
// console.log("utils user", user); // console.log("utils user", user);
if (user.role) if (user.role)
ret = user.role; ret = user.role;
} }
return ret; return ret;
} }
static getIsAuth(): boolean { static getIsAuth(): boolean {
const user = this.getCurrentUser(); const user = this.getCurrentUser();
if (user.username != "") { if (user.username != "") {
return true; return true;
} }
else else
return false; return false;
} }
static getAdminRoleName(): number { static getAdminRoleName(): number {
return userRole.Admin; return userRole.Admin;
} }
static clearCurrentUser(): void { static clearCurrentUser(): void {
sessionStorage.clear(); sessionStorage.clear();
//localStorage.clear(); //localStorage.clear();
} }
static formatNode(item:Person): TreeNode static formatNode(item:Person): TreeNode
{ {
let label = item.lastName + " " + item.firstName; let label = item.lastName + " " + item.firstName;
let key = item.id.toString(); let key = item.id.toString();
let node: TreeNode ={ let node: TreeNode ={
key, key,
label, label,
type: 'person', type: 'person',
data: {title: item.title, name: label, image: item.image}, data: {title: item.title, name: label, image: item.image},
icon: 'pi pi-user' icon: 'pi pi-user'
}; };
return node; return node;
} }
static getFileExtension(filename: string): string { static getFileExtension(filename: string): string {
const lastDotIndex = filename.lastIndexOf('.'); const lastDotIndex = filename.lastIndexOf('.');
if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { // Ensure a dot exists and is not the last character if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { // Ensure a dot exists and is not the last character
return filename.substring(lastDotIndex + 1); return filename.substring(lastDotIndex + 1);
} }
return ''; // No extension found return ''; // No extension found
} }
static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] { static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] {
let result: TreeNode[] =[]; let result: TreeNode[] =[];
let tree_node_child: TreeNode[]; let tree_node_child: TreeNode[];
let node:TreeNode; let node:TreeNode;
let item: Person; let item: Person;
const children = childressNodes.filter(x => x[proName] == pid); const children = childressNodes.filter(x => x[proName] == pid);
for (let c = 0; c < children.length; c++) for (let c = 0; c < children.length; c++)
{ {
item = children[c]; item = children[c];
node = this.formatNode(item); node = this.formatNode(item);
tree_node_child = this.getChildForParentId(proName,item.id, childressNodes); tree_node_child = this.getChildForParentId(proName,item.id, childressNodes);
console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child); console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child);
if (tree_node_child.length > 0) if (tree_node_child.length > 0)
{ {
node.expanded = true; node.expanded = true;
node.children = tree_node_child; node.children = tree_node_child;
} }
result.push(node); result.push(node);
} }
return result; return result;
} }
static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] { static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] {
let familyTree: TreeNode[] = []; let familyTree: TreeNode[] = [];
let node:TreeNode; let node:TreeNode;
let child_nodes:TreeNode[]; let child_nodes:TreeNode[];
let childressNodes:Person[]; let childressNodes:Person[];
let item: Person; let item: Person;
const topNodes = familyList.filter(x => x[proName]! < 1); const topNodes = familyList.filter(x => x[proName]! < 1);
childressNodes = familyList.filter(x => x[proName]! > 0); childressNodes = familyList.filter(x => x[proName]! > 0);
for(let i = 0; i < topNodes.length; i++) for(let i = 0; i < topNodes.length; i++)
{ {
item = topNodes[i]; item = topNodes[i];
node = Utils.formatNode(item); node = Utils.formatNode(item);
child_nodes = this.getChildForParentId(proName, item.id,childressNodes); child_nodes = this.getChildForParentId(proName, item.id,childressNodes);
console.log("populate getchildrenForParentId", child_nodes); console.log("populate getchildrenForParentId", child_nodes);
if (child_nodes.length > 0) if (child_nodes.length > 0)
{ {
node.expanded = true; node.expanded = true;
node.children = child_nodes; node.children = child_nodes;
} }
familyTree.push(node); familyTree.push(node);
//childressNodes = //childressNodes =
} }
return familyTree; return familyTree;
} }
} }

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