diff --git a/API/FamilyTreeAPI/Entities/TreeNode.cs b/API/FamilyTreeAPI/Entities/TreeNode.cs index d7cb19a..531ba35 100644 --- a/API/FamilyTreeAPI/Entities/TreeNode.cs +++ b/API/FamilyTreeAPI/Entities/TreeNode.cs @@ -6,10 +6,12 @@ public class TreeNode public T? Data { get; set; } public List>? Children { get; set; } public string? Icon { get; set; } - public bool? Checked { get; set; } + public bool? Checked { get; set; } public bool? Leaf { get; set; } public bool? Expanded { get; set; } public string? Type { get; set; } + + public string StyleClass { get; set; } public string? Key { get; set; } public bool? Loading { get; set; } } \ No newline at end of file diff --git a/API/FamilyTreeAPI/Properties/launchSettings.json b/API/FamilyTreeAPI/Properties/launchSettings.json index 0e0cdd6..6118837 100644 --- a/API/FamilyTreeAPI/Properties/launchSettings.json +++ b/API/FamilyTreeAPI/Properties/launchSettings.json @@ -8,7 +8,8 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:5015" + "applicationUrl": "http://localhost:5015", + "applicationUrl1": "http://192.168.8.188:5015" }, "IIS Express": { "commandName": "IISExpress", diff --git a/API/FamilyTreeAPI/Repository/PersonRepository.cs b/API/FamilyTreeAPI/Repository/PersonRepository.cs index 42f46ba..336946d 100644 --- a/API/FamilyTreeAPI/Repository/PersonRepository.cs +++ b/API/FamilyTreeAPI/Repository/PersonRepository.cs @@ -214,6 +214,27 @@ public partial class PersonRepository : IPerson Person rval = await _context.Persons.FindAsync(id); return rval; } + + private string getStyleClass(string va) + { + string result = ""; + //result = "bg-indigo-500 text-black"; + + if (va == "T") + { + result = "bg-indigo-500 text-white"; + } + else if (va == "P") + { + //result = "bg-purple-500 text-black"; + result = "bg-purple-500 text-black"; + } + else if (va == "C") + { + result = "bg-teal-500 text-white"; + } + return result; + } public async Task>> GetByFamilyAsync(int id) { int statuscode = 0; @@ -241,6 +262,8 @@ public partial class PersonRepository : IPerson node.Label = item.FirstName; node.Key = item.Id.ToString(); node.Type = type; + node.Icon = "T"; + node.StyleClass = getStyleClass(node.Icon); node.Expanded = true; node.Data = item.Sex; statuscode = 1; @@ -300,13 +323,13 @@ public partial class PersonRepository : IPerson { if (data == "M") { - fatherId = relate.PersonId; - motherId = relate.RelatePersonId; + fatherId = relate.RelatePersonId; + motherId = relate.PersonId; } else { - fatherId = relate.RelatePersonId; - motherId = relate.PersonId; + fatherId = relate.PersonId; + motherId = relate.RelatePersonId; } } //get children @@ -321,7 +344,7 @@ public partial class PersonRepository : IPerson pe = await GetPerson(motherId); pName = pe.FirstName; key = motherId.ToString(); - data = "F"; + data = pe.Sex; } } else @@ -331,12 +354,14 @@ public partial class PersonRepository : IPerson pe = await GetPerson(fatherId); pName = pe.FirstName; key = fatherId.ToString(); - data = "M"; + data = pe.Sex; } } citem = new TreeNode(); citem.Label = pName; + citem.Icon = "P"; + citem.StyleClass = getStyleClass(citem.Icon); citem.Expanded = true; citem.Data = data; citem.Type = type; @@ -349,6 +374,8 @@ public partial class PersonRepository : IPerson child = new TreeNode(); child.Expanded = true; child.Type = type; + child.Icon = "C"; + child.StyleClass = getStyleClass(child.Icon); child.Label = dto.FirstName; child.Key = dto.Id.ToString(); child.Data = dto.Sex; diff --git a/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs b/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs index ef4e879..64afaff 100644 --- a/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs +++ b/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs @@ -12,6 +12,7 @@ public partial class PersonRepository treeNode.Label = model.FirstName; treeNode.Data = model.Id.ToString(); treeNode.Key = model.Id.ToString(); + treeNode.Expanded = false; treeNode.Children = new(); return treeNode; } diff --git a/UI/angular.json b/UI/angular.json index 969baf6..88a0fad 100644 --- a/UI/angular.json +++ b/UI/angular.json @@ -13,6 +13,9 @@ "build": { "builder": "@angular/build:application", "options": { + "allowedCommonJsDependencies": [ + "moment", "file-saver", "xlsx" + ], "browser": "src/main.ts", "tsConfig": "tsconfig.app.json", "assets": [ diff --git a/UI/package-lock.json b/UI/package-lock.json index 37cbeba..538fb26 100644 --- a/UI/package-lock.json +++ b/UI/package-lock.json @@ -16,6 +16,7 @@ "@angular/router": "^20.1.0", "@primeuix/themes": "^1.2.1", "@tailwindcss/postcss": "^4.1.11", + "file-saver": "^2.0.5", "moment": "^2.30.1", "postcss": "^8.5.6", "primeicons": "^7.0.0", @@ -23,12 +24,14 @@ "rxjs": "~7.8.0", "tailwindcss": "^4.1.11", "tailwindcss-primeui": "^0.6.1", - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "xlsx": "^0.18.5" }, "devDependencies": { "@angular/build": "^20.1.1", "@angular/cli": "^20.1.1", "@angular/compiler-cli": "^20.1.0", + "@types/file-saver": "^2.0.7", "@types/jasmine": "~5.1.0", "jasmine-core": "~5.8.0", "karma": "~6.4.0", @@ -3864,6 +3867,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/jasmine": { "version": "5.1.8", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.8.tgz", @@ -3925,6 +3935,15 @@ "node": ">= 0.6" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -4400,6 +4419,19 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -4535,6 +4567,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4718,6 +4759,18 @@ "node": ">= 0.10" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5417,6 +5470,12 @@ } } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -5503,6 +5562,15 @@ "node": ">= 0.6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -9232,6 +9300,18 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/ssri": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", @@ -9959,6 +10039,24 @@ "node": ">= 8" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -10164,6 +10262,27 @@ } } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/UI/package.json b/UI/package.json index 7d027e1..24e9881 100644 --- a/UI/package.json +++ b/UI/package.json @@ -28,6 +28,7 @@ "@angular/router": "^20.1.0", "@primeuix/themes": "^1.2.1", "@tailwindcss/postcss": "^4.1.11", + "file-saver": "^2.0.5", "moment": "^2.30.1", "postcss": "^8.5.6", "primeicons": "^7.0.0", @@ -35,12 +36,14 @@ "rxjs": "~7.8.0", "tailwindcss": "^4.1.11", "tailwindcss-primeui": "^0.6.1", - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "xlsx": "^0.18.5" }, "devDependencies": { "@angular/build": "^20.1.1", "@angular/cli": "^20.1.1", "@angular/compiler-cli": "^20.1.0", + "@types/file-saver": "^2.0.7", "@types/jasmine": "~5.1.0", "jasmine-core": "~5.8.0", "karma": "~6.4.0", diff --git a/UI/src/app/app.config.ts b/UI/src/app/app.config.ts index 091094c..7a8fbcc 100644 --- a/UI/src/app/app.config.ts +++ b/UI/src/app/app.config.ts @@ -30,3 +30,6 @@ export const appConfig: ApplicationConfig = { provideRouter(routes) ] }; +/* +ng build --base-href "/FamilyTreeUI/" -c production +*/ \ No newline at end of file diff --git a/UI/src/app/mythem.ts b/UI/src/app/mythem.ts index 3728d2a..884d922 100644 --- a/UI/src/app/mythem.ts +++ b/UI/src/app/mythem.ts @@ -3,10 +3,22 @@ //mypreset.ts import { definePreset } from '@primeuix/themes'; import Aura from '@primeuix/themes/aura'; +import { primitive } from '@primeuix/themes/aura/base'; const MyPreset = definePreset(Aura, { semantic: { colorScheme: { + primitive: { + cyan: { + 50: '{cyan.50}', + 100: '{cyan.100}', + 200: '{cyan.200}', + 300: '{cyan.300}', + 400: '{cyan.400}', + 500: '{cyan.500}', + } + + }, primary: { 50: '{zinc.50}', 100: '{zinc.100}', diff --git a/UI/src/app/person/family.orga.html b/UI/src/app/person/family.orga.html index 7b1c112..5bd8736 100644 --- a/UI/src/app/person/family.orga.html +++ b/UI/src/app/person/family.orga.html @@ -3,7 +3,8 @@
- + +
diff --git a/UI/src/app/person/family.orga.ts b/UI/src/app/person/family.orga.ts index 6389976..9cdd606 100644 --- a/UI/src/app/person/family.orga.ts +++ b/UI/src/app/person/family.orga.ts @@ -59,43 +59,67 @@ export class FamilyOrga implements OnInit, OnDestroy{ if (item != undefined) this.person = item; this.loadPersonFamilyTree(id); - // this. populateTree(); + //this.populateTree(); } populateTree() : void { - const tree = [ - { - label: 'F.C Barcelona', + const data: TreeNode[] = [ + { expanded: true, + type: 'person', + styleClass: 'bg-indigo-500 text-white', + data: { + image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png', + name: 'Amy Elsner', + title: 'CEO' + }, children: [ { - label: 'Argentina', expanded: true, + type: 'person', + styleClass: 'bg-purple-500 text-white', + data: { + image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png', + name: 'Anna Fali', + title: 'CMO' + }, children: [ { - label: 'Argentina' + label: 'Sales', + styleClass: 'bg-purple-500 text-white', + style: ' border-radius: 12px' }, { - label: 'France' + label: 'Marketing', + styleClass: 'bg-purple-500 text-white', + style: ' border-radius: 12px' } ] }, { - label: 'France', expanded: true, + type: 'person', + styleClass: 'bg-teal-500 text-white', + data: { + image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png', + name: 'Stephen Shaw', + title: 'CTO' + }, children: [ { - label: 'France' + label: 'Development', + styleClass: 'bg-teal-500 text-white' }, { - label: 'Morocco' + label: 'UI/UX Design', + styleClass: 'bg-teal-500 text-white' } ] } ] } ]; - this.familyTree.set(tree); + this.familyTree.set(data); } loadPersonFamilyTree(id: number): void { const relationShip$ = this.personService.loadPersonFamily(id); diff --git a/UI/src/app/person/family.tree.html b/UI/src/app/person/family.tree.html index 15449ee..905b0be 100644 --- a/UI/src/app/person/family.tree.html +++ b/UI/src/app/person/family.tree.html @@ -2,18 +2,15 @@

Family Tree by Person has Father

-
-
- - -
+
+
+ + +
- - -
+ +
diff --git a/UI/src/app/person/family.tree.ts b/UI/src/app/person/family.tree.ts index bc27239..1d5bdcf 100644 --- a/UI/src/app/person/family.tree.ts +++ b/UI/src/app/person/family.tree.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef} from '@angular/core'; +import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core'; import { StaffView ,StaffSearch, Person } from '../models'; import { OrganizationChartModule } from 'primeng/organizationchart'; @@ -17,18 +17,22 @@ import { InputTextModule } from 'primeng/inputtext'; import { DialogService } from 'primeng/dynamicdialog'; import { PersonEdit } from './person.edit'; import { MessageService } from 'primeng/api'; +import { CheckboxModule } from 'primeng/checkbox'; import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; import { Utils } from '../shares'; @Component({ selector: 'family-tree', templateUrl: './family.tree.html', - imports:[TableModule,FormsModule,TreeModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule], + imports:[TableModule,FormsModule,TreeModule,CheckboxModule, + OrganizationChartModule,CommonModule, + FormsModule, + ButtonModule,InputTextModule], styleUrls: ['./family.tree.css'], providers: [DialogService] }) export class FamilyTree implements OnInit, OnDestroy{ - + isFather = signal(true); private subscription:Subscription = new Subscription(); firstname = ''; selectedNode!: TreeNode; @@ -73,25 +77,8 @@ export class FamilyTree implements OnInit, OnDestroy{ this.authenticationService.isReport = false; const prev = this.personService.searchCriteria; let goload = true; - if (prev.lastName !== '') - { - this.lastname = prev.lastName; - goload = true; - } - if (prev.firstName !== '') - { - this.firstname = prev.firstName; - goload = true; - } - if (prev.email !== '') - { - this.email = prev.email; - goload = true; - } - if (goload) - { - this.search(); - } + this.load(); + } getActive(active:boolean):string { let result = 'false-icon pi-times-circle'; @@ -99,15 +86,13 @@ export class FamilyTree implements OnInit, OnDestroy{ result = 'true-icon pi-check-circle'; return result; } - search():void { - const canSearch = true; // this.canSearch(); - if (canSearch) - { + load():void { + let father = this.isFather(); this.loading = true; //const criteria = this.getSearchCiteria(); // this.personService.searchCriteria = criteria; this.subscription.add( - this.personService.loadPersonFamilyTree(true, false).subscribe( + this.personService.loadPersonFamilyTree(father, !father).subscribe( { next: x => { if (x.statusCode == 1) @@ -126,31 +111,7 @@ export class FamilyTree implements OnInit, OnDestroy{ console.log("error ", e); } }) - ); - - /* - this.personService.searchPersons(criteria).subscribe( { - next: result => { - // console.log(this.msg + "search load Data", result); - this.familyList = result.data; - this.familyTree = Utils.populateNode( "fatherId", this.familyList); - this.loading = false; - this.cd.detectChanges(); - } - }, - - error: e => { - const message = e || e.message; - // this.toastr.error(message); - this.loading = false; - console.log("error ", e); - } - }) - ); - */ - - - } + ); } nodeSelect(event: TreeNodeSelectEvent) { diff --git a/UI/src/app/person/familylist.html b/UI/src/app/person/familylist.html index e81839a..024e19a 100644 --- a/UI/src/app/person/familylist.html +++ b/UI/src/app/person/familylist.html @@ -21,20 +21,23 @@
- +
-
+ diff --git a/UI/src/app/person/familylist.ts b/UI/src/app/person/familylist.ts index edc2923..007309d 100644 --- a/UI/src/app/person/familylist.ts +++ b/UI/src/app/person/familylist.ts @@ -20,6 +20,10 @@ import { IconFieldModule } from 'primeng/iconfield'; import { InputIconModule } from 'primeng/inputicon'; import { Menu, MenuModule } from 'primeng/menu'; import { FamilyOrga } from './family.orga'; + +import { saveAs } from 'file-saver'; +import * as XLSX from 'xlsx'; + @Component({ selector: 'family-list', templateUrl: './familylist.html', @@ -88,6 +92,20 @@ export class FamilyList implements OnInit, OnDestroy{ ]; } + exportExport() : void { + this.ExcelExport(this.familyList(), 'family_export'); + } + + ExcelExport(data: any, fileName:string) :void + { + const worksheet = XLSX.utils.json_to_sheet(data); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); + const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); + const blob = new Blob([excelBuffer], { type: 'application/octet-stream' }); + saveAs(blob, `${fileName}.xlsx`); + } + actionClick(id: number, event:Event): void { // console.log("action edit "+ id); this.selectId = id; diff --git a/UI/src/app/shares/utils.ts b/UI/src/app/shares/utils.ts index 0ebe03f..047e628 100644 --- a/UI/src/app/shares/utils.ts +++ b/UI/src/app/shares/utils.ts @@ -166,6 +166,7 @@ static formatNode(item:Person): TreeNode let tree_node_child: TreeNode[]; let node:TreeNode; let item: Person; + const children = childressNodes.filter(x => x[proName] == pid); for (let c = 0; c < children.length; c++) { diff --git a/document_Install.docx b/document_Install.docx new file mode 100644 index 0000000..3c1fc81 Binary files /dev/null and b/document_Install.docx differ diff --git a/document_table.docx b/document_table.docx index f206605..59feaf8 100644 Binary files a/document_table.docx and b/document_table.docx differ