From 6b70902d46ba7e771f0c3a273f8bb9a8be3c4f30 Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Date: Wed, 23 Apr 2025 14:28:10 +0530 Subject: [PATCH] sitebuilder --- .../editstepper.component.css | 52 + .../editstepper.component.css.map | 1 + .../editstepper.component.html | 399 ++++ .../editstepper.component.scss | 46 + .../editstepper.component.spec.ts | 25 + .../editstepper.component.ts | 369 +++ .../VisaOrderWorkflow/play.ts | 9 + .../Visa_application.component.html | 176 +- .../Visa_application.component.ts | 55 +- .../Visa_application.service.ts | 91 +- .../SiteBuilderGrid/SiteTree.component.html | 3 + .../SiteBuilderGrid/SiteTree.component.ts | 3 +- .../SiteBuilderGrid/SiteTree.service.ts | 5 +- .../main/fnd/SiteTreeBuilder/TreeNode.zip | Bin 0 -> 8794 bytes .../TreeNode/tree-node.component.css | 1275 +++++----- .../TreeNode/tree-node.component.html | 349 ++- .../TreeNode/tree-node.component.ts | 1263 +++++----- .../main/fnd/SiteTreeBuilder/WireframesUi.zip | Bin 0 -> 15343 bytes .../WireframesUi/common-css.ts | 2068 ++++++++++++++++- .../wireframe-renderer.component.ts | 103 +- .../tree-visualizer.component.html | 19 +- .../tree-visualizer.component.ts | 14 +- .../app/modules/main/main-routing.module.ts | 4 + .../src/app/modules/main/main.module.ts | 2 + .../src/environments/environment.prod.ts | 1 + .../src/environments/environment.ts | 8 +- .../angular-clarity-master/src/index.html | 1 + 27 files changed, 4631 insertions(+), 1710 deletions(-) create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css.map create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.html create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.scss create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.spec.ts create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.ts create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/play.ts create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode.zip create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css new file mode 100644 index 0000000..3f95205 --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css @@ -0,0 +1,52 @@ +.horizontal { + width: 25%; + padding: 5px; +} + +.horizontal1 { + width: 50%; + padding: 10px; +} + +.middle { + width: 33%; + padding: 10px; +} + +.middle1 { + width: 75%; + padding: 10px; +} + +.full { + width: 100%; + padding: 10px; +} + +input[type=text], [type=date], select { + width: 100%; + padding: 12px 20px; + margin: 8px 0; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +.required-field { + color: red; +} + +.center { + text-align: center; +} + +.center { + color: blue; +} + +@media (max-width: 600px) { + .horizontal, .middle, .horizontal1, .middle1 { + width: 100%; + } +}/*# sourceMappingURL=editstepper.component.css.map */ \ No newline at end of file diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css.map b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css.map new file mode 100644 index 0000000..c9f68f7 --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["editstepper.component.scss","editstepper.component.css"],"names":[],"mappings":"AAAA;EACE,UAAA;EACA,YAAA;ACCF;;ADCA;EACE,UAAA;EACA,aAAA;ACEF;;ADAA;EACE,UAAA;EACA,aAAA;ACGF;;ADDA;EACE,UAAA;EACA,aAAA;ACIF;;ADDA;EACE,WAAA;EACA,aAAA;ACIF;;ADDA;EACE,WAAA;EACA,kBAAA;EACA,aAAA;EACA,qBAAA;EACA,sBAAA;EACA,kBAAA;EACA,sBAAA;ACIF;;ADFA;EACE,UAAA;ACKF;;ADHA;EACE,kBAAA;ACMF;;ADHA;EACE,WAAA;ACMF;;ADJA;EACA;IACA,WAAA;ECOE;AACF","file":"editstepper.component.css"} \ No newline at end of file diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.html new file mode 100644 index 0000000..bdafd0d --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.html @@ -0,0 +1,399 @@ + + +
+ + + + + + + +
+ +
+
+
+
+

Customer Information

+
+ +
+ + + + Step 0 + Application Update + Datagrid + Create/Add Customer Information
Information Of + Customer
+
+ + + Step 1 + Document Upload + Upload Your
+ Document.
+ +
+ + + + Step 2 + Review And
Confirmation
+ + Finish . + +
+
+
+
+
+
+ + +
+
+

Customer Information Form

+

Update {{entryForm.name}}

+
+ +
+ + + +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + + + +
+ + +
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+
+ *This field + is Required
+
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
* Email must be a + valid + email address +
+
+
+ +
+ + +
+
+ * Please + Follow your pattern,+91 Enter 10 digit Mobile Number. +
+
+
+ +
+ + +
+ +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + +
+ + +
+ + + + +
+ + +
+ + + + + +
+ + +
+ + + + + +
+ + +
+ +
+ + +
+ + +
+ +
+ +
+ +
+
+ + + + + +
+
+
+ +
+ +
+
+ + +
+
+

File Upload

+
+ + +
+ + + +
List of Uploaded file
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
No FileFile NamePreviewCancel
+ {{attach.uploadedfile_name}} File Preview + + + +
+
+ +
+
+ + + + + +
+
+ +
+
+
+ + + +
+
+

Customer Information Review And Confirmation

+
+ + +
+
+
Customer Details:
+

Name: {{ rowSelected.name }}

+

Phone: {{ rowSelected.phone }}

+

Email: {{ rowSelected.email }}

+
+
+ + +
+
+
Uploaded Document:
+

{{ uploadedFileName }}

+
+
+ +
+ + + +
+ +
+
+
+ + + +
+
\ No newline at end of file diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.scss b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.scss new file mode 100644 index 0000000..35a9e5a --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.scss @@ -0,0 +1,46 @@ +.horizontal{ + width: 25%; + padding: 5px; +} +.horizontal1{ + width: 50%; + padding: 10px; +} +.middle{ + width: 33%; + padding: 10px; +} +.middle1{ + width: 75%; + padding: 10px; +} + +.full{ + width: 100%; + padding: 10px; +} + +input[type=text],[type=date], select { + width: 100%; + padding: 12px 20px; + margin: 8px 0; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} +.required-field{ + color: red; +} +.center { + text-align: center; + +} +.center{ + color: blue; +} +@media (max-width: 600px){ +.horizontal,.middle,.horizontal1,.middle1 { +width: 100%; +}} + diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.spec.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.spec.ts new file mode 100644 index 0000000..99c22bb --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditstepperComponent } from './editstepper.component'; + +describe('EditstepperComponent', () => { + let component: EditstepperComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ EditstepperComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditstepperComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.ts new file mode 100644 index 0000000..0e3560c --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component.ts @@ -0,0 +1,369 @@ +import { Component, OnInit } from '@angular/core'; + + +import { ToastrService } from 'ngx-toastr'; +import { ActivatedRoute, Router } from '@angular/router'; +import { College } from 'src/app/models/fnd/play'; +import { student } from 'src/app/models/fnd/Studentadd'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ValidationError } from 'src/app/models/fnd/ValidationError'; +import { Visa_applicationservice } from '../Visa_application.service'; +@Component({ + selector: 'app-editstepper', + templateUrl: './editstepper.component.html', + styleUrls: ['./editstepper.component.scss'] +}) +export class EditstepperComponent implements OnInit { + updated = false; + // college: College; + stringJson: any; + customerId: string = ''; + selectedFile!: File; + fileName = ''; + + id: number; + errorFields: ValidationError[] = []; + appToUpdate: College = null; + trained = false; + + json: string = ""; + luisApp = + { + name: '', + created: 1, + trained: 1, + tested: 1, + updated: 1, + published: 1, + + }; + // Layout direction changing + layout = { + direction: "vertical", + block1: "clr-col-lg-3 clr-col-12 ", + block2: "clr-col-lg-9 clr-col-12 ", + }; + + timelineStyle = { + step0: { state: "current", open: true, failed: false }, + step1: { state: "not-started", open: false, failed: false }, + step2: { state: "not-started", open: false, failed: false }, + }; + + + public entryForm: FormGroup; + submitted = false; + rowSelected: any = {}; + modalcomplete = false; + + constructor(private mainService: Visa_applicationservice, + + private _fb: FormBuilder, + private router: Router, + private route: ActivatedRoute, + private toastr: ToastrService,) { } + + ngOnInit(): void { + // this.college = new College(); + this.id = this.route.snapshot.params["id"]; + console.log("update with id = ", this.id); + + this.entryForm = this._fb.group({ + name: [null], + + description: [null], + + active: [false], + + visa_entry_type: [null], + + visa_duration: [null], + + visa_processing: [null], + + travel_start_date: [null], + + travel_end_date: [null], + + passport_number: [null, [Validators.required]], + + passport_issue_date: [null], + + passport_expiry_date: [null], + + email: [null], + + phone_number: ['+91'], + + birth_place: [null], + + date_of_birth: [null], + + gender: [null], + + profession: [null], + + visa_cost: [null, { updateOn: 'blur' }], + + referrer: [null], + + nationality: [null], + + supplier: [null], + + agent: [null], + + }); + } + + // Change to Horizontal Layout + changeToHorizonTal() { + this.layout = { + direction: "horizontal", + block1: "clr-col-lg-12 clr-col-12 height container", + block2: "clr-col-lg-12 clr-col-12 container", + } + } + // Change to Vertical Layout + changeToVertical() { + this.layout = { + direction: "vertical", + block1: "clr-col-lg-3 clr-col-12 ", + block2: "clr-col-lg-9 clr-col-12 ", + } + } + getById(id: number) { + // this.mainService.getBywfId(id).subscribe( + // (data) => { + // this.college = data; + // }, + // (err) => { + // console.log(err); + // } + // ); + // this.currentservice.getById(id).subscribe( + // (data) => { + // this.student = data; + + // var current = JSON.parse(data.current_json); + // this.timelineStyle = current; + // console.log("data", data); + // }, + // (err) => { + // console.log(err); + // } + // ); + + + } + + + onSave() { + console.log('Form Submitted:', this.entryForm.value); + this.submitted = true; + if (this.entryForm.invalid) { + + console.log('invalid form ..'); + // Log all form errors + Object.keys(this.entryForm.controls).forEach(field => { + const control = this.entryForm.get(field); + if (control && control.invalid) { + console.log(`Error in field: ${field}`, control.errors); + } + } + ); + + + return; + } + this.onCreate(); + } + + onCreate() { + this.mainService.create(this.entryForm.value).subscribe( + (data) => { + console.log('adding data ', data); + this.customerId = data.id; + + console.log('id is ', this.customerId); + + + if (data || data.status >= 200 && data.status <= 299) { + this.toastr.success("Added Successfully"); + } + setTimeout(() => { + this.ngOnInit(); + }, 500); + + }, (error) => { + console.log(error); + if (error.status >= 200 && error.status <= 299) { + // this.toastr.success("Added Succesfully"); + } + if (error.status >= 400 && error.status <= 499) { + this.toastr.error("Not Added"); + } + if (error.status >= 500 && error.status <= 599) { + this.toastr.error("Not Added"); + } + }); + setTimeout(() => { + this.ngOnInit(); + }, 500); + } + + + onnext() { + this.router.navigate(["../../main/workflow"], { relativeTo: this.route }); + } + reset() { + this.json = ""; + this.luisApp = + { + name: '', + trained: 1, + tested: 1, + updated: 1, + published: 1, + + + created: 1, + + }; + + this.timelineStyle = { + step0: { state: "current", open: true, failed: false }, + step1: { state: "not-started", open: false, failed: false }, + step2: { state: "not-started", open: false, failed: false }, + + }; + } + current() { + console.log(this.timelineStyle) + this.stringJson = JSON.stringify(this.timelineStyle); + console.log("String json object :", this.stringJson); + + // this.mainService.update(this.id, this.college, this.stringJson).subscribe( + // (data) => { + // console.log(data); + // }, + + // ); + } + + onFileUpload(): void { + + for (let i = 0; i < this.selectedfileupload_field.length; i++) { + + this.mainService.uploadDocument(this.customerId, 'visa order', this.selectedfileupload_field[i]).subscribe(uploaddata => { + console.log(uploaddata); + + console.log('id is ', this.customerId); + + + if (uploaddata || uploaddata.status >= 200 && uploaddata.status <= 299) { + // alert('File uploaded successfully!'); + this.toastr.success("File uploaded successfully!"); + + } + setTimeout(() => { + this.ngOnInit(); + }, 500); + + }, (error) => { + console.log(error); + if (error.status >= 200 && error.status <= 299) { + // this.toastr.success("Added Succesfully"); + } + if (error.status >= 400 && error.status <= 499) { + this.toastr.error("Not Added"); + } + if (error.status >= 500 && error.status <= 599) { + this.toastr.error("Not Added"); + } + } + + ) + } + + this.selectedFile = null; + } + + + filePreviewfileupload_field: string | ArrayBuffer | null = null; + FileDatafileupload_field: { uploadedfile_name?: any, filePreview: string | ArrayBuffer | null }[] = []; // Initialize the array + selectedfileupload_field: File[] = []; + public onFileChangedfileupload_field(event, index) { + const files = event.target.files; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + this.FileDatafileupload_field[index].uploadedfile_name = files[i].name; + this.selectedfileupload_field.push(files[i]); + if (file.type.startsWith('file/')) { + const reader = new FileReader(); + reader.onload = (e) => { + // Set the file preview source + const filePreview = e.target?.result as string; + this.FileDatafileupload_field[index] = { + ...this.FileDatafileupload_field[index], // Preserve existing properties + filePreview: filePreview // Update only the filePreview property + }; + }; + reader.readAsDataURL(file); + } + } + } + onAddLinesfileupload_field() { + this.FileDatafileupload_field.push({ + uploadedfile_name: "", + filePreview: "", + // f3: "", + }); + } + deleteRowfileupload_field(index, id) { + this.FileDatafileupload_field.splice(index, 1); + + if (id) { + this.mainService.uploadfiledeletefileupload_field(id).subscribe(data => { + console.log(data); + }) + } + } + + + + + + + isValidemail(email: string): boolean { + const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; + return emailPattern.test(email); + } + + isValidPhone_number(phone: string): boolean { + const phonePattern = /^(\+[1-9][0-9]{0,2})?[1-9][0-9]{9}$/; + return phonePattern.test(phone); + } + + + + + + updategender(gender: string): void { + this.entryForm.get('gender').setValue(gender); + } + + updategenderEdit(gender: string): void { this.rowSelected.gender = gender } + + + + + //currency field start + formatCurrencyvisa_cost() { + // Format the currency with two decimal places + this.rowSelected.visa_cost = Number(this.rowSelected.visa_cost).toFixed(2); + // Remove commas from the formatted currency + this.rowSelected.visa_cost = this.rowSelected.visa_cost?.replace(/,/g, ''); + } + //currency field end +} diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/play.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/play.ts new file mode 100644 index 0000000..3c5b927 --- /dev/null +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/play.ts @@ -0,0 +1,9 @@ +export class College { + public studentid: number; + public wf_instance_id:number; + public studentname: string; + public department: string; + public joiningDate: string; + public phone: number; + public emailId:string; +} diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/Visa_application.component.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/Visa_application.component.html index b78e768..704f659 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/Visa_application.component.html +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/BuilderComponents/vpspack/Visa_application/Visa_application.component.html @@ -12,7 +12,10 @@ shape="bars"> - @@ -349,7 +352,8 @@ [style.color]="item.conditionValue == app[transform(item.fieldtext) ] ? item.conditiontextcolor : item.textcolor"> File Preview + [style.height]="item.imagewidth !== '' ? item.imagewidth + 'px' : '100px'"> + @@ -371,90 +375,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { + callLlm(data: any): Observable { return this.http.post(`${this.nodeURL}/chatMemory`, data); } @@ -57,6 +57,5 @@ export class SiteTreeservice { - // updateaction } \ No newline at end of file diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode.zip b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode.zip new file mode 100644 index 0000000000000000000000000000000000000000..59cc834d77c1c19b37dbb343833876caa94d4bf0 GIT binary patch literal 8794 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 literal 0 HcmV?d00001 diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.css b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.css index 7c135f8..ea29224 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.css +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.css @@ -1,744 +1,653 @@ -/* .node-card { - border: 1px solid #ccc; - padding: 12px; +.visual-editor-container { + display: flex; + flex-direction: column; + height: 100vh; + position: relative; + overflow: hidden; +} + +.fixed-grid-container { + flex: 1; + position: relative; + height: calc(100vh - 200px); + overflow: hidden; +} + +.editor-content { + width: 100%; + height: 100%; + position: relative; +} + +.canvas-container { + width: 100%; + height: 100%; + position: relative; + overflow: hidden; + cursor: grab; +} + +.canvas-boundary { + width: 100%; + height: 100%; + position: relative; + overflow: hidden; +} + +.sitemap-grid { + position: absolute; + top: 0; + left: 0; + transform-origin: 0 0; + min-width: 100%; + min-height: 100%; + transition: transform 0.1s ease-out; +} + +.grid-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: radial-gradient(circle, #e0e0e0 1px, transparent 1px); + background-size: 25px 25px; /* Slightly larger grid size */ + z-index: -1; +} + +.tree-grid { + position: relative; + padding: 100px; + display: flex; + flex-direction: column; + align-items: center; + min-width: 5000px; /* Increased from 2000px */ + min-height: 3000px; /* Increased from 1000px */ +} + +.tree-row { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.node-grid { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 30px; +} + +.node-container { + display: flex; + flex-direction: column; + align-items: center; + position: relative; + margin: 0 25px; /* Increased from 15px to 25px */ +} + +.node-card { background: white; - border-radius: 6px; - margin: 8px auto; - width: 300px; - box-shadow: 0 2px 8px rgba(0,0,0,0.1); + border: 1px solid #ddd; + border-radius: 10px; + padding: 18px; + min-width: 220px; + max-width: 320px; + box-shadow: 0 3px 8px rgba(0,0,0,0.08); + margin-bottom: 25px; + position: relative; + transition: all 0.3s ease; +} + +.node-card:hover { + box-shadow: 0 6px 12px rgba(0,0,0,0.15); + transform: translateY(-2px); +} + +.node-card.selected { + border: 2px solid #007bff; + box-shadow: 0 0 0 4px rgba(0, 123, 255, 0.2); +} + +.home-node { + background: #f8f9fa; + border: 1px solid #ccc; +} + +.node-header { + display: flex; + align-items: center; + padding-bottom: 12px; + border-bottom: 1px solid #eee; + margin-bottom: 12px; +} + +.node-icon { + margin-right: 12px; + color: #007bff; + font-size: 16px; +} + + +.node-title { + font-weight: 600; + font-size: 16px; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #333; +} + +.connection-line { + width: 2px; + height: 35px; /* Increased from 20px to 35px */ + background-color: #ccc; + position: absolute; + top: -35px; /* Adjusted to match height */ + z-index: -1; } .children-container { - display: flex; - flex-direction: column; - align-items: center; - position: relative; - margin-top: 20px; -} - - - -.children { display: flex; flex-wrap: wrap; - gap: 20px; justify-content: center; -} - - -.connector-line { - position: absolute; - top: -30px; - height: 100px; - width: 100%; -} */ - - -/* visual-site-editor.component.scss */ -/* -.visual-editor-container { - display: flex; - flex-direction: column; - height: 100vh; - font-family: 'Inter', sans-serif; - background-color: #f5f5f5; -} - -.editor-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 20px; - background-color: #fff; - border-bottom: 1px solid #e0e0e0; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - - h2 { - margin: 0; - font-weight: 600; - font-size: 18px; - color: #333; - } - - .actions { - display: flex; - gap: 10px; - } -} - -.json-input-container { - display: flex; - justify-content: center; - align-items: center; - flex: 1; - padding: 32px; - - .card { - background-color: white; - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - width: 100%; - max-width: 600px; - padding: 24px; - - h3 { - margin-top: 0; - margin-bottom: 16px; - font-weight: 600; - font-size: 16px; - color: #333; - } - - .json-textarea { - width: 100%; - height: 300px; - padding: 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-family: monospace; - margin-bottom: 16px; - resize: vertical; - } - - .error-message { - color: #dc3545; - margin-bottom: 16px; - font-size: 14px; - } - } -} - -.visual-editor { - flex: 1; - display: flex; - overflow: hidden; -} - -.sitemap-container { - flex: 1; - padding: 20px; - overflow-y: auto; - display: flex; - flex-direction: column; - align-items: center; -} - -.sitemap-row { - display: flex; - justify-content: center; - width: 100%; position: relative; - margin-bottom: 30px; - - &.multi-node-row { - justify-content: space-around; - flex-wrap: wrap; - gap: 15px; - } + margin-top: 35px; /* Increased from 15px to 35px */ + min-width: 100%; + gap: 20px; /* Added gap for consistent spacing */ } -.connector-vertical { +.children-container::before { + content: ''; position: absolute; - top: -30px; + top: -35px; /* Increased from -15px to -35px */ left: 50%; + height: 35px; /* Increased from 15px to 35px */ width: 2px; - height: 30px; - background-color: #aaa; - z-index: 0; + background-color: #ccc; } -.connector-horizontal { - position: absolute; - top: -15px; - left: 20%; - right: 20%; - height: 2px; - background-color: #aaa; - z-index: 0; +.sections-container { + min-height: 30px; + padding: 5px 0; + width: 100%; } -.connector-sub { - position: absolute; - top: -15px; - width: 80%; - height: 2px; - background-color: #aaa; - z-index: 0; - left: 10%; -} - -.sitemap-node { - background-color: white; - border-radius: 8px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); - min-width: 180px; - max-width: 300px; - position: relative; - border: 2px solid transparent; - transition: all 0.2s ease; - - &.selected { - border-color: #4dabf7; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.2); - } - - &.root-node { - background-color: #f0f0f0; - margin-bottom: 20px; - - .node-header { - background-color: #e0e0e0; - } - } - - &.child-node { - .node-header { - background-color: #f8f9fa; - } - } - - &.grandchild-node { - .node-header { - background-color: #f1f3f5; - } - } - - .node-header { - padding: 10px 12px; - border-bottom: 1px solid #eee; - border-radius: 6px 6px 0 0; - font-weight: 600; - font-size: 14px; - color: #333; - cursor: pointer; - - i { - margin-right: 6px; - color: #666; - } - } - - .node-content { - padding: 8px; - } -} - -.section-item { +.section-card { + background-color: #f9f9f9; border: 1px solid #eee; border-radius: 4px; - margin-bottom: 8px; - padding: 8px; + padding: 12px; + margin-bottom: 12px; + position: relative; cursor: pointer; transition: all 0.2s ease; - - &:hover { - background-color: #f9f9f9; - } - - &.selected { - border-color: #4dabf7; - background-color: #e7f5ff; - } - - .section-title { - font-weight: 500; - font-size: 13px; - color: #333; - margin-bottom: 4px; - } - - .section-content { - font-size: 12px; - color: #666; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } } -.properties-panel { - width: 320px; +.section-card:hover { + border-color: #007bff; + box-shadow: 0 3px 8px rgba(0,0,0,0.1); + background-color: #f5f9ff; +} + +.section-title { + font-weight: 600; + font-size: 14px; + margin-bottom: 8px; + color: #333; +} + +.section-content { + font-size: 13px; + color: #555; + max-height: 60px; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.4; +} + +.section-actions { + position: absolute; + top: 5px; + right: 5px; + display: none; +} + +.section-card:hover .section-actions { + display: block; +} + +.section-action-btn { + background: none; + border: none; + color: #777; + font-size: 12px; + cursor: pointer; + padding: 3px; +} + +.section-action-btn:hover { + color: #007bff; +} + +.add-section-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 10px; + background-color: #f5f9ff; + border: 1px dashed #99c2ff; + border-radius: 6px; + cursor: pointer; + color: #007bff; + font-size: 14px; + margin-top: 12px; + transition: all 0.2s ease; +} + +.add-section-btn:hover { + background-color: #e6f0ff; + color: #0056b3; +} + +.add-section-hover-btn { + position: absolute; + bottom: -12px; + left: 50%; + transform: translateX(-50%); + width: 24px; + height: 24px; + background-color: #007bff; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + opacity: 0; + transition: opacity 0.2s ease, transform 0.2s ease; + z-index: 10; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); +} + + +.section-card:hover .add-section-hover-btn { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +.add-section-hover-btn:hover { + transform: translateX(-50%) scale(1.2); + background-color: #0056b3; +} +.node-actions { + display: flex; + justify-content: flex-end; + margin-top: 10px; + padding-top: 8px; + border-top: 1px solid #eee; +} + +.node-action-btn { + background: none; + border: none; + color: #777; + cursor: pointer; + padding: 5px; + margin-left: 5px; +} + +.node-action-btn:hover { + color: #007bff; +} + +.add-child-hover-btn { + display: none; +} + +.node-card:hover .add-child-hover-btn { + opacity: 1; +} + +.controls { + position: absolute; + bottom: 25px; + right: 25px; + display: flex; + flex-direction: column; + z-index: 100; +} + +.control-btn { + width: 45px; + height: 45px; + margin-bottom: 12px; + border-radius: 50%; background-color: white; - border-left: 1px solid #e0e0e0; - - .panel-header { - padding: 14px 20px; - border-bottom: 1px solid #e0e0e0; - - h3 { - margin: 0; - font-weight: 600; - font-size: 16px; - color: #333; - } - } - - .panel-content { - padding: 20px; - } - - .property-form { - .form-group { - margin-bottom: 16px; - - label { - display: block; - margin-bottom: 8px; - font-weight: 500; - font-size: 14px; - color: #555; - } - - input, select, textarea { - width: 100%; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-family: inherit; - font-size: 14px; - - &:focus { - outline: none; - border-color: #4dabf7; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.2); - } - } - - textarea { - resize: vertical; - min-height: 80px; - } - } - - .form-actions { - display: flex; - gap: 8px; - margin-bottom: 16px; - } - } + border: 1px solid #ddd; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: #333; + box-shadow: 0 3px 8px rgba(0,0,0,0.1); + transition: all 0.2s ease; +} + +.control-btn:hover { + background-color: #f5f9ff; + color: #007bff; + transform: scale(1.1); +} + + +.editor-form { + padding: 20px; + border-top: 1px solid #ddd; + max-height: 300px; + overflow-y: auto; + background-color: #f9f9f9; +} + +.editor-title { + font-size: 18px; + font-weight: 600; + margin-bottom: 15px; +} + +.form-group { + margin-bottom: 15px; +} + +.form-label { + display: block; + margin-bottom: 5px; + font-weight: 500; +} + +.form-textarea { + width: 100%; + min-height: 150px; + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + font-family: monospace; + resize: vertical; +} + +.form-actions { + display: flex; + justify-content: flex-end; + margin-top: 15px; + gap: 10px; } .btn { - padding: 8px 16px; + padding: 8px 15px; border-radius: 4px; - font-weight: 500; - font-size: 14px; cursor: pointer; + font-weight: 500; border: 1px solid transparent; transition: all 0.2s ease; - - &.primary { - background-color: #4dabf7; - color: white; - - &:hover { - background-color: #339deb; - } - - &:disabled { - background-color: #adb5bd; - cursor: not-allowed; - } - } - - &.secondary { - background-color: #f1f3f5; - border-color: #ddd; - color: #555; - - &:hover { - background-color: #e9ecef; - } - } - - &.sm { - padding: 6px 12px; - font-size: 13px; - } -} */ - - - -/* by harsh */ - -/* visual-site-editor.component.scss */ -.visual-editor-container { - display: flex; - flex-direction: column; - height: 100vh; - font-family: 'Inter', sans-serif; - background-color: #f5f5f5; } -.editor-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 20px; - background-color: #fff; - border-bottom: 1px solid #e0e0e0; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - - h2 { - margin: 0; - font-weight: 600; - font-size: 18px; - color: #333; - } - - .actions { - display: flex; - gap: 10px; - } +.btn-primary { + background-color: #007bff; + color: white; } -.json-input-container { - display: flex; - justify-content: center; - align-items: center; - flex: 1; - padding: 32px; - - .card { - background-color: white; - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - width: 100%; - max-width: 600px; - padding: 24px; - - h3 { - margin-top: 0; - margin-bottom: 16px; - font-weight: 600; - font-size: 16px; - color: #333; - } - - .json-textarea { - width: 100%; - height: 300px; - padding: 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-family: monospace; - margin-bottom: 16px; - resize: vertical; - } - - .error-message { - color: #dc3545; - margin-bottom: 16px; - font-size: 14px; - } - } +.btn-primary:hover { + background-color: #0069d9; } -.visual-editor { - flex: 1; - display: flex; - overflow: hidden; - min-height: 0; /* Important to make flex child respect parent height */ +.btn-secondary { + background-color: #6c757d; + color: white; } -.sitemap-container { - flex: 1; - padding: 20px; - overflow-y: auto; - display: flex; - flex-direction: column; - align-items: center; +.btn-secondary:hover { + background-color: #5a6268; } -.sitemap-row { - display: flex; - justify-content: center; +.form-control { + display: block; width: 100%; - position: relative; - margin-bottom: 30px; - - &.multi-node-row { - justify-content: space-around; - flex-wrap: wrap; - gap: 15px; - } + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } -.connector-vertical { - position: absolute; - top: -30px; - left: 50%; - width: 2px; - height: 30px; - background-color: #aaa; - z-index: 0; +.mb-3 { + margin-bottom: 1rem; } -.connector-horizontal { - position: absolute; - top: -15px; - left: 20%; - right: 20%; - height: 2px; - background-color: #aaa; - z-index: 0; -} - -.connector-sub { - position: absolute; - top: -15px; - width: 80%; - height: 2px; - background-color: #aaa; - z-index: 0; - left: 10%; -} - -.sitemap-node { +.offcanvas { + position: fixed; + top: 0; + right: -400px; + width: 400px; + height: 100%; background-color: white; - border-radius: 8px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); - min-width: 180px; - max-width: 300px; - position: relative; - border: 2px solid transparent; - transition: all 0.2s ease; - - &.selected { - border-color: #4dabf7; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.2); - } - - &.root-node { - background-color: #f0f0f0; - margin-bottom: 20px; - - .node-header { - background-color: #e0e0e0; - } - } - - &.child-node { - .node-header { - background-color: #f8f9fa; - } - } - - &.grandchild-node { - .node-header { - background-color: #f1f3f5; - } - } - - .node-header { - padding: 10px 12px; - border-bottom: 1px solid #eee; - border-radius: 6px 6px 0 0; - font-weight: 600; - font-size: 14px; - color: #333; - cursor: pointer; - - i { - margin-right: 6px; - color: #666; - } - } - - .node-content { - padding: 8px; - max-height: 200px; - overflow-y: auto; - } + box-shadow: -5px 0 15px rgba(0,0,0,0.1); + z-index: 1050; + transition: right 0.3s ease; + overflow-y: auto; } -.section-item { - border: 1px solid #eee; - border-radius: 4px; - margin-bottom: 8px; - padding: 8px; - cursor: pointer; - transition: all 0.2s ease; - - &:hover { - background-color: #f9f9f9; - } - - &.selected { - border-color: #4dabf7; - background-color: #e7f5ff; - } - - .section-title { - font-weight: 500; - font-size: 13px; - color: #333; - margin-bottom: 4px; - } - - .section-content { - font-size: 12px; - color: #666; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } +.offcanvas.show { + right: 0; } -.properties-panel { - width: 320px; - background-color: white; - border-left: 1px solid #e0e0e0; +.offcanvas-header { display: flex; - flex-direction: column; - overflow: hidden; - - .panel-header { - padding: 14px 20px; - border-bottom: 1px solid #e0e0e0; - - h3 { - margin: 0; - font-weight: 600; - font-size: 16px; - color: #333; - } - } - - .panel-content { - padding: 20px; - overflow-y: auto; - flex: 1; - } - - .property-form { - .form-group { - margin-bottom: 16px; - - label { - display: block; - margin-bottom: 8px; - font-weight: 500; - font-size: 14px; - color: #555; - } - - input, select, textarea { - width: 100%; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-family: inherit; - font-size: 14px; - - &:focus { - outline: none; - border-color: #4dabf7; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.2); - } - } - - textarea { - resize: vertical; - min-height: 80px; - } - - .section-type-input { - display: flex; - gap: 8px; - margin-bottom: 8px; - - input { - flex: 1; - } - } - } - - .form-actions { - display: flex; - gap: 8px; - margin-bottom: 16px; - } - } + align-items: center; + justify-content: space-between; + padding: 15px 20px; + border-bottom: 1px solid #ddd; } -.btn { - padding: 8px 16px; - border-radius: 4px; - font-weight: 500; - font-size: 14px; +.offcanvas-title { + font-size: 18px; + font-weight: 600; +} + +.close-btn { + background: none; + border: none; + font-size: 24px; cursor: pointer; - border: 1px solid transparent; - transition: all 0.2s ease; - - &.primary { - background-color: #4dabf7; - color: white; - - &:hover { - background-color: #339deb; - } - - &:disabled { - background-color: #adb5bd; - cursor: not-allowed; - } - } - - &.secondary { - background-color: #f1f3f5; - border-color: #ddd; - color: #555; - - &:hover { - background-color: #e9ecef; - } - } - - &.sm { - padding: 6px 12px; - font-size: 13px; - } + color: #666; } -.json-output-container { +.offcanvas-body { + padding: 20px; +} + +.section-template { + padding: 15px; + border: 1px solid #ddd; + border-radius: 8px; + margin-bottom: 15px; + cursor: pointer; + transition: all 0.2s ease; +} + +.section-template:hover { background-color: #f8f9fa; - border-top: 1px solid #e0e0e0; - max-height: 30vh; /* Limit height to at most 30% of viewport height */ - min-height: 100px; + border-color: #007bff; +} + +.section-template-title { + font-weight: 600; + font-size: 16px; + margin-bottom: 5px; + color: #333; +} + +.section-template-desc { + font-size: 14px; + color: #666; +} + +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1100; +} + +.modal.show { + display: block; +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); +} + +.modal-dialog { + width: 500px; + margin: 100px auto; + position: relative; +} + +.modal-content { + background-color: white; + border-radius: 8px; + box-shadow: 0 5px 15px rgba(0,0,0,0.3); overflow: hidden; +} + +.modal-header { + padding: 15px 20px; + border-bottom: 1px solid #ddd; + display: flex; + align-items: center; + justify-content: space-between; +} + +.modal-title { + font-weight: 600; + font-size: 18px; +} + +.modal-body { + padding: 20px; +} + +.modal-footer { + padding: 15px 20px; + border-top: 1px solid #ddd; + display: flex; + justify-content: flex-end; + gap: 10px; +} + +.loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255,255,255,0.7); display: flex; flex-direction: column; - - .card { - background-color: white; - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - padding: 16px; - margin: 16px; - display: flex; - flex-direction: column; - height: calc(100% - 32px); - - h3 { - margin-top: 0; - margin-bottom: 12px; - font-weight: 600; - font-size: 16px; - color: #333; - } - - .json-output { - width: 100%; - padding: 12px; - background-color: #f5f5f5; - border: 1px solid #ddd; - border-radius: 4px; - font-family: monospace; - font-size: 14px; - overflow-y: auto; - white-space: pre-wrap; - flex: 1; - } - } -} \ No newline at end of file + align-items: center; + justify-content: center; + z-index: 2000; +} + +.spinner { + border: 4px solid #f3f3f3; + border-top: 4px solid #007bff; + border-radius: 50%; + width: 40px; + height: 40px; + animation: spin 1s linear infinite; +} + +.loading-text { + margin-top: 15px; + font-size: 16px; + color: #666; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.cdk-drag-preview { + box-sizing: border-box; + border-radius: 4px; + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.cdk-drag-placeholder { + opacity: 0; +} + +.cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.sections-container.cdk-drop-list-dragging .section-card:not(.cdk-drag-placeholder) { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +/* Additional styles for better tree visualization */ +.children-container { + position: relative; + padding-top: 20px; +} + +.children-container::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + width: 2px; + height: 20px; + background-color: #ccc; +} + +.children-container > .node-container:not(:only-child)::before { + content: ''; + position: absolute; + top: -35px; + left: 50%; + width: 50%; + height: 2px; + background-color: #ccc; + z-index: -1; +} +.children-container > .node-container:not(:first-child):not(:last-child)::before { + width: 100%; + left: 0; +} + +.children-container > .node-container:first-child:not(:only-child)::before { + left: 50%; +} + +.children-container > .node-container:last-child:not(:only-child)::before { + width: 50%; + left: 0; +} diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.html index cf199c9..0ee1c18 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.html +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.html @@ -1,199 +1,174 @@ - - -
- - - - - - - -
-
- -
-
-
- Primary Sitemap -
-
-
- - -
-
-
-
- {{ homePage.name }} -
-
-
-
{{ section.type }}
-
{{ getSectionContentPreview(section.content) }}
-
-
-
-
- - - -
-
-
-
-
- {{ childPage.name }} -
-
-
-
{{ section.type }}
-
{{ getSectionContentPreview(section.content) }}
-
-
-
-
- - - -
-
-
-
-
- {{ grandchildPage.name }} -
-
-
-
{{ section.type }}
-
{{ getSectionContentPreview(section.content) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ + {{ rootPage.name }} +
+
+ +
+
+
+ + + +
-
-
- - -
- - -
-
-

{{ editMode === 'page' ? 'Page Properties' : 'Section Properties' }}

-
- -
- -
-
- - -
- -
- - - -
- -
- - -
- -
- -
- -
- - -
- - - -
- - -
+
+
+ + + + +
+
+
+
+
+
Edit Section: {{ selectedSection.type }}
+
+ + +
+
+ + +
+
+
+
+
+
Add Section
+ +
+
+
+
{{ template.type }}
+
{{ template.description }}
+
+
+

Custom Section

+
+ + +
+
+ + +
+ +
+
+
+ - - - +
+
+
Processing...
+
+ +
+
+
+
+ + {{ page.name }} +
+
+ + +
+
{{ section.type }}
+
+
+ + +
+ +
+ +
+
+ +
+
+ + +
+
+ +
+
+
+ + + +
+
+
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts index 0eea0aa..221e529 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts @@ -1,9 +1,9 @@ -import { Component, Input } from '@angular/core'; -import { OnInit } from '@angular/core'; -import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service'; +import { Component, ViewChild, ElementRef, OnInit, OnDestroy, AfterViewInit, HostListener } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; - +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service'; interface Section { type: string; @@ -11,13 +11,11 @@ interface Section { } interface Page { + id: string; // Unique identifier for each page name: string; sections: Section[]; children?: Page[]; -} - -interface SiteStructure { - pages: Page[]; + parent?: Page; // Reference to parent for easier navigation } @Component({ @@ -25,593 +23,798 @@ interface SiteStructure { templateUrl: './tree-node.component.html', styleUrls: ['./tree-node.component.css'] }) -export class TreeNodeComponent implements OnInit { - - @Input() node: any; - showJsonInput = true; - jsonInput = ''; - errorMessage = ''; - siteStructure: SiteStructure = { pages: [] }; +export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { + // Canvas properties + @ViewChild('canvasContainer') canvasContainer!: ElementRef; + private destroy$ = new Subject(); + + // Grid movement properties + private isDraggingCanvas = false; + private startX = 0; + private startY = 0; + private panning = false; + + // Zoom properties + currentScale = 1; + private maxScale = 2; + private minScale = 0.5; + private scaleFactor = 0.1; + + // Grid position + gridTranslateX = 0; + gridTranslateY = 0; + + // Site structure data rootPage: Page | null = null; selectedPage: Page | null = null; selectedSection: Section | null = null; editMode: 'page' | 'section' | null = null; - currentOutputJson = ''; - - // Form controls - newSectionType = ''; - newSectionContent = ''; + siteId: number = 0; + loading = false; + hasUnsavedChanges = false; + + // Section management + availableSectionTypes: string[] = []; + showOffcanvas = false; + currentParentPage: Page | null = null; + customSectionType = ''; + customSectionContent = ''; + + // Adding child pages + showAddChildModal = false; newPageName = ''; - showAddChildForm = false; - id: number; + currentParentForChild: Page | null = null; + + // For drag and drop + draggedPage: Page | null = null; + draggedSection: Section | null = null; + dropTargetPage: Page | null = null; + currentSectionIndex: number = -1; + + + // Section templates with better descriptions + sectionTemplates = [ + { + type: 'Hero Header Section', + description: 'Main banner with headline and CTA', + content: 'Introduce the company and its mission, with a strong call-to-action to sign up or learn more.' + }, + { + type: 'Feature Section', + description: 'Highlight product features with icons', + content: 'Highlight the core features of the platform, such as project creation, technology selection, and wireframing.' + }, + { + type: 'Features List Section', + description: 'Bulleted list of key features', + content: 'Secure JWT-based login, Easy project management, and Technology choices for frontend and backend.' + }, + { + type: 'Benefits Section', + description: 'Explain the benefits to users', + content: 'Detail the benefits of using our platform including time savings, quality improvement, and better outcomes.' + }, + { + type: 'How It Works Section', + description: 'Step-by-step process explanation', + content: 'Explain the process of using our platform from signup to completion.' + }, + { + type: 'Testimonial Section', + description: 'Customer quotes and reviews', + content: 'Showcase customer testimonials to build trust and credibility.' + }, + { + type: 'CTA Section', + description: 'Call-to-action buttons and form', + content: 'Encourage visitors to sign up or contact for more information.' + }, + { + type: 'Gallery Section', + description: 'Image gallery showcase', + content: 'Display portfolio items, project screenshots, or other visual content.' + }, + { + type: 'Navbar', + description: 'Top navigation bar', + content: 'Site navigation with links to main sections.' + }, + { + type: 'Footer', + description: 'Bottom site footer', + content: 'Contact information, links, and copyright details.' + } + ]; constructor( private siteTreeService: SiteTreeservice, - private route: ActivatedRoute, - - ) { } - + private route: ActivatedRoute + ) {} + ngOnInit(): void { - this.id = this.route.snapshot.params['id']; - - console.log(' id is : ', this.id) - - this.fetchTreeById(this.id); + this.siteId = this.route.snapshot.params['id']; + this.fetchSiteTree(); } - // Will be populated dynamically from incoming JSON - availableSectionTypes: string[] = []; - - - getSections(data: any): { title: string, description: string }[] { - const sections: { title: string, description: string }[] = []; - for (const key in data) { - if (key !== 'Children' && typeof data[key] === 'string') { - sections.push({ title: key, description: data[key] }); - } - if (key !== 'Children' && Array.isArray(data[key])) { - sections.push({ title: key, description: data[key].join('\n') }); - } + + ngAfterViewInit(): void { + this.initCanvasControls(); + + // Set up touch events for mobile + const canvas = this.canvasContainer.nativeElement; + canvas.addEventListener('touchstart', this.handleTouchStart.bind(this)); + canvas.addEventListener('touchmove', this.handleTouchMove.bind(this)); + canvas.addEventListener('touchend', this.handleTouchEnd.bind(this)); + + // Center grid after initial load + setTimeout(() => { + this.centerGrid(); + }, 500); + } + + ngOnDestroy(): void { + // Auto-save changes before component destruction + if (this.hasUnsavedChanges) { + this.saveTreeData(true); } - return sections; + + this.destroy$.next(); + this.destroy$.complete(); } - - getChildren(data: any): any[] { - if (data.Children) { - return Object.keys(data.Children).map((key) => ({ - title: key, - data: data.Children[key] - })); - } - return []; - } - - - // Code by harsh - - - - - fetchTreeById(id: number) { - this.siteTreeService.getById(id).subscribe({ - next: (res) => { - console.log('📥 Fetched from getById:', res); - - // Check if model key exists and has some value - if (res && res.model) { - try { - // Optional: if model is already a stringified JSON - const maybeParsed = typeof res.model === 'string' ? JSON.parse(res.model) : res.model; - this.jsonInput = JSON.stringify(maybeParsed, null, 2); - this.parseJsonInput(); // convert it immediately - } catch (err) { - console.error('❌ Error parsing model JSON:', err); - alert('Model data is not valid JSON format'); - } - } else { - console.warn('⚠️ Model key not found or empty, keeping inputJson as is.'); - } - }, - error: (err) => { - console.error('❌ Error in getById:', err); - alert('Failed to fetch data from server'); + + // GRID MOVEMENT METHODS + + initCanvasControls(): void { + const canvas = this.canvasContainer.nativeElement; + + // Mouse down event for dragging + canvas.addEventListener('mousedown', (e: MouseEvent) => { + // Only initiate drag if not clicking on interactive elements + if ((e.target as HTMLElement).closest('.node-card, .node-header, button, input, select, textarea, .modal, .editor-form, .section-card, .add-button')) { + return; } + + this.panning = true; + this.startX = e.clientX - this.gridTranslateX; + this.startY = e.clientY - this.gridTranslateY; + canvas.style.cursor = 'grabbing'; + e.preventDefault(); }); - } - - - parseJsonInput(): void { - try { - const parsedData = JSON.parse(this.jsonInput); - - // Transform the nested JSON format to our page structure - if (parsedData && typeof parsedData === 'object') { - this.siteStructure = { pages: [] }; - this.rootPage = { - name: 'Primary Sitemap', - sections: [], - children: [] - }; - - // Collect all section types dynamically - this.availableSectionTypes = []; - this.availableSectionTypes.push('callToAction'); - - - // Process the root level objects (Home, etc.) - Object.keys(parsedData).forEach(pageName => { - const pageData = parsedData[pageName]; - const page: Page = { - name: pageName, - sections: this.extractSections(pageData), - children: [] - }; - - // Process children if they exist - if (pageData.Children) { - Object.keys(pageData.Children).forEach(childName => { - const childData = pageData.Children[childName]; - const childPage: Page = { - name: childName, - sections: this.extractSections(childData), - children: [] - }; - - // Process grandchildren if they exist - if (childData.Children) { - Object.keys(childData.Children).forEach(grandchildName => { - const grandchildData = childData.Children[grandchildName]; - const grandchildPage: Page = { - name: grandchildName, - sections: this.extractSections(grandchildData), - }; - childPage.children?.push(grandchildPage); - }); - } - - page.children?.push(childPage); - }); - } - - this.rootPage?.children?.push(page); - }); - - this.siteStructure.pages.push(this.rootPage); - this.showJsonInput = false; - this.errorMessage = ''; - - // Generate initial output JSON - this.updateOutputJson(); - } else { - this.errorMessage = 'Invalid JSON structure'; - } - } catch (e) { - this.errorMessage = 'Invalid JSON format: ' + (e as Error).message; - } - } - - // Extract sections from page data and collect section types - extractSections(pageData: any): Section[] { - const sections: Section[] = []; - - Object.keys(pageData).forEach(key => { - // Skip the Children key as it's not a section - if (key !== 'Children') { - const value = pageData[key]; - - // Add to available section types if not already included - if (!this.availableSectionTypes.includes(key)) { - this.availableSectionTypes.push(key); - } - - // Handle array of sections - if (Array.isArray(value)) { - sections.push({ - type: key, - content: value.join('\n') - }); - } - // Handle object sections with empty content - else if (typeof value === 'object' && value !== null) { - sections.push({ - type: key, - content: JSON.stringify(value) - }); - } - // Handle string content - else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { - sections.push({ - type: key, - content: String(value) - }); - } - } + + // Mouse move for dragging the grid + document.addEventListener('mousemove', (e: MouseEvent) => { + if (!this.panning) return; + + // Calculate new position + const newX = e.clientX - this.startX; + const newY = e.clientY - this.startY; + + // Update position + this.gridTranslateX = newX; + this.gridTranslateY = newY; + this.applyGridTransform(); }); - - return sections; + + // Mouse up to end dragging + document.addEventListener('mouseup', () => { + this.panning = false; + canvas.style.cursor = 'grab'; + }); + + // Mouse wheel for zooming + canvas.addEventListener('wheel', (e: WheelEvent) => { + e.preventDefault(); + + // Calculate zoom based on wheel direction + const delta = e.deltaY < 0 ? this.scaleFactor : -this.scaleFactor; + const newScale = Math.min(this.maxScale, Math.max(this.minScale, this.currentScale + delta)); + + // Get mouse position relative to canvas + const rect = canvas.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + // Calculate zoom around mouse position + this.zoomAtPoint(newScale, mouseX, mouseY); + }, { passive: false }); } - - resetEditor(): void { - this.rootPage = null; - this.siteStructure = { pages: [] }; - this.selectedPage = null; - this.selectedSection = null; - this.editMode = null; - this.showJsonInput = true; - this.jsonInput = ''; - this.errorMessage = ''; - this.availableSectionTypes = []; - this.currentOutputJson = ''; + + // Touch event handlers for mobile devices + handleTouchStart(e: TouchEvent): void { + if (e.touches.length !== 1) return; + + const touch = e.touches[0]; + this.panning = true; + this.startX = touch.clientX - this.gridTranslateX; + this.startY = touch.clientY - this.gridTranslateY; + e.preventDefault(); } - - updateOutputJson(): void { - if (this.rootPage) { - const output = this.transformToNestedJson(); - this.currentOutputJson = JSON.stringify(output, null, 2); - - console.log('updtae json output ', this.currentOutputJson); - - // 🔁 Now call update API - const updatePayload = { - model: this.currentOutputJson // model format expected by backend - }; - - console.log('payload : ', updatePayload); - this.siteTreeService.update(this.id, updatePayload).subscribe({ - next: (resp) => { - console.log('✅ JSON updated on server:', resp); - }, - error: (err) => { - console.error('❌ Failed to update JSON on server:', err); - alert('Update to server failed'); - } - }); + + handleTouchMove(e: TouchEvent): void { + if (!this.panning || e.touches.length !== 1) return; + + const touch = e.touches[0]; + this.gridTranslateX = touch.clientX - this.startX; + this.gridTranslateY = touch.clientY - this.startY; + this.applyGridTransform(); + e.preventDefault(); + } + + handleTouchEnd(): void { + this.panning = false; + } + + // Apply transform to the grid + applyGridTransform(): void { + const grid = document.querySelector('.sitemap-grid') as HTMLElement; + if (!grid) return; + + grid.style.transform = `translate(${this.gridTranslateX}px, ${this.gridTranslateY}px) scale(${this.currentScale})`; + } + + // Public method to get total transform for template binding + getTotalTransform(): string { + return `translate(${this.gridTranslateX}px, ${this.gridTranslateY}px) scale(${this.currentScale})`; + } + + // Zoom at a specific point + zoomAtPoint(newScale: number, x: number, y: number): void { + if (newScale === this.currentScale) return; + + // Calculate offset change to zoom at mouse position + const scaleFactor = newScale / this.currentScale; + const dx = x - x * scaleFactor; + const dy = y - y * scaleFactor; + + // Apply new position and scale + this.gridTranslateX = this.gridTranslateX * scaleFactor + dx; + this.gridTranslateY = this.gridTranslateY * scaleFactor + dy; + this.currentScale = newScale; + + this.applyGridTransform(); + } + + // Center the grid in the viewport + centerGrid(): void { + const canvas = this.canvasContainer.nativeElement; + const grid = document.querySelector('.tree-grid') as HTMLElement; + const rootNode = document.querySelector('.home-node') as HTMLElement; + + if (!canvas || !grid) { + setTimeout(() => this.centerGrid(), 100); + return; + } + + const canvasRect = canvas.getBoundingClientRect(); + const gridRect = grid.getBoundingClientRect(); + + // Calculate scale to fit content within the fixed box + const scaleX = canvasRect.width / gridRect.width * 0.2; + const scaleY = canvasRect.height / gridRect.height * 0.2; + const scale = Math.min(Math.max(this.minScale, Math.min(scaleX, scaleY)), 1); + + // Position the grid - centered horizontally, but align top of tree with top of viewport + this.currentScale = scale; + this.gridTranslateX = -700; // Set left position to -900px + + // Position vertically to place root node near top with some padding + const topPadding = 50; // Pixels from top of viewport + if (rootNode) { + const rootRect = rootNode.getBoundingClientRect(); + const rootOffsetY = (rootRect.top - gridRect.top) * 0.9; + this.gridTranslateY = topPadding - rootOffsetY; } else { - this.currentOutputJson = ''; + // Fallback if root node not found + this.gridTranslateY = topPadding; } + + this.applyGridTransform(); } - - downloadJson(): void { - if (this.rootPage) { - const output = this.transformToNestedJson(); - const dataStr = JSON.stringify(output, null, 2); - const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr); - - const exportName = 'site-structure.json'; - const linkElement = document.createElement('a'); - linkElement.setAttribute('href', dataUri); - linkElement.setAttribute('download', exportName); - linkElement.click(); - } + + // ZOOM CONTROL METHODS + + zoomIn(): void { + const canvas = this.canvasContainer.nativeElement; + const rect = canvas.getBoundingClientRect(); + const centerX = rect.width / 2; + const centerY = rect.height / 2; + + const newScale = Math.min(this.maxScale, this.currentScale + this.scaleFactor); + this.zoomAtPoint(newScale, centerX, centerY); } + + zoomOut(): void { + const canvas = this.canvasContainer.nativeElement; + const rect = canvas.getBoundingClientRect(); + const centerX = rect.width / 2; + const centerY = rect.height / 2; + + const newScale = Math.max(this.minScale, this.currentScale - this.scaleFactor); + this.zoomAtPoint(newScale, centerX, centerY); + } + + resetZoom(): void { + this.centerGrid(); + } + + // TREE DATA METHODS + + fetchSiteTree(): void { + this.loading = true; + this.siteTreeService.getById(this.siteId).subscribe({ + next: (res) => { + this.loading = false; + // Parse and unwrap + const raw = typeof res.model === 'string' ? JSON.parse(res.model) : res.model; + const rootTitle = raw.title || 'Site Structure'; + const pagesObj = raw.data?.Children || raw.Children || raw.data || raw; + this.processSiteData( pagesObj,rootTitle); + }, + error: () => { + this.loading = false; + this.createDefaultStructure(); + } + }); + } + + // Create a default structure + createDefaultStructure(): void { + const defaultData = { + "Home": { + "Navbar": {}, + "Hero Header Section": "Introduce the company and its mission, with a strong call-to-action to sign up or learn more.", + "Feature Section": "Highlight the core features of the platform.", + "CTA Section": "Encourage visitors to sign up or contact for more information.", + "Footer": {} + } + }; + this.processSiteData(defaultData); + } + + processSiteData(pagesObj: any, rootTitle?: string): void { + // Initialize root with dynamic title + this.rootPage = { id: 'root', name: rootTitle, sections: [], children: [] }; + this.availableSectionTypes = []; + this.hasUnsavedChanges = false; + let idCounter = 0; - // Transform our page structure back to the nested JSON format - transformToNestedJson(): any { - const result: any = {}; - - // Process only the first level (Home) - if (this.rootPage && this.rootPage.children && this.rootPage.children.length > 0) { - this.rootPage.children.forEach(page => { - result[page.name] = {}; - - // Add sections - page.sections.forEach(section => { - // Try to parse content as JSON if it looks like JSON - if (section.content.trim().startsWith('{') && section.content.trim().endsWith('}')) { - try { - result[page.name][section.type] = JSON.parse(section.content); - } catch (e) { - result[page.name][section.type] = section.content || {}; + // Recursive builder + const buildPages = (obj: any, parent?: Page): Page[] => { + return Object.keys(obj || {}).map(key => { + const pageData = obj[key] || {}; + const page: Page = { + id: `page-${++idCounter}`, + name: key, + sections: [], + children: [], + parent + }; + // Sections: all props except Children + Object.keys(pageData).forEach(prop => { + if (prop !== 'Children') { + const val = pageData[prop]; + page.sections.push({ + type: prop, + content: Array.isArray(val) + ? val.join('\n') + : typeof val === 'object' + ? JSON.stringify(val, null, 2) + : String(val) + }); + if (!this.availableSectionTypes.includes(prop)) { + this.availableSectionTypes.push(prop); } } - // Try to parse as array if it looks like array - else if (section.content.trim().startsWith('[') && section.content.trim().endsWith(']')) { - try { - result[page.name][section.type] = JSON.parse(section.content); - } catch (e) { - result[page.name][section.type] = section.content || []; - } - } - // Handle numeric values - else if (!isNaN(Number(section.content)) && section.content.trim() !== '') { - result[page.name][section.type] = Number(section.content); - } - // Handle boolean values - else if (section.content.trim() === 'true' || section.content.trim() === 'false') { - result[page.name][section.type] = section.content.trim() === 'true'; - } - // Default to string - else { - result[page.name][section.type] = section.content || ""; - } }); + // Recurse into Children + if (pageData.Children) { + page.children = buildPages(pageData.Children, page); + } + return page; + }); + }; - // Add children - if (page.children && page.children.length > 0) { - result[page.name]['Children'] = {}; - - page.children.forEach(childPage => { - result[page.name]['Children'][childPage.name] = {}; - - // Add child sections - childPage.sections.forEach(section => { - // Try to parse content as JSON if it looks like JSON - if (section.content.trim().startsWith('{') && section.content.trim().endsWith('}')) { - try { - result[page.name]['Children'][childPage.name][section.type] = JSON.parse(section.content); - } catch (e) { - result[page.name]['Children'][childPage.name][section.type] = section.content || {}; - } - } - // Try to parse as array if it looks like array - else if (section.content.trim().startsWith('[') && section.content.trim().endsWith(']')) { - try { - result[page.name]['Children'][childPage.name][section.type] = JSON.parse(section.content); - } catch (e) { - result[page.name]['Children'][childPage.name][section.type] = section.content || []; - } - } - // Handle numeric values - else if (!isNaN(Number(section.content)) && section.content.trim() !== '') { - result[page.name]['Children'][childPage.name][section.type] = Number(section.content); - } - // Handle boolean values - else if (section.content.trim() === 'true' || section.content.trim() === 'false') { - result[page.name]['Children'][childPage.name][section.type] = section.content.trim() === 'true'; - } - // Default to string - else { - result[page.name]['Children'][childPage.name][section.type] = section.content || ""; - } + this.rootPage.children = buildPages(pagesObj, this.rootPage); + setTimeout(() => this.centerGrid(), 100); + } + + createPageFromData(pageName: string, pageData: any, idPrefix: string): Page { + const page: Page = { + id: idPrefix, + name: pageName, + sections: [], + children: [] + }; + + // Extract sections + if (pageData) { + // Process properties as sections + Object.keys(pageData).forEach(key => { + if (key !== 'Children') { + const value = pageData[key]; + + // Add to available section types if not already included + if (!this.availableSectionTypes.includes(key)) { + this.availableSectionTypes.push(key); + } + + // Handle different value types + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + // Object value + page.sections.push({ + type: key, + content: Object.keys(value).length > 0 ? JSON.stringify(value, null, 2) : '' }); - - // Add grandchildren - if (childPage.children && childPage.children.length > 0) { - result[page.name]['Children'][childPage.name]['Children'] = {}; - - childPage.children.forEach(grandchildPage => { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name] = {}; - - // Add grandchild sections - grandchildPage.sections.forEach(section => { - // Try to parse content as JSON if it looks like JSON - if (section.content.trim().startsWith('{') && section.content.trim().endsWith('}')) { - try { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = JSON.parse(section.content); - } catch (e) { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content || {}; - } - } - // Try to parse as array if it looks like array - else if (section.content.trim().startsWith('[') && section.content.trim().endsWith(']')) { - try { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = JSON.parse(section.content); - } catch (e) { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content || []; - } - } - // Handle numeric values - else if (!isNaN(Number(section.content)) && section.content.trim() !== '') { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = Number(section.content); - } - // Handle boolean values - else if (section.content.trim() === 'true' || section.content.trim() === 'false') { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content.trim() === 'true'; - } - // Default to string - else { - result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content || ""; - } - }); - }); - } - }); + } else if (Array.isArray(value)) { + // Array value + page.sections.push({ + type: key, + content: value.join('\n') + }); + } else { + // String or primitive value + page.sections.push({ + type: key, + content: String(value) + }); + } } }); + + // Process children recursively if the Children key exists + if (pageData.Children) { + let childId = 0; + Object.keys(pageData.Children).forEach(childName => { + const childData = pageData.Children[childName]; + const childPage = this.createPageFromData(childName, childData, `${idPrefix}-child-${++childId}`); + childPage.parent = page; + page.children?.push(childPage); + }); + } } - - return result; + + return page; } - + + // Generate output JSON from tree structure + generateOutputJson(): any { + const output: any = {}; + + if (this.rootPage?.children) { + this.rootPage.children.forEach(page => { + output[page.name] = this.convertPageToJsonObject(page); + }); + } + + return output; + } + + convertPageToJsonObject(page: Page): any { + const pageObj: any = {}; + + // Add sections + page.sections.forEach(section => { + try { + // Check if content is an array (line breaks) + if (section.content.includes('\n')) { + pageObj[section.type] = section.content.split('\n'); + } else { + // Try to parse JSON content for objects + const parsedContent = JSON.parse(section.content); + pageObj[section.type] = parsedContent; + } + } catch (e) { + // If parsing fails, use the raw content + pageObj[section.type] = section.content; + } + }); + + // Add children + if (page.children && page.children.length > 0) { + pageObj.Children = {}; + + page.children.forEach(childPage => { + pageObj.Children[childPage.name] = this.convertPageToJsonObject(childPage); + }); + } + + return pageObj; + } + + // PAGE AND SECTION MANAGEMENT + selectPage(page: Page): void { this.selectedPage = page; this.selectedSection = null; - this.editMode = 'page'; } - + selectSection(section: Section): void { this.selectedSection = section; this.editMode = 'section'; } - - addSectionToPage(page: Page): void { - if (this.newSectionType && page) { - // Create new section type if it doesn't exist - if (!this.availableSectionTypes.includes(this.newSectionType)) { - this.availableSectionTypes.push(this.newSectionType); - } - - page.sections.push({ - type: this.newSectionType, - content: this.newSectionContent || "" - }); - - this.newSectionType = ''; - this.newSectionContent = ''; - - // Update JSON output - this.updateOutputJson(); - } - } - - updatePage(): void { - // Already updated via ngModel binding - this.updateOutputJson(); - } - - updateSection(): void { - // Already updated via ngModel binding - this.updateOutputJson(); - } - - addChildPage(parentPage: Page): void { - if (!this.newPageName) return; - - if (!parentPage.children) { - parentPage.children = []; - } - - parentPage.children.push({ - name: this.newPageName, - sections: [] - }); - + + // Show modal to add a child page + showAddChildPageModal(parent: Page): void { + this.currentParentForChild = parent; this.newPageName = ''; - this.updateOutputJson(); + this.showAddChildModal = true; } - - addSectionType(): void { - if (this.newSectionType && !this.availableSectionTypes.includes(this.newSectionType)) { - this.availableSectionTypes.push(this.newSectionType); + + // Add a child page + addChildPage(): void { + if (this.currentParentForChild && this.newPageName) { + if (!this.currentParentForChild.children) { + this.currentParentForChild.children = []; + } + + const newPage: Page = { + id: `page-${Date.now()}`, + name: this.newPageName, + sections: [], + children: [], + parent: this.currentParentForChild + }; + + this.currentParentForChild.children.push(newPage); + this.hasUnsavedChanges = true; + this.closeAddChildModal(); + + // Auto-select the new page + this.selectPage(newPage); } } - - deletePage(page: Page, parentPage: Page | null): void { - if (parentPage && parentPage.children) { - const index = parentPage.children.indexOf(page); - if (index > -1) { - parentPage.children.splice(index, 1); - - // Reset selection if deleting selected page + + closeAddChildModal(): void { + this.showAddChildModal = false; + this.currentParentForChild = null; + this.newPageName = ''; + } + + // Delete a page + deletePage(page: Page, event: Event): void { + event.stopPropagation(); + + if (!page.parent || !page.parent.children) return; + + if (confirm(`Are you sure you want to delete "${page.name}" and all its children?`)) { + const index = page.parent.children.indexOf(page); + if (index !== -1) { + page.parent.children.splice(index, 1); + if (this.selectedPage === page) { this.selectedPage = null; this.editMode = null; } - - this.updateOutputJson(); + + this.hasUnsavedChanges = true; } } } - - deleteSection(section: Section, page: Page): void { - const index = page.sections.indexOf(section); - if (index > -1) { - page.sections.splice(index, 1); - - // Reset selection if deleting selected section - if (this.selectedSection === section) { - this.selectedSection = null; - this.editMode = null; + + // Format section content as HTML with colored text + getSectionContentHtml(content: string): string { + if (!content) return ''; + + // Replace line breaks with
for proper display + return content.replace(/\n/g, '
'); + } + + // Show add section offcanvas + openSectionOffcanvas(page: Page, sectionIndex: number = -1, event?: Event): void { + if (event) { + event.stopPropagation(); + } + this.currentParentPage = page; + this.currentSectionIndex = sectionIndex; + this.customSectionType = ''; + this.customSectionContent = ''; + this.showOffcanvas = true; + } + + // Close the offcanvas + closeOffcanvas(): void { + this.showOffcanvas = false; + this.currentParentPage = null; + this.currentSectionIndex = -1; + } + + // Add section from a template + addSectionFromTemplate(template: any): void { + if (this.currentParentPage) { + const newSection: Section = { + type: template.type, + content: template.content + }; + + // Insert at specific position if provided, otherwise add to end + if (this.currentSectionIndex >= 0) { + this.currentParentPage.sections.splice(this.currentSectionIndex + 1, 0, newSection); + } else { + this.currentParentPage.sections.push(newSection); } - - this.updateOutputJson(); + + if (!this.availableSectionTypes.includes(template.type)) { + this.availableSectionTypes.push(template.type); + } + + this.hasUnsavedChanges = true; + this.closeOffcanvas(); } } - - onPageDrop(event: CdkDragDrop, parentPage: Page): void { - if (parentPage.children) { - moveItemInArray(parentPage.children, event.previousIndex, event.currentIndex); - this.updateOutputJson(); + + // Add custom section + +// Similar update for addCustomSection +addCustomSection(): void { + if (this.currentParentPage && this.customSectionType) { + const newSection: Section = { + type: this.customSectionType, + content: this.customSectionContent || '' + }; + + // Insert at specific position if provided, otherwise add to end + if (this.currentSectionIndex >= 0) { + this.currentParentPage.sections.splice(this.currentSectionIndex + 1, 0, newSection); + } else { + this.currentParentPage.sections.push(newSection); + } + + if (!this.availableSectionTypes.includes(this.customSectionType)) { + this.availableSectionTypes.push(this.customSectionType); + } + + this.hasUnsavedChanges = true; + this.closeOffcanvas(); + } +} + + // Edit section + editSection(section: Section, event: Event): void { + event.stopPropagation(); + this.selectedSection = section; + this.editMode = 'section'; + } + + // Cancel edit + cancelEdit(): void { + this.editMode = null; + this.selectedSection = null; + } + + // Save edit + saveEdit(): void { + this.editMode = null; + this.hasUnsavedChanges = true; + } + + // Delete section + deleteSection(section: Section, page: Page, event: Event): void { + event.stopPropagation(); + + if (confirm(`Are you sure you want to delete this "${section.type}" section?`)) { + const index = page.sections.indexOf(section); + if (index !== -1) { + page.sections.splice(index, 1); + + if (this.selectedSection === section) { + this.selectedSection = null; + this.editMode = null; + } + + this.hasUnsavedChanges = true; + } } } - - onSectionDrop(event: CdkDragDrop, page: Page): void { - moveItemInArray(page.sections, event.previousIndex, event.currentIndex); - this.updateOutputJson(); + + // DRAG AND DROP METHODS + + // Handle page drag start + onPageDragStart(page: Page): void { + this.draggedPage = page; } - - getParentPage(childPage: Page | null): Page | null { - if (!this.rootPage || !childPage) return null; - - // Check all levels recursively - return this.findParentPageRecursive(this.rootPage, childPage); + + // Handle page drag end + onPageDragEnd(): void { + this.draggedPage = null; + this.dropTargetPage = null; } - - findParentPageRecursive(currentPage: Page, targetPage: Page): Page | null { - if (!currentPage.children) return null; - - // Check direct children - if (currentPage.children.includes(targetPage)) { - return currentPage; + + // Handle page drop + onPageDrop(targetPage: Page): void { + if (!this.draggedPage || this.draggedPage === targetPage) return; + + // Remove from current parent + if (this.draggedPage.parent && this.draggedPage.parent.children) { + const sourceIndex = this.draggedPage.parent.children.indexOf(this.draggedPage); + if (sourceIndex > -1) { + this.draggedPage.parent.children.splice(sourceIndex, 1); + } } - - // Check nested children - for (const child of currentPage.children) { - const result = this.findParentPageRecursive(child, targetPage); - if (result) return result; + + // Add to new parent + if (!targetPage.children) { + targetPage.children = []; } - - return null; + + // Update parent reference + this.draggedPage.parent = targetPage; + targetPage.children.push(this.draggedPage); + + this.hasUnsavedChanges = true; } - - canAddChildPage(page: Page | null): boolean { - if (!page) return false; - - // Root can have unlimited children in dynamic mode - if (page === this.rootPage) { - return true; + + // Handle section drag drop + onSectionDrop(event: CdkDragDrop): void { + if (event.previousContainer === event.container) { + // Reordering within the same page + moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); + } else { + // Moving to a different page + transferArrayItem( + event.previousContainer.data, + event.container.data, + event.previousIndex, + event.currentIndex + ); } - - // First level children can have children - const pageLevel = this.getPageLevel(page); - if (pageLevel === 1) { - return true; + + this.hasUnsavedChanges = true; + } + + // UI HELPER METHODS + + getNodeIcon(page: Page): string { + if (page.name.toLowerCase() === 'home') { + return 'fas fa-home'; + } else if (page.children && page.children.length > 0) { + return 'fas fa-folder-open'; + } else { + return 'fas fa-file'; } - - // Second level children can't have children - return pageLevel < 2; } - - getPageLevel(page: Page): number { - if (page === this.rootPage) return 0; - - let currentPage = page; - let level = 0; - - while (currentPage !== this.rootPage) { - const parent = this.getParentPage(currentPage); - if (!parent) break; - - level++; - currentPage = parent; + + // Save tree data + saveTreeData(silent: boolean = false): void { + const outputData = this.generateOutputJson(); + const jsonData = JSON.stringify(outputData, null, 2); + + if (this.siteId) { + this.loading = true; + + this.siteTreeService.update(this.siteId, { + id: this.siteId, + model: jsonData + }).subscribe({ + next: () => { + this.loading = false; + this.hasUnsavedChanges = false; + + if (!silent) { + alert('Site structure saved successfully'); + } + }, + error: (err) => { + this.loading = false; + console.error('Error saving data:', err); + + if (!silent) { + alert('Failed to save site structure'); + } + } + }); } - - return level; } - - getHomePageFromRoot(): Page | null { - return this.rootPage?.children?.[0] || null; - } - - getChildrenPages(parentPageName: string): Page[] { - const parentPage = this.findPageByName(parentPageName); - return parentPage?.children || []; - } - - findPageByName(name: string): Page | null { - if (!this.rootPage) return null; - - return this.findPageByNameRecursive(this.rootPage, name); - } - - findPageByNameRecursive(currentPage: Page, name: string): Page | null { - if (currentPage.name === name) return currentPage; - - if (!currentPage.children) return null; - - for (const child of currentPage.children) { - const result = this.findPageByNameRecursive(child, name); - if (result) return result; + + @HostListener('window:beforeunload', ['$event']) + beforeUnloadHandler(event: BeforeUnloadEvent): void { + if (this.hasUnsavedChanges) { + event.preventDefault(); + event.returnValue = 'You have unsaved changes. Are you sure you want to leave?'; } - - return null; } - - hasChildPages(page: Page | null): boolean { - return !!page?.children && page.children.length > 0; + + @HostListener('window:resize') + onWindowResize(): void { + this.centerGrid(); } - - getSectionContentPreview(content: string): string { - return content?.length > 50 ? content.slice(0, 50) + '...' : content || ''; - } -} \ No newline at end of file +} diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip new file mode 100644 index 0000000000000000000000000000000000000000..a039f77fcd065e8594fb06d4c648fc43d9e012bb GIT binary patch literal 15343 zcmbumbC71+vgrGj%`V&Ova7qyuWZ}4ZQDkdZFkwW-DTT+Ywh>eIs5K-@5Vjx#*7?e z&VMo^V`k=zn7>RpNic9Uz~Ad7CsyNMH~-Iu03ZUWnmZVqIOtm&J1UzqC@UcVplqx@ zO&ms)9Sp zOv@D-NGB{^FCN2_rEe0FfKBJ`4RaeuBvAVlM|-&g7q>%j8@CL2;rIkw&5bKRy-dGM zCzu+k?_B9;9?8IQQB_r5c2%9eIIoQ)LP-po9&{neuLBD2#_qn5#0V$7sIS57cqY_I zHkaf+uKF~p_3??#`k)zEO;U(+n{Mjfn6<;Z*Rw=?o9$Gd=pdYq<%)v;R3YF$#q=84K+)aXg<3SrSY|!J@ z*lx@>9Y7mI=tn2OYV4}k@*N7ebXin(qge+vB7pPA9Zd>qEQKI9AC?398fDC5V+XSqm}p=gHPxZ5C>NDD^x*%X;RXF6?MQ{ zg`Zlkc@kM#zOZFTRC{)U{EQ8(+vOb{rv-YuIf(g?qJ6OLMmk}beg^dW6wChZZS^c~ zEqp-@?L*otTZZVWBQgdXYWGH{UarGT3$tPs+S<^(h~?n{UJhq`{+>58OdB3H1RXXZ z(FzNi6k(6lr_16)U)7CJJ{{~oA`|1G!;+Onu=o~Fml%eRkQ>PS!mt4T_^^ELkX=x4 z!!e zlEz3yv9TuAXdVfjGURfA9d<8J-&Z@$>vB;5VM{|2?}r_yB}F(eYJ)`(s09|OFs)O6 zG-N#~iU%I~_iyO zXwa+ls@H2R+PG>!`V+ccyng^dNE>T)rSfelcc9MS;9B&c`kFmVYSQHn%}KEC4g^UM zd5<=wS%Nub0<4`B8cCu7u5#J<=R|*;xV11|&wD^VZ6RZyZ0xgAQ~a&Rwo>&Z(DQLRZa4F%r?A&O}_pLD%rJG(07;Ke#|`V9gN=31m$_0{8DlG%Cw!H zY^CyW>r=iIvf>J}0h}Z_75*|#ipIv0x-1a&-dkSNLPmoq0zpj>b9t+Z~TN-jeDa?vYuSES{@v=Yx#=03lfr<%`$D?0UDf49&+e%4Jq*3$Tl zz^Ni_xE4EB88u}|Wy=1`lh((x?Js;!ec#NM1p~vfI&U@ge(=y zFlt5%KXR5Q2vG|(>()ZC{z?g1b4QfYC{l-0k5(3SWV%7RvUp~<_@6(j1sfhw-1Ds{ z+bPMjfe06t#BbWcJz*kYpc+DlL{q3xZ1ZjgQBJ|6V}&PFQ!56VMg%>+$sKM88}!XN zdCDn`%2OM^6?^j7LxhXipVji!is>C3YQ!76d>=~q0^}pV>2pe)Xw+j*n;}(l4Ir=E zABw`pH;cC`XKU`U&=G}vryfjUf6g(w_71ZpvV_{+VuEj3e@q~_za}E^a*? zG#1*ko=+d=jk|1=!>5|AH-?eL)&7ESVRoW_9B3i4t($T66Plpi2khZ`WV9nF6OSS{0?xvEw2|qcYf|N*1K_cLjCTg3N!o>ShcQ3ti4*i6J0(U*c1ghy;LKJe5jZ zEDSB6p*!T?kru_H#*((qMF>)&ku9)O8`X5be58_~Y$lcRM|lwj!!a_m`uS27$*=z^8HG(IROj6#$I!28Ov~U%T0WL;)D_?#FIzz0p z;vO7gl9-eczTyh8!K5c?^AC{H;@<`FJqRSq0mQeitm2yydE4C`Z*Ef-R(mO*y+_0t zieXIp0=u!IMO&ILx3>JvT|G@jheZComo%j(oSZr;n!9Oce}5R~!(rlgj%q}Fb6-U; zx=oyp$d&I0D#@t8Rj5@){y8UAKDb1{+Qly1ZH}4xE<=He=@A#2oWseD8MC1sjF#u= z4?W!K@Mk<;fiDti7cTzXU;Ns`hs5lyBir;O^k*EN)9)b`wu_+{FT}J-9x-~A-ztOD ztC6a{)zx@2bCCAoX~RcpQg=kcE~jDjzd_a-?r--#Ge!&{7=zfMzN6FfTh2j<5vC4m zL?8PH0!9OG9luc^>gOdG0vZ$ds4TGP{*K&^tt3aI>L({KO8GX-^5S|!1YJIn_z(2u7C=Ckz}#~8-iG> zM->RJ?})RIQ_uvmrC_6dV~?|lgWJjo81Yj7(H)oH3Pp|K21#oC2#aPpmuW&nDKOBV zFQHhqmtl!!yaA~}^()U&G+V;o0S^yP0;xt0;)^q!_^W90Jx)3$%>xZ00%Ii7Oq5ty z$B7T@>+PbuetSrAXx4lOCmp(9FM`r#%)Lw<1{{?USphGni(elUjr?g;RT`9^G7beh zo2c*@lPeSSY;@6_rqqfouuu&#cYwH6Tir5w3ZI7`DvRR~cKw&%q@pn5%2KrC@JsKq zZs3WAZ77^hAvpSKZBu(#U z&_lo%n@SI#5HA|B4^!523cMxpTpanyn;05QwBMd0wmEkEdAxrgP@<&|0Fe9B>9|)< zmUk8tu|Haafv7LBGO=43UX<8xG*&3w3;kHPZDAqjGb7#WzdWBp^v&MZS)1?%RT zD9aN)EE7P3ba_C;5w-0WB?r0EskMwRhg{j>y(`!*gdv(u!xSrP)5_vy&%k3bfa7&{v$8J@|XImHF45eY05xC8l{(hRS%$^@qyZj4-S!1T-} zr(6SGkv*azJd@=w2JqU@iHM4Gb)l};ySl<4{Z*nger6ecUntUQA+>74itqHtJrL@kY1hmEvc6v@BWko2u^_N)Y`(nvIR6nAu zF`@jGWc$rBvZ1o{%_dziLC&EIJLl7}rPNK8tH+A;Avg&5K#sDYY4Y8qbW z!IU47FYmzam$!;c{3WiW$8yH?#6-ox0_DbKS0odVH>g#Hp5=q}koI=%Nkl;(Ja8B$Irgi4cH9vub&{dFJEZ8bkujCkM!utSvl##ROVuWgC2S0oi9)*otG%JLka zhKy%ln!7ql2@eBrv*TKMV*^Ldcq7|8ow*tnpEot>YBOzBo24)|&FE*9%FU>Xm~CHZ zeSteMvpo>2r)2(aI8Yvt^2R4|p^zawwJ|@WG~*7N^Zo!{k76RwVK5)X^M295pbo+` zuTWP!U`PTlJ^0xQTrj^C>I%Jk!?}--E)IpL;OIaoRAHY?_tY7EtP<%4GnMr8!_4e5 z_Oj(DfyuuovGE=$ve{G$j9V4GApv0un6ixq=F$Wve&iJ>^=%lpT9k_`lmeR{_M?MI z-&vbKxo(z*S&WKHhldBW9sx@rG%dG9^Tn{SF;frMEy=vrjBV`lQ`Hq-(ykhNDcLUJ z@IH8>QKDkarrN}B3PFc=Vs19vzkI4R=J4FrSKC;P5;B>ANvlbHr{8g3n_5^q8XDnJ; zx_v_!efYIOSV|9+R`pfD5xJk*kl|QxK;4`cy7xHP)WDD^C+zMy*q@VDxk@~*b4#eG z5+Ky3##|x&fD~?&86AevB@P}(t6Dx zLP~lr-@poDbQxv+C@yb&jqDg<(b`%_KTdDZ3wkQ*`nEP9?gMZkH|cm zCYBuSLOxP%h%y3MyQ)Pe#I6x{;*C{${cqQmVGi5;WwzX{kdp3qV{1yDIf^ScKr^Y7 zaAy@v80b1F=&)w=AoF`6$xoO=5*-s6L*ptHEi`-n%Z6#2Em|ny>jhHPxd2m#tW5KJ z&1B7`y{A5UOy=D7_?Jd}<~cB1o~VX1!`pP+#OW=3isAUn470JRbRp%+@GGQ z6>T)BUrKK2t2Iy5BDhZG?EO&mkd<`;w*iY@(g=lNmHh|56vX@qrYt0JeZvd@$ zWsfbk42bs;aFkmvlOH2s8);y9SdeX$`}=581b)l+kA-F>qjPpTh|q}58l7Y==fnRT zPD%r-QChaEieE?Q?{||@UAe`0JJe`Mkm1?$fH1YzWNlnxSK2CYG8eV1P$QYODqyHI z%wF3au1|W&QTN^rd2vEwlqz%N&#u@s(y%vZsHxgQZHp*de5YR^jH)g>bem*}sDqH- zieGrbdS`*Mcwq~crfTLGxXiZJkBT(bf^6#$D3KA|3bh}SjrQN_5J$(|oMKU?SN;s9bIqnI?Lr%a z*}irIoHd)(;OjRjzZLBg(?SCMIP^Dj48O4}Z}3nAS>}%T}4KMQ;52L^ zho^NzD^Bw-U9D&{E9|e*iqK_?#!a4Cg|hFRTvyfJ9UWWimjUz-1)NQDvj|}9Ev+nW zj~^AjueO_VM8|>>K0o_g&q-{T(>L=)6w7qm1r;v!&yQNfUQer|zi2%$vbi!Xt_$Mc zkZb2Z@)d|KibGO}Xw{uvGH9$$_0UTOVhG{5)@C@n^s5_|f16l^GkWU|ohlvN(jmJs zh0j4?|CxpE)O1TG&!afv(oMsRs7*|(4#Iv??MvRS)kK^q#eYY2XLB&+ zOY@^`SyhD07b?^}x7?@TCh;x!dz`0Ws?RYwPO`V3{!y=5HqY!$;1*Q(%#WDVXn=Hw z4H=v4Mc87Qb>pG7yJXlmHp~eeEQP0nv-bwbp(b|`X3fK1lh+!_xAsTOi*ihpC&xC9 z+AEJ!-cJ|W*{rb!(R_mx`j6Uny@sPHqBMm3_YjkS} z)W%J0?d3sO9jqKiOHQh~HI#o9)FwJbXJ&+zdFjry64RI&IWVi$SgzsqX+_uWqHm{} z%~~mbi_Y3(ya~wLalmwLoRkF^f$Sl|^RGqBS9XNUnZ%EbUS;O7=(K9CXlIONGIdAw zW#NEVV_KUVx^C!x{)M*hs^A9-@Z+4=#p2$MIB>!zMEj1P1lGY7;q7f)yPcNTXIGz_ zRw9M(Zyp(VBn5=s#?I_7YAI-*0xc1)nk#D0?U>V{&moXXCfd>cXW>kpF;Voq`7z5z z3+2paPu$6m!Bod>z`~#0Ih~88;3a4**_)qwp1&JWtUTZ7i*XfvH~F1Wl-ZTIfW?=D z5kGJH$Y!yHpHtec-F?XipQbqPUfZXz6S1e9XYmN8851M%OlD9q-K&IfbiA7iW>nnU z`VBtU1S}5xK*d;GFWxHAz?3H^7I>rTx6vv_e=~>@Z}IjW%gVm9luE9u9Aqq#c@}Kx zmz7GVm;MPZ*|SsdSS{P5$EK6Z{L`T>U!*NW-hxug4iJ0!g&>4;C}BkV^gYn)F_<^g47JBi|8`Ii}iV8qnli7`~INI+5%EJ-PWtS z(vTrzi&4|ptI@EVMRU=Vh5@yj?kskJM8reCv#n)o&)NgO4svasQ>&P5Oid0p)%$815vBUO9Q$wljqNIDx6YtjR-L7SqUkhS&74pGu?09~oQ}(53 z@b@kz4hEDY$6dkW){iM#)b7cvy2Vhs(>FJWH;kzJ_x#lJ>4LOtl?oWhNVW!#X#8J-HEReCX6ZGz#xjk!a-&*V-80s#Sq<(9QIC z`_g25Zku^8zPm?IBhMTruhFB+_HX;+ur^ECUr=(AP|(4a5A>s00Kng%I@VQZTdsfG7tlk}Rtb8F1t?Yw_0aVrB>V}{AYfg7NP0c|h%A_4vB(J@@{$GO zRgF0LR>^2jL7s3Cm2ZhaTEO!bb&eT5z=aY+MmHCCTTc-j`w zKZB8fbX<<5CB740z@_IE3(_h%Y$5Pw=R!VO$agg#e-b3NQ9naOkS7iL7fMiVwCj8mxa7Cd4q^L7Fk&;= z7xBHEtxe~dVwU}cc=yEl5Vz*1ydejSb5>0LTB-BCbuq3V#Y5)_?An%#m373AGc9E+ zaV`EPl%=O&zbmr{h%EaMG9A<}tJ$|geyU@5;==^{^mdh6Y^l~HGXid(`^iQT2mMr; zyegK|f-y((*hmr_q^G`@R{SpQ)SxjqjksX@wqG!9He5@HR*gfQ+b>IXJ8h~ric%7< zACKvpcI1)mAkEL?arHDBt3fTDthl+q)@^E+q@! z2b`(N_j@!shMs<$ZTn(hCPRMh_?HEvMbtyt%r#EdY`woPuj_K)Yn`P`J&MaGoxj7m zCjQuRqFfv4bg1RkUK;D(oGnCmKMk%6Z*f;ww3C~7TlRfF_TUx44AKaykti>PsA!Ze zIEj!OP{<}wMb>50DBCGJ5By_p0u`s2=+MHwvYcAWN0r>74muy?M0W>d;@ulLUGPx$ zRAn;A$SM(?BtHveRx0N76NWKGR(;MGa$$%`hSxWbc z{P>=A$J@9&myfN~q1)+oBs_Ci7=r0m*(5Ldr(kqtXh>_>f%^|=$vw>O(Hlz3NP;S2g?tNi_B9y+W0bD?SwIokTJJ)Rw+? zwB&soIpZV5h|gWcS@uxyS=Q)DNOW=g?>yHcwTiL=`zw?G%I5#hbCtyZMy2p6007Q^ z=J{V-)Y{gD&d|~EA214^0A+^qm;8PHM@}#DtlO*uQ?6cMNd&IQ5EP}gNq@%=a)u|e zQ`DJWOn?UV>V@W0h2uvkB_`p^lJ<0+>(bW#wcmZtdG-4AEq!>D1IJEJHmPdq!nvKx zPdCo8`tQf9D>I0wBimuNw0z&)ydM~BZEOQodv$ZXo~*B7zu;`>Z5VhVzK}b3yF2v= zz27zyZ3A<2!F%)HQI}SA8w|WdKC4sA(eq=w#I~*WEV9u}1%kHEcMlS@0=K-qZ;j;X z2}dp}>dpF;J0@>K8+JHaPTHjc`LsR{$x8R~5ziXORGuG2vb=Ou54zNHAf{DHo`d;h z#yTozC<|txyC)k+5ID@6w^&Vk?HEageyP7t`@S#xLSgD=G^!O#vKpa#NiEykp>Nae z(zt0R|2B5l-QhxLK8~!-)75)VQSTf#3|gm=9j)})Se9<~fS!#Yb~DTJz@#*91!lgn z(p{t5=Tt>9TFzN}V~`zZG|s8$pBdSUBV-)tgy)|ttHH^;s0qb8BOIuq&_oz8m99BomQ0l3$oBZ+K2Uy@ zpzD}SxpJ6y3gJMFB25@}Zx)Z!BIelcF%TE5&tvER!UV_ptArBt8>1sBdVhTv?HE*u?scGw)YToUQUJTmyaJ2osR2=maU@AQNAKL*>=KfJPL3<5jU3v=gwTh}f zV^(7rK|&#iH?t;T)Doiv536q6E>%zAGd4mm;)(eP8z~|fMY-g6wpPB(sjz)l8HS$$J<&v~38zxsGo4M*%Y9wAe`Z`*monDnnyqQA6M7Epr@16ywFRC>IBh^y1D8p&c7=tBRRwlQ4orZ<4XIALatIerVkU2h zg|`_+qkFH3k;1ImoHhkbNDfGgGQAANx0*OMkjL6Nf6X>Z@fxH0#iO7f8*r4*4Gkg@ z8?8yJtx`+8AM4$pM~W>a#aRBu>XnK2C6Vj0sf0B(^wWHP<=+xe_%!#&j^LEb#S8evhXYXa-nxbp?~? z9;U=r4GqCD_KcwFS!1+V%Hys_Xic^F`=(J#WNBS2m)^7j)*eyss;YGhuMGGikP%L6 z#_~D3Hn2%|MHR%a2@&Y`Y5^}yj)^b5Rw)D&wqQ!`K^?@_kU(waYUbuQ_~QWpEY7vm zxd0gT)OV&Rydbe5%FkO_&q71=vEn&0%NYqW8I-|BYIG(X$FAR9pH9Di{t%4RjId@K zW{~_2J6E^RwtJi3nTP>uoFK$Q&EI4`6|PA;KO3J(B0n16km1QqAY&7E-i#~{SvF2~ z2gW{Y;A)o`fP%Gbmeup@X>EuR8628meotf_gW}H%!#>H*!98I+aM%)Dl)Q86o&%+| zvA4KRT=C784PygZ{t+D~_L+tfM7t;vaFwG*HpB8O&?}5^%v&hE7%}eI9lAb$Duv94 z8MwPkU)_&izYk4h6HFR?fMherbk@$yO8BNUy2!$TFFM9&FIp$+s-H@{Rt87HCOJGgYroRHV z<@a>CCjiRrSgiu6ur!*ou2CF0N8nHPD!_tvejIr1-??ZS&4vnUMHt%ox0aTS*7f7rTib3=GT0d-caWdWS!GAzPXEBBm?LscpSlSv;ProHry z@E}YIhojCY3wXJW57qoYl-SaEk)m}I{|kxm52K(s2|qSo0{2RLV-+idProJRszjP~ zn2^V38^hb_T18WjcL_DaTeabpiE4WsV@UR4LaYiVFbg3B@o1YL1=R8ND52&OZ}pO1 z;g@JIIgM~2$7#T15Q6aX5rXd_-$}mbUXJe|*w@E=(=?B~v?Rt9vx127Y#ng-q%D;7 zdd7?jZiIAaW5G?h%X?Jp9pcr1ijE{0c4<%J5bg*GM*TroN4(Cxb0+%%!rCLp!quDB zw=v_hcEeqAu${Z@)h-yb8G81#+(Hz5hd^+?TP|Q?jK4cidPN}eNH8k2$KLAIS4>&} z1r{D{DRcN&1wG4joM?Sj+J|(xx%H=mB1iz3XdZOp|8i`*9GUoO4k2;(hpUXA?mL`F7g{iV&hnC4kz6 zLL);uj`<`KW9{3b&5Y4(ceS;BUK6)=czC`XzRx!ipz)tJO*nR_sH1Yw(iRLYbwuJ} z0?+4TF@Y+S=8S^i@|E=gT#!pJom^Xfuo&i7rvV|Mq(v+CAaNMAF#hI>AiFKO^P$S< zw(}XJ(O0OEFD%J$74?FQBe|RoPs2yEkyl_|(5w$yfof@5Md>$WBZ`_X*u_k!T*BQb zx;!OHZm8V2L;Z`40=`ixd!w8lAY57ml86e@a}xVgCZ-UjT)cVg+zY0?yFzenlf8`7 zGp*ls3fqc)Q$#$gHk-s2Ai>0+CSdGwUg{0H$kj^}`){K(1ommDjdu#`g*~k@3V_d1 zkNkWZUOK=6h9O-O(#s+?LuUUJ;pe#o$C?wTFVNP^HfNQjC?A9qrnVBwUnF-D!?!A# zYOZKA%p;N*K|4`kz3Liy$L5v`k<$*W)%0QU;bD zi@@|2C|`z{_TbZ7sn0_*OX*tdUC^1dD+tD^?qZf*X3M^rMR}GKAAr5E>XKUW>sEQ% zQ9ez%>HNE+Rlb$9qBp9)V`wbyBpA&kchGbt-it^|D0Q=kk6e3vM3F-)v?hmM!Y_tn zIKqwaJ>c!SQYK5ka(eG>W;WU*u;@CkRw~{&b(wyC1o**RNcu}9PA%2`0h>`EL+VQibXh=N?D(;bC+-7bZBPcR_-zOAjDCp0nroQx1zs$r5^ucUcDt?% z_=~XHOh_%~szR7;Im}}7`?4U|6%f)-1ljF-9Lf%N#}nRyrW#V*Q*<-ydFl}83KC<@ zGdCyWLkS!%-4X#p4!W~Ns&*~DF|+8KXh1xPJWOGndN`vbrxO*vvF@SweYDhwxv3{7 zwb<*X&*kGYiC$QJcBnci>v^=gOy*d3ydwqjucvtyd?$$~FEpE|Xd_VApn4VtiAop`fCysm1Y#NQ@KR~HDvt;_>%!(TYEoh#VVTJpQ zksbgT`6qNPWwheOie(+nG>M8ykKX;RHkY=KYp4)k+?O|{FH~9rzIEU*5UH@p`>2VZ z$Ul|G1@3(%VXzTCUk}#g4I~Yb-K@z0+hoyRrXZN+j;u3lij<#wC;cgoB|$?N1Hj@L z=~l5}BYH~vOGs!hcg`&`F{zZjWVHN-y>b?HFR1FWjV0?^qje$#0q?A-4hr0)Bz%a* zrDvZMUeq7>1YnM5mr)`s;+emcq&|0@0#BAs%LTX;eW0a*bi&7YK8ZrG8URYKN zqbs#Qp+#7_s*xbo{5Tj-9qT1bwrk0}CCK#wKX1Ik*4Fqm*H!FGuDo9a=e8q1MJgtP z*`VH+;)L2i$RuLy`;Bb+@`wP7@{8lae^HguK_*Z~%2(U`HG*<@dZpd$C*Pe3kPA}2 zbl*)GM(Dh)R~bgZR&6){W7}CzHgQx+RmbZZ`UZ~asmD}|GhKGI-^^|-d|hu;K(*S@ z>G9fvE{ud{ipg%+#42$Q&tyIxZf^EuQX69L7YCF^N)v3Wvtll~wZ?=ttdVHEZ`CKg zizpndPDaaUDMVTzwO-E5!-A(2M~l>+WxIr&(vg`a?lzSjlypam!)P))Al#_z)1)?T zi46pQvJ6d3JBcLtk7G2HkYUwts0@c)arHFo*b~pW-xuK- zT?UOj*fU0@$Q*oJFpyBos`w3H5jk8nY}_X(AesHIYOf(%@OkmJUmFI2ej9_z9o4&= zLlm%~D{uC!1nxk zOo~e1sG#M9Q!o;7{XN%X!dLbNpQ0VBd*oEE5JzieldfVm-VX`Ccv=cc4`^^`7VMMP zlp#3q+5N3d4__P8e{D|nW(4%%OAxn=Fb?ZS^Ni7@WaNeu9>z0xVGT{pdPPPuGz=e= zSUcW2AI4#9 zc=Ezp>m7i_%L_eTVv2-}H%%CHuIJpvCEKoNG!!W9hwis{MJWUB{z{&Q+`ktlMxG3g zS*vPeqn|=3`7%U?bXOrFy=n+$qA>4fBakQahCYbu4V23hW@t=NF;^-3ggDvUEw(L> zNO7^*xzQ#gUrMWa$ug^i*xEGM+<@a01^4(E2~$KZ?1Os`#u=Rb8XED1EtHB!^XHd5 z=~|&+ID#P(d%-FsS%7)$nMZgq!=r@@Cs{->5X8oVQUq)Dc|UV=t*0cOQmC z>)9^^^VD>&@`X;pTWwy0Gva!bk9;T|RPJwHA^lq-wY@K%G(3O6|Ecq8-;mJ=)l7-e zo8gS|ow2W>{rOF$KS&cHB{^73;MTRp)(J%-EqNWRrxFm0dr3wy$20;o3t69-=(Jee z5*kR96zBm?|C7Nv%;-e~cyQUIT#-F~bK$Pc-YRsZBx~F6L|H+?M#f*O z8+nGk?Hq$5e@w2G>+xh$@S)qAavBGF3W-xc@GV-i1iKNbf#jfNz2l zlaxXhMFikQOIj4i8^vlKy3kPKbj#CYL(m}kjICirx+Q%4-lqhye&UIC#b}&7`pspqpx~l_wy+(eatL2b)GiOi2YEl+ z`ZwoWEg?n$pdt>D@oRJO1HU3?_*5ZdaG}{&4^vp}Br^Z7I~R#qW9Dm304X;s(eCUS zv9nB;HnfEHu}Wdwp7!D%z1^LpZ!rB4p}f%pzbfmxH70Ea#GkQuHkKO!MeJdk-u`4I zMgWz9ttjt0n1;aEZhTcxdFWtXzD_%b?em}>hPYQ=uaYJVC%=cn zVSv`?$>KV3c?~mHF)mBoaVW6~8r##A9PKRXfcMq>Tj5FU(4VkyuvtsyfxNU&qeJ0y zUI`Nu=S6{QFJOi)K5HQtX*6<>I$*Q8kUk2xb)^>Mi7+N4Y!9Jzc;7BifLL+fYQ`i)TEAGq}!A@5PlXY#=W$L$IE&0-zG z@%kH6s<~v?O9-Befdfb1Z);aM1DQb26i_H8BwT{<+nequj z{=cDCXC+CSbw=dPH#9J}S@tliHgQEEjRSa|-Y~*&IuTeh^mXcb+8OI72#=PWJh9`r zd$AaNl(^`ZlTLS84SM}Lc^P16Csi67NHPjy*I)lc!%_?7i0f26RC@~)OKp1#Zh|po zMqlNYh~Acd{KV1e)vDg(I)vQUN^cHG{`SH9$_zI0g%Rzn)f!N61J0tTQ^AaGZGZM*=5!R}3#zo8-@L6iPkE^jcEc3Lf* z1nsbwV;>t>XF(P}ab8Z4o%$+FHG~X>RQpdz1iwDtU(>N)l0qo|1Q?WpAcRi+c)(L$ zbQj&v98sr9Tu=gu>0QG7)~&X}0%jA;aRu5)0>A}RqrQuxMLGFl)vH;>Ie~5Xz@*5z zZ*h-a$r5>@!=4Shduo9Do6akg+u-{N_l(mr`*T-^v}xU|Tz45eB|}4AB{5GWfu~|F zoH)&djGzgBbKb+b8zg7P;wfXPO2!Bs){<8}GCI3_P|IZoD zf1LW;^54x4|BgrhZg%)*75c~gm9hVeQ~z7b|0k^v|8Dzl0Q>KC;co!@&nonH!T%52 x|05jx?{@yKGXMTk|5qPh*#E=M|JKL { @@ -54,6 +56,9 @@ export class WireframeRendererComponent implements OnInit { // Check if model key exists and has some value if (res && res.model) { try { + + this.initialPrompt = res.prompt; + // Optional: if model is already a stringified JSON const maybeParsed = typeof res.model === 'string' ? JSON.parse(res.model) : res.model; this.jsonInput = JSON.stringify(maybeParsed, null, 2); @@ -114,11 +119,13 @@ export class WireframeRendererComponent implements OnInit { if (sectionName === 'Children') continue; // already handled - console.log('✅ Generating prompt for:', pageName, sectionName); - console.log('sc name sectionDescription : ', sectionName, ' ', sectionDescription) + console.log('✅ Generating prompt for : ', pageName, 'sectionc name and sectionDescription : ', sectionName, ' ', sectionDescription); const prompt = await this.generatePromptFromSection(sectionName, sectionDescription); // ✅ await sectionPrompts[sectionName] = prompt; + + // console.log( sectionName, ': ', prompt) + } // 🧠 Store all prompts for that page @@ -168,7 +175,7 @@ HTML Only. No CSS. // Step 4 - generatePromptFromSection( + async generatePromptFromSection( sectionName: string, sectionDescription: string, headerId: number = 35, @@ -177,24 +184,48 @@ HTML Only. No CSS. return new Promise((resolve) => { // const fieldType = sectionName.toLowerCase().replace(/\s+/g, '').replace(/section$/, '').trim(); const fieldType = sectionName - .toLowerCase() - .replace(/section$/i, '') // remove trailing 'section' - .replace(/\s{2,}/g, ' ') // multiple spaces to single space - .trim(); // trim leading/trailing spaces - + .toLowerCase() + .replace(/section$/i, '') // remove trailing 'section' + .replace(/\s{2,}/g, ' ') // multiple spaces to single space + .trim(); // trim leading/trailing spaces + this.siteTreeService.getDlf(headerId, operationType, fieldType).subscribe({ - next: (res) => { + next: async (res) => { try { const jsonBlock = res?.javacode; + + // console.log( fieldType , ' json is : ', jsonBlock) + if (jsonBlock) { const parsedJson = typeof jsonBlock === 'string' ? JSON.parse(jsonBlock) : jsonBlock; // const updatedJson = this.replaceContentRecursively(parsedJson, sectionDescription); const finalJsonString = JSON.stringify(parsedJson, null, 2); + // 🧠 Build prompt to LLM with section info + json + full page prompt + const enhancedPrompt = ` +📘 You are an intelligent layout enhancer. +Your task is to update and fill in the values of a given UI section JSON based on the following information: + +- Page-level prompt: "${this.initialPrompt || ''}" +- Section name: "${sectionName}" +- Section description: "${sectionDescription}" + +You must only update relevant values like "title", "text", "description", "label", "placeholder", etc. + +⚠️ DO NOT change the structure. Only update content values. + +JSON: +${finalJsonString} + +✅ Return only the updated JSON. Do not return any explanation. +`; + + // console.log('enhanced query ', enhancedPrompt); + + // const finalJson = await this.calLlm(enhancedPrompt); resolve(finalJsonString); } else { - console.warn('⛔ No JSON found, skipping.'); resolve('{}'); } } catch (err) { @@ -235,7 +266,7 @@ HTML Only. No CSS. pageSections: Record = {}; // final UI map - // Step 5 - generate html code via llm through prompt + // Step 5 - generate html code via java Api through prompt async processAllPagesLiveUpdate() { for (const [pageName, sectionMap] of Object.entries(this.allPagePrompts)) { const sectionHtmls: string[] = []; @@ -436,6 +467,32 @@ HTML Only. No CSS. } + + // // Step 6 + async calLlm(data): Promise { + return new Promise((resolve, reject) => { + console.log('call Llm Start ') + const payload = { + query: data, + conversationId: '677' + }; + console.log(' query : ', payload.query) + this.siteTreeService.callLlm(payload).subscribe({ + next: (res) => { + console.log('response ', res) + if (res && res.responseContent) { + resolve(res.responseContent); + } else { + resolve('⚠️ No response received.'); + } + }, + error: () => resolve('⚠️ LLaMA API failed.') + + }); + }); + } + + // // Step 4 // generatePromptFromSection( // sectionName: string, @@ -485,29 +542,7 @@ HTML Only. No CSS. - // // Step 6 - // async generateJson(data): Promise { - // return new Promise((resolve, reject) => { - // console.log('genearte json start ') - // const payload = { - // query: data.message, - // conversationId: '677' - // }; - // console.log(' query : ', payload.query) - // this.siteTreeService.generateJson(payload).subscribe({ - // next: (res) => { - // console.log('response ', res) - // if (res && res.responseContent) { - // resolve(res.responseContent); - // } else { - // resolve('⚠️ No response received.'); - // } - // }, - // error: () => resolve('⚠️ LLaMA API failed.') - // }); - // }); - // } // pageSections: Record> = {}; // { pageName: { sectionName: html } } // async processAllPagesLiveUpdate() { diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html index 464ad26..3d0276b 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html @@ -57,6 +57,8 @@ + +
@@ -76,11 +78,18 @@
- +
- +
@@ -92,7 +101,9 @@
- + + +
@@ -119,4 +130,4 @@ Generate
-
+
\ No newline at end of file diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts index f7617d8..86fc161 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts @@ -125,6 +125,13 @@ after click this icard inside we upload excel of student data, icard template, a // } // } + isWireframeLoaded = false; + + generateWireframe() { + this.activeTab = 'wireframe'; + this.isWireframeLoaded = true; + } + getSections(data: any): { title: string, description: string }[] { const sections: { title: string, description: string }[] = []; for (const key in data) { @@ -180,7 +187,7 @@ after click this icard inside we upload excel of student data, icard template, a }`; - const suffix = `You are a JSON structure generator for website page hierarchies. + const suffix = ` You are a JSON structure generator for website page hierarchies. You are a JSON structure generator for website UI and sitemap hierarchy. @@ -250,13 +257,13 @@ Analyze the flow implied in the prompt: const payload = { - query: this.rawInputText + suffix, + query: this.rawInputText + ' ' + suffix, conversationId: '677' }; console.log(' query : ', payload.query) - this.siteTreeService.generateJson(payload).subscribe({ + this.siteTreeService.callLlm(payload).subscribe({ next: (res) => { console.log('response ', res) @@ -268,6 +275,7 @@ Analyze the flow implied in the prompt: // 🔁 Now call update API const updatePayload = { + prompt: this.rawInputText + ' ', model: this.inputJson // model format expected by backend }; diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts index da05f0a..742221d 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts @@ -97,6 +97,7 @@ import { MappingruleeditComponent } from './datamanagement/mappingrule/mappingru import { TreeVisualizerComponent } from './fnd/SiteTreeBuilder/tree-visualizer.component'; import { SiteTreeComponent } from './fnd/SiteTreeBuilder/SiteBuilderGrid/SiteTree.component'; +import { EditstepperComponent } from './BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component'; @@ -271,6 +272,9 @@ const routes: Routes = [ { path: 'sitetree', component: SiteTreeComponent, }, + { path: 'stepper', component: EditstepperComponent }, + + { path: 'sitetree/SiteBuilder/:id', component: TreeVisualizerComponent }, diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts index 6453ca4..5de9635 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts @@ -124,6 +124,7 @@ import { WireframeRendererComponent } from './fnd/SiteTreeBuilder/WireframesUi/w import { HomepageEditorComponent } from './fnd/SiteTreeBuilder/HomePage/Templete1/homepage-editor.component'; import { SiteTreeComponent } from './fnd/SiteTreeBuilder/SiteBuilderGrid/SiteTree.component'; import { TreeNodeComponent } from './fnd/SiteTreeBuilder/TreeNode/tree-node.component'; +import { EditstepperComponent } from './BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component'; @NgModule({ @@ -145,6 +146,7 @@ import { TreeNodeComponent } from './fnd/SiteTreeBuilder/TreeNode/tree-node.comp // buildercomponents SiteTreeComponent, + EditstepperComponent, HomepageEditorComponent, WireframeRendererComponent, diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts index 21ef221..2d66447 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts @@ -15,6 +15,7 @@ export const environment = { backendUrl:'http://157.66.191.31:30101/back', nodeUrl:'http://157.66.191.31:31170/llm', + builderUrl:'http://157.66.191.31:31170', diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts index e276c77..34cb441 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts @@ -11,10 +11,12 @@ export const environment = { captchaSiteKey: '6LfrdSUpAAAAALkYDmnvdX3GLLCArgPWNHfXasjP', // backport:'31701/visaproject36808/back', - backendUrl:'http://localhost:9292/back', - nodeUrl:'http://localhost:3000/llm', +// backendUrl:'http://localhost:9292/back', + nodeUrl:'http://157.66.191.31:31170/llm', + builderUrl:'http://157.66.191.31:31170', -// backendUrl:'http://157.66.191.31:30101/back', + + backendUrl:'http://157.66.191.31:30101/back', diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html index 08d3e58..8e178a3 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html @@ -12,6 +12,7 @@ +