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 0000000..59cc834 Binary files /dev/null and b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode.zip differ 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 0000000..a039f77 Binary files /dev/null and b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip differ diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts index 916fefa..e2e91af 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts @@ -326,10 +326,24 @@ button:hover { } .testimonial-card { - border: 1px solid #ddd; - border-radius: 6px; + background: white; padding: 20px; - background: #fff; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0,0,0,0.05); + max-width: 320px; +} +.stars { + color: #f39c12; + font-size: 1.2rem; +} +.author { + margin-top: 15px; + text-align: center; +} +.author .avatar { + width: 50px; + height: 50px; + border-radius: 50%; } .testimonial-card .profile { @@ -346,6 +360,8 @@ button:hover { margin: 10px 0; } + + /* ======= FOOTER ======= */ .footer-1 { background: #f8f8f8; @@ -434,5 +450,2051 @@ button { text-decoration: none; } +.hero-section { + display: flex; + justify-content: space-between; + align-items: center; + gap: 40px; + padding: 40px; + background-color: #ffffff; + font-family: sans-serif; + flex-wrap: wrap; +} + +.hero-left { + flex: 1; + min-width: 300px; +} + +.hero-right { + flex: 1; + min-width: 300px; + display: flex; + justify-content: center; + background-color: #e5e5e5; +} + +.hero-right-img { + width: 100%; + height: auto; +} + +.hero-buttons { + margin-top: 20px; + display: flex; + gap: 16px; +} + +.btn { + padding: 10px 20px; + font-weight: 500; + text-decoration: none; + border-radius: 4px; + transition: all 0.3s ease; +} + +.btn-filled { + background-color: #000; + color: #fff; +} + +.btn-outlined { + background-color: #fff; + color: #000; + border: 1px solid #000; +} + +.hero-centered { + background: #ffffff; + font-family: sans-serif; + text-align: center; + padding: 60px 20px; +} + +.hero-heading { + font-size: 32px; + font-weight: bold; + color: #000000; + margin-bottom: 20px; +} + +.hero-description { + font-size: 16px; + color: #333333; + max-width: 600px; + margin: 0 auto 30px; +} + +.btn-filled { + padding: 10px 20px; + background-color: #000000; + color: #ffffff; + border: none; + border-radius: 4px; + cursor: pointer; + margin-right: 10px; +} + +.btn-outlined { + padding: 10px 20px; + background-color: #ffffff; + color: #000000; + border: 1px solid #000000; + border-radius: 4px; + cursor: pointer; +} + +.hero-image { + width: 100%; + height: auto; + display: block; + margin-top: 40px; + background-color: #e5e5e5; +} + + +/* ======= HERO SECTION (Two Column) ======= */ +.feature-section { + display: flex; + flex-wrap: wrap; + align-items: center; + padding: 60px 40px; + gap: 40px; +} + +.feature-content { + flex: 1; + min-width: 300px; +} + +.feature-content h2 { + font-size: 36px; + font-weight: bold; + margin-bottom: 15px; +} + +.feature-content p { + font-size: 16px; + color: #444; + margin-bottom: 20px; +} + +.cta-buttons { + display: flex; + gap: 15px; + margin-top: 20px; + flex-wrap: wrap; +} + +.cta-buttons a { + padding: 10px 20px; + border-radius: 6px; + text-decoration: none; + font-weight: 500; + transition: 0.3s ease; +} + +.cta-buttons a:hover { + opacity: 0.9; +} + +.feature-section img { + max-width: 100%; + height: auto; + border-radius: 8px; + display: block; +} + +/* ======= PARTNER LOGOS ======= */ +.feature-content .partners-logos { + display: flex; + gap: 10px; + margin-top: 12px; +} + +.feature-content .partners-logos img { + height: 24px; + object-fit: contain; +} + +/* ======= DECORATIONS ======= */ +.decoration { + width: 50px; + height: 50px; + position: absolute; + z-index: 0; + opacity: 0.4; +} + +.decoration.circle { + border-radius: 50%; +} + +.decoration.abstract-green { + clip-path: polygon(0 0, 100% 20%, 80% 100%, 0 100%); +} + +.decoration.abstract-orange { + clip-path: polygon(10% 0%, 90% 0%, 100% 100%, 0% 100%); +} + +/* ======= SCROLL INDICATOR ======= */ +.scroll-indicator { + font-size: 20px; + position: absolute; + animation: bounce 2s infinite; + color: #666; +} + +.scroll-indicator.bottom-left { + bottom: 20px; + left: 20px; +} + +.scroll-indicator.bottom-right { + bottom: 20px; + right: 20px; +} + +@keyframes bounce { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(8px); } +} + +.hero-left-cards { + background: radial-gradient(circle, #1a1f1c, #2d3430); + color: white; + font-family: sans-serif; + padding: 60px 40px; +} + +.hero-container { + display: flex; + flex-wrap: wrap; + gap: 40px; + align-items: center; +} + +.hero-left { + flex: 1; + min-width: 300px; +} + +.hero-logo { + width: 48px; + margin-bottom: 10px; +} + +.hero-label { + font-size: 14px; + text-transform: uppercase; + color: #aaa; +} + +.hero-heading { + font-size: 36px; + font-weight: bold; + margin-top: 10px; +} + +.hero-right { + flex: 1; + display: grid; + gap: 20px; + min-width: 300px; +} + +.hero-card { + background: #1c1f1e; + padding: 20px; + border-radius: 8px; + text-align: center; +} + +.hero-card img.card-img { + width: 100%; + border-radius: 4px; + margin-bottom: 10px; +} + +.card-buttons { + display: flex; + justify-content: center; + gap: 10px; + margin-top: 10px; +} + +.btn-filled { + background: #506c4f; + color: white; + padding: 10px 20px; + border: none; + border-radius: 4px; + text-decoration: none; +} + +.btn-outlined { + border: 1px solid #fff; + background: transparent; + color: white; + padding: 10px 20px; + border-radius: 4px; + text-decoration: none; +} + +.hero-centered-art { + text-align: center; + background: #fff; + padding: 80px 20px; + position: relative; +} + +.hero-centered-art .hero-heading { + font-size: 36px; + color: #333; + font-family: sans-serif; + max-width: 700px; + margin: 0 auto 30px; + line-height: 1.3; +} + +.hero-centered-art .btn-teal { + background-color: #00b3b3; + color: white; + padding: 12px 24px; + border-radius: 4px; + text-decoration: none; + font-weight: 500; + display: inline-block; +} + +.hero-art-left { + position: absolute; + top: 0; + left: 0; + width: 100px; +} + +.hero-art-right { + position: absolute; + bottom: 0; + right: 0; + width: 100px; +} + +.subsection-centered { + text-align: center; + padding: 60px 20px; + max-width: 700px; + margin: 0 auto; + font-family: sans-serif; +} + +.subsection-centered h2 { + font-size: 28px; + margin-bottom: 15px; +} + +.subsection-centered p { + font-size: 16px; + color: #444; +} + +.testimonial-section.simple-testimonial { + padding: 60px 20px; + text-align: center; + background: #fff; + color: #222; +} + +.testimonial-logo img { + height: 40px; + margin-bottom: 20px; +} + +.testimonial-quote { + font-size: 20px; + font-style: italic; + margin-bottom: 30px; + color: #444; + max-width: 800px; + margin-left: auto; + margin-right: auto; +} + +.testimonial-author { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; +} + +.testimonial-author .author-img { + width: 60px; + height: 60px; + border-radius: 50%; + background: #ccc; + object-fit: cover; +} + +.testimonial-author .author-details { + font-size: 14px; + color: #555; + line-height: 1.4; +} +.testimonial-section.dark { + background: #111; + color: #fff; + padding: 60px 20px; + text-align: center; +} + +.testimonial-section.dark .testimonial-heading { + font-size: 32px; + margin-bottom: 30px; +} + +.testimonial-section.dark .testimonial-grid { + display: flex; + gap: 30px; + flex-wrap: wrap; + justify-content: center; +} + +.testimonial-section.dark .testimonial-card { + background: #1a1a1a; + padding: 30px; + border-radius: 12px; + max-width: 350px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); +} + +.testimonial-section.dark .stars { + font-size: 18px; + color: gold; + margin-bottom: 10px; +} + +.testimonial-section.dark .author { + margin-top: 20px; +} + +.testimonial-section.dark .author .avatar { + width: 40px; + height: 40px; + border-radius: 50%; + margin-bottom: 10px; +} +.client-testimonial-section { + padding: 60px 20px; + background-color: #111; + color: #fff; + font-family: sans-serif; + position: relative; + text-align: center; +} + +.testimonial-bg { + position: absolute; + inset: 0; + background-size: cover; + background-position: center; + opacity: 0.1; + z-index: -1; +} + +.testimonial-logo { + width: 80px; + margin-bottom: 20px; +} + +.testimonial-title { + font-size: 32px; + margin-bottom: 40px; +} + +.testimonial-cards { + display: flex; + gap: 30px; + justify-content: center; + flex-wrap: wrap; +} + +.testimonial-card { + background: #222; + border-radius: 8px; + padding: 20px; + max-width: 300px; +} + +.testimonial-card .avatar { + width: 60px; + height: 60px; + border-radius: 50%; + object-fit: cover; +} + +.testimonial-card blockquote { + font-style: italic; + margin: 20px 0; +} + +.testimonial-card .author { + font-size: 14px; + color: #aaa; +} + +.testimonial-card .stars { + color: gold; + font-size: 18px; +} + +.testimonial-cta .btn-filled { + margin-top: 30px; + background: teal; + color: white; + padding: 12px 24px; + text-decoration: none; + border-radius: 4px; +} + +.testimonial-contact p { + margin-top: 10px; +} + +.testimonial-landing { + padding: 60px 20px; + text-align: center; + font-family: sans-serif; +} + +.testimonial-landing.orange { + background-color: #fff3e0; + color: #bf360c; +} + +.landing-title { + font-size: 32px; + margin-bottom: 10px; +} + +.landing-description { + font-size: 16px; + margin-bottom: 40px; + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +.testimonial-grid { + display: flex; + gap: 30px; + justify-content: center; + flex-wrap: wrap; +} + +.testimonial-box { + background: #ffffff; + border: 1px solid #eee; + padding: 20px; + border-radius: 8px; + max-width: 300px; + box-shadow: 0 0 5px rgba(0,0,0,0.05); +} + +.testimonial-box.highlight { + background: #ffe0b2; + border-color: #ff9800; +} + +.testimonial-box blockquote { + font-style: italic; + margin-bottom: 16px; +} + +.author-name { + font-weight: bold; + font-size: 14px; +} + +/* Team secction */ +.team-slider-section { + text-align: center; + padding: 40px; + font-family: sans-serif; +} + +.team-slider-section h2 { + font-size: 32px; + margin-bottom: 10px; +} + +.team-slider-section p { + color: gray; +} + +.team-slider { + margin-top: 40px; + display: flex; + justify-content: center; + gap: 20px; + overflow-x: auto; + padding-bottom: 10px; +} + +.team-member { + min-width: 300px; + border: 1px solid #ddd; + border-radius: 10px; + padding: 20px; + background: #fff; + flex-shrink: 0; + text-align: center; +} + +.team-member img { + width: 100px; + height: 100px; + object-fit: cover; + border-radius: 50%; + margin-bottom: 15px; +} + +.team-member h4 { + margin: 10px 0 5px; +} + +.team-member .social-icons a { + margin: 0 5px; + color: #555; + text-decoration: none; + font-size: 16px; +} + +.team-member .profile-link { + color: #007BFF; + text-decoration: underline; + display: inline-block; + margin-top: 10px; +} +.team-section { + padding: 60px 20px; + background: #fff; + text-align: center; +} +.team-member { + display: inline-block; + margin: 20px; +} +.team-member img { + width: 120px; + height: 120px; + border-radius: 50%; +} +.team-member .name { + margin-top: 10px; + font-weight: bold; +} +.team-member .role { + color: #777; +} + +/* === Team Showcase === */ +#team-showcase { + padding: 60px 20px; + text-align: center; + background-color: #fff; +} +#team-showcase .heading { + font-size: 2.5rem; + margin-bottom: 10px; +} +#team-showcase .subtitle { + color: #666; + margin-bottom: 40px; +} +#team-showcase .group-list { + display: flex; + flex-direction: column; + gap: 50px; +} +#team-showcase .group { + padding: 0 10px; +} +#team-showcase .group .groupName { + font-size: 1.6rem; + font-weight: 600; + margin-bottom: 20px; +} +#team-showcase .group .team-member-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 20px; + justify-items: center; +} +.team-member { + text-align: center; +} +.team-member img { + width: 140px; + height: 140px; + object-fit: cover; + border-radius: 50%; + margin-bottom: 10px; +} +.team-member .name { + font-weight: bold; +} +.team-member .role { + font-size: 0.95rem; + color: #777; +} +.team-showcase { + padding: 60px 20px; + background: #f9f9f9; + text-align: center; +} + +.team-showcase .group { + margin-bottom: 40px; +} + +.team-showcase .group h3 { + font-size: 1.8rem; + margin-bottom: 20px; +} + +.team-showcase .member-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 20px; + justify-items: center; +} + +.team-showcase .member { + background: white; + padding: 15px; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0,0,0,0.05); + max-width: 200px; +} + +.team-showcase .member img { + width: 100px; + height: 100px; + object-fit: cover; + border-radius: 50%; + margin-bottom: 10px; +} + +.team-showcase .member .name { + font-weight: bold; + margin-bottom: 4px; +} + +.team-showcase .member .role { + font-size: 0.9rem; + color: #777; +} + + +/* === Team Grid Section === */ +#team-grid { + padding: 60px 20px; + background-color: #f9f9f9; + text-align: center; +} +#team-grid .tag { + text-transform: uppercase; + font-weight: 600; + color: #ff6600; + margin-bottom: 10px; +} +#team-grid .heading { + font-size: 2.2rem; + margin-bottom: 10px; +} +#team-grid .description { + font-size: 1.1rem; + color: #666; + margin-bottom: 40px; +} +#team-member-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 30px; + justify-items: center; +} +.team-member { + background: white; + padding: 20px; + border-radius: 12px; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.05); + max-width: 250px; + text-align: center; +} +.team-member img { + width: 100%; + height: auto; + border-radius: 8px; +} +.team-member .name { + font-weight: bold; + margin-top: 12px; +} +.team-member .description { + font-size: 0.95rem; + color: #555; + margin-top: 6px; +} + + +/* === FAQ === */ +.faq-section { + padding: 60px 20px; +} +.faq-title { + font-size: 2rem; + margin-bottom: 20px; +} +.faq-subtitle { + font-size: 1rem; + color: #666; + margin-bottom: 30px; +} +.faq-question { + font-weight: bold; + margin-top: 15px; +} +.faq-answer { + margin-bottom: 20px; + color: #444; +} + +/* === Logo Showcase === */ +.logo-showcase { + padding: 60px 20px; + text-align: center; +} +.logo-showcase img { + height: 40px; + margin: 10px 20px; + opacity: 0.8; +} + +/* === HEADER === */ +header[ref="header-1"] { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 40px; + background: #fff; + border-bottom: 1px solid #eee; +} +.logo-1 { + font-size: 1.5rem; + font-weight: bold; +} +.nav-1 { + display: flex; + gap: 20px; + list-style: none; +} +.nav-1 li a { + text-decoration: none; + color: #333; + font-weight: 500; +} +.button-join, .button { + background: black; + color: white; + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; +} + +/* === HERO SECTION === */ +.hero-section { + text-align: center; + padding: 80px 20px; + background: #fff; +} +.hero-heading { + font-size: 2.5rem; + font-weight: bold; + margin-bottom: 20px; +} +.hero-paragraph { + font-size: 1.2rem; + margin-bottom: 30px; + max-width: 700px; + margin-inline: auto; +} +.button-group { + display: flex; + justify-content: center; + gap: 15px; +} +.button-group .button:first-child { + background-color: black; + color: white; +} +.button-group .button:last-child { + background-color: transparent; + border: 1px solid #222; + color: #222; +} + +/* === IMAGE GRID === */ +.image-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 20px; + padding: 40px; + background: #fafafa; +} +.image-tile img { + width: 100%; + border-radius: 8px; + display: block; + box-shadow: 0 2px 6px rgba(0,0,0,0.1); +} +/* Header */ +header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 40px; + background: #fff; + border-bottom: 1px solid #eee; +} +.logo-1 { + font-size: 20px; + font-weight: bold; +} +.nav-1 { + display: flex; + list-style: none; + gap: 20px; +} +.nav-1 li a { + text-decoration: none; + color: #333; + font-weight: 500; +} +.join-btn { + background: black; + color: white; + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; +} + +/* Hero */ +.hero-section { + text-align: center; + padding: 80px 20px; + background: #f9f9f9; +} +.hero-heading { + font-size: 32px; + margin-bottom: 20px; +} +.hero-paragraph { + max-width: 600px; + margin: auto; + color: #555; +} +.button-group { + margin-top: 20px; +} +.button-group .join-btn { + margin-right: 10px; +} + +/* Image Grid */ +.image-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 20px; + padding: 40px; +} +.image-tile img { + width: 100%; + border-radius: 8px; +} +/* ======= HERO SECTION CENTERED ======= */ +.hero-centered { + text-align: center; + padding: 60px 20px; + background: #fdfdfd; + max-width: 100%; +} + +.hero-label { + font-size: 14px; + font-weight: 600; + color: #007bff; + margin-bottom: 10px; + display: block; +} + +.hero-heading { + font-size: 32px; + font-weight: bold; + margin-bottom: 20px; + color: #222; +} + +.hero-description { + font-size: 16px; + color: #555; + max-width: 700px; + margin: 0 auto 30px auto; + line-height: 1.6; +} + +.hero-centered .btn { + font-size: 15px; + padding: 10px 20px; + border-radius: 4px; + text-decoration: none; + display: inline-block; + margin: 0 8px; + cursor: pointer; +} + +/* Button Filled */ +.btn-filled { + background-color: black; + color: white; + border: none; +} + +.btn-filled:hover { + background-color: #333; +} + +/* Button Outlined */ +.btn-outlined { + border: 1px solid black; + background: transparent; + color: black; +} + +.btn-outlined:hover { + background-color: black; + color: white; +} + +.text-dark { + color: #333333; +} +.text-blue { + color: #3b82f6; +} +.site-info { + margin-left: 20px; +} + +.site-info h2 { + font-size: 18px; + margin-bottom: 5px; +} + +.site-info p { + font-size: 14px; + color: #777; +} +.feature-list-section { + padding: 60px 20px; + background-color: #ffffff; + font-family: sans-serif; +} + +.feature-heading { + font-size: 28px; + font-weight: bold; + color: #000; + text-align: center; + margin-bottom: 40px; +} + +.feature-list-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 30px; +} + +.feature-card { + background: white; + border-radius: 10px; + padding: 20px; + box-shadow: 0 2px 10px rgba(0,0,0,0.05); + text-align: center; +} + +.feature-card img { + width: 48px; + height: 48px; + margin-bottom: 20px; +} + +.feature-card h4 { + font-size: 18px; + margin-bottom: 10px; +} + +.feature-card p { + font-size: 14px; + color: #666; + margin-bottom: 15px; +} + +.text-button { + background: none; + color: #000; + font-weight: 500; + text-decoration: underline; + border: none; + cursor: pointer; + padding: 0; +} +.subscription-section { + display: flex; + justify-content: space-between; + align-items: center; + padding: 60px 40px; + background-color: #ffffff; + flex-wrap: wrap; + gap: 40px; +} + +.subscription-heading { + font-size: 24px; + font-weight: bold; + color: #000000; + margin-bottom: 10px; +} + +.subscription-description { + font-size: 14px; + color: #555555; + margin-bottom: 20px; +} + +.subscription-form { + display: flex; + flex-direction: column; + gap: 10px; + max-width: 400px; + width: 100%; +} + +.form-input { + padding: 10px 15px; + font-size: 14px; + border: 1px solid #ccc; + border-radius: 6px; +} + +.form-submit { + background-color: black; + color: white; + padding: 12px 20px; + border: none; + border-radius: 6px; + cursor: pointer; + font-weight: 500; +} + +.form-submit:hover { + background-color: #222; +} + +.subscription-form .disclaimer { + font-size: 12px; + color: #777; +} + +.subscription-form .link-text { + color: #2563eb; + text-decoration: underline; + margin-left: 4px; + font-weight: 500; +} +.cta-gradient-section { + background: linear-gradient(90deg, #fbb034, #ffdd00); + text-align: center; + padding: 40px 20px; + border-radius: 10px; + font-family: sans-serif; +} +.contact-signup-section { + display: flex; + justify-content: space-between; + background-color: #fafafa; + padding: 40px 20px; + border-radius: 12px; + font-family: sans-serif; + flex-wrap: wrap; + gap: 30px; +} + +.contact-form { + display: flex; + align-items: stretch; + gap: 0; + max-width: 300px; +} + +.contact-form input[type="email"] { + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px 0 0 4px; + flex: 1; +} + +.btn-filled-orange { + background-color: #f5a623; + color: white; + border: none; + padding: 10px 20px; + border-radius: 0 4px 4px 0; + cursor: pointer; +} +.hero-banner { + background-color: #fcd34d; + text-align: center; + padding: 80px 20px; + position: relative; + font-family: sans-serif; +} + +.hero-subheading { + font-size: 24px; + color: #333333; + font-weight: 500; + margin-top: 10px; +} + +.hero-button { + background-color: #1f2937; + color: #ffffff; + padding: 10px 24px; + border-radius: 999px; + font-weight: bold; + display: inline-block; + margin-top: 20px; + cursor: pointer; + text-decoration: none; +} + +.decoration { + position: absolute; + z-index: 0; + opacity: 0.5; +} + +.decoration.top-left { + top: 0; + left: 0; +} + +.decoration.top-right { + top: 0; + right: 0; +} +.pricing-section { + padding: 60px 20px; + text-align: center; + background: #fff; + font-family: sans-serif; +} + +.pricing-section .hero-label { + font-size: 14px; + color: #888; + margin-bottom: 10px; +} + +.pricing-section .hero-heading { + font-size: 32px; + font-weight: bold; + margin-bottom: 10px; +} + +.pricing-section .hero-description { + font-size: 16px; + color: #555; + max-width: 600px; + margin: 0 auto 40px; +} + +.pricing-section .pricing-cards { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 30px; +} + +.pricing-card { + background: #f9f9f9; + padding: 30px; + border-radius: 8px; + width: 280px; + box-shadow: 0 2px 10px rgba(0,0,0,0.05); + text-align: left; +} + +.pricing-card h3 { + font-size: 20px; + margin-bottom: 10px; +} + +.pricing-card .price { + font-size: 24px; + font-weight: bold; + color: #000; + margin: 10px 0; +} + +.pricing-card ul { + list-style: none; + padding: 0; + margin: 20px 0; +} + +.pricing-card ul li { + margin-bottom: 10px; + color: #444; +} + +.pricing-card .btn-filled { + display: inline-block; + padding: 10px 20px; + background: black; + color: white; + border-radius: 4px; + text-decoration: none; +} +.pricing-section-dark { + background: #111; + color: #fff; + padding: 60px 20px; + text-align: center; + font-family: sans-serif; +} + +.pricing-section-dark .badge { + text-transform: uppercase; + color: #facc15; + font-weight: 600; + margin-bottom: 10px; +} + +.pricing-section-dark .title { + font-size: 32px; + margin-bottom: 10px; +} + +.pricing-section-dark .description { + color: #ccc; + margin-bottom: 40px; + max-width: 700px; + margin-inline: auto; +} + +.pricing-section-dark .card-grid { + display: flex; + gap: 20px; + flex-wrap: wrap; + justify-content: center; +} + +.pricing-card { + background: #1a1a1a; + padding: 30px; + border-radius: 8px; + width: 280px; + text-align: left; +} + +.pricing-card.highlight { + border: 2px solid #facc15; +} + +.pricing-card .badge { + color: #facc15; + font-weight: bold; + margin-bottom: 8px; +} + +.pricing-card .price { + font-size: 24px; + font-weight: bold; + margin: 10px 0; + color: #fff; +} + +.pricing-card ul { + list-style: none; + padding: 0; + color: #bbb; + margin-bottom: 20px; +} + +.pricing-card ul li { + margin-bottom: 8px; +} + +.pricing-card button { + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + font-weight: 500; +} + +button.filled { + background: #facc15; + color: #000; + border: none; +} + +button.outline { + background: transparent; + color: #facc15; + border: 1px solid #facc15; +} + +button.light { + background: #fff; + color: #111; + border: none; +} +/* ======= PRICING SECTION ======= */ +.pricing-section { + padding: 60px 40px; + background: #fff; + text-align: center; +} + +.pricing-header { + text-align: center; + margin-bottom: 40px; +} + +.pricing-header h2 { + font-size: 28px; + font-weight: bold; + margin-bottom: 10px; +} + +.pricing-header p { + color: #666; + font-size: 16px; +} + +.pricing-tabs { + display: flex; + flex-wrap: wrap; + gap: 15px; + justify-content: center; + list-style: none; + padding: 0; + margin: 20px 0 40px 0; +} + +.pricing-tabs li { + background: #f2f2f2; + padding: 8px 16px; + border-radius: 20px; + cursor: pointer; + font-size: 14px; +} + +.pricing-tabs li:hover { + background: #ddd; +} + +.pricing-hub { + text-align: center; + margin-bottom: 40px; +} + +.pricing-hub h3 { + font-size: 20px; + font-weight: bold; +} + +.pricing-hub p { + color: #444; + font-size: 15px; + margin-bottom: 10px; +} + +.hub-link { + color: #007bff; + font-weight: 500; + text-decoration: underline; +} + +.pricing-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 20px; +} + +.pricing-card { + border: 1px solid #ddd; + border-radius: 6px; + padding: 20px; + background: #fdfdfd; + text-align: center; +} + +.pricing-card h4 { + font-size: 20px; + font-weight: bold; + margin-bottom: 10px; +} + +.pricing-card .price { + font-size: 24px; + font-weight: bold; + margin-bottom: 5px; +} + +.price-yearly { + display: block; + font-size: 14px; + color: #888; +} + +.pricing-card .contacts { + font-size: 14px; + color: #444; + margin-bottom: 10px; +} + +.pricing-card .notes { + font-size: 13px; + color: #777; + margin-bottom: 15px; +} + +.buy-btn { + background: black; + color: white; + padding: 10px 16px; + border: none; + border-radius: 4px; + cursor: pointer; +} + +.buy-btn:hover { + background: #333; +} + + + +.pricing-header { + margin-bottom: 30px; +} + +.pricing-toggle { + display: inline-flex; + border: 1px solid #ccc; + border-radius: 6px; + overflow: hidden; + margin-bottom: 30px; +} + +.pricing-toggle button { + padding: 10px 20px; + border: none; + background: #eee; + cursor: pointer; +} + +.pricing-toggle button.active { + background: #000; + color: #fff; +} + +.pricing-cards { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: center; +} + +.pricing-card { + background: #fff; + border: 1px solid #ddd; + border-radius: 10px; + padding: 30px 20px; + width: 300px; + text-align: left; + box-shadow: 0 0 6px rgba(0,0,0,0.05); +} + +.pricing-card.highlight { + border: 2px solid #000; +} + +.pricing-card h3 { + font-size: 20px; + margin-bottom: 10px; +} + +.pricing-card .price { + font-size: 24px; + font-weight: bold; + margin-bottom: 5px; +} + +.pricing-card .savings, +.pricing-card .bonus { + font-size: 14px; + color: #666; + margin-bottom: 10px; +} + +.pricing-card ul { + list-style: none; + padding: 0; + margin: 15px 0; +} + +.pricing-card ul li { + margin-bottom: 8px; +} + +.pricing-card button { + background: #000; + color: #fff; + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; +} +.pricing-single-plan { + padding: 60px 20px; + background-color: #fff; + text-align: center; + font-family: sans-serif; +} + +.pricing-header .pricing-title { + font-size: 32px; + font-weight: bold; + margin-bottom: 10px; +} + +.pricing-header .pricing-subtitle { + color: #555; + margin-bottom: 40px; + font-size: 16px; +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 30px; + margin-bottom: 40px; +} + +.feature-item { + background: #f9f9f9; + padding: 20px; + border-radius: 8px; + text-align: center; +} + +.feature-icon { + font-size: 24px; + display: block; + margin-bottom: 10px; +} + +.feature-title { + font-weight: bold; + font-size: 18px; + margin-bottom: 5px; +} + +.feature-description { + font-size: 14px; + color: #666; +} + +.plan-card { + background: #f0f4ff; + padding: 30px; + border-radius: 10px; + max-width: 400px; + margin: 0 auto; +} + +.plan-title { + font-size: 24px; + font-weight: bold; + margin-bottom: 10px; +} + +.plan-price { + font-size: 20px; + color: #333; + margin-bottom: 10px; +} + +.plan-description { + font-size: 14px; + color: #555; + margin-bottom: 20px; +} +.contact-form-section { + padding: 60px 20px; + background-color: #fff; + font-family: sans-serif; + text-align: center; +} + +.contact-header .contact-title { + font-size: 28px; + font-weight: bold; + color: #000; + margin-bottom: 10px; +} + +.contact-description { + color: #555; + font-size: 14px; + margin-bottom: 30px; +} + +.contact-form { + max-width: 600px; + margin: 0 auto; + display: grid; + gap: 20px; +} + +.contact-form input[type="text"], +.contact-form input[type="email"], +.contact-form textarea { + width: 100%; + padding: 12px; + border-radius: 4px; + border: 1px solid #ccc; + font-size: 14px; + font-family: inherit; +} + +.contact-form textarea { + resize: vertical; + min-height: 120px; +} + +.checkbox-label { + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + color: #444; + justify-content: start; + text-align: left; +} + +.contact-form .btn-filled { + background-color: black; + color: white; + padding: 12px 24px; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: 500; + font-size: 16px; +} +.contact-info-section { + text-align: center; + padding: 60px 20px; + background-color: #fff; +} + +.contact-profile-image { + width: 80px; + height: 80px; + object-fit: cover; + border-radius: 50%; + margin-bottom: 20px; +} + +.contact-heading { + font-size: 24px; + font-weight: bold; + margin-bottom: 10px; +} + +.contact-description { + font-size: 14px; + color: #666; + max-width: 600px; + margin: 0 auto 40px; + white-space: pre-line; +} + +.contact-columns { + display: flex; + justify-content: center; + gap: 30px; + flex-wrap: wrap; +} + +.contact-column { + flex: 1; + min-width: 240px; + max-width: 300px; + text-align: left; +} + +.contact-icon { + font-size: 24px; + display: block; + margin-bottom: 10px; +} + +.contact-title { + font-weight: bold; + font-size: 16px; + margin-bottom: 6px; +} + +.contact-text { + font-size: 14px; + color: #444; + white-space: pre-line; +} +.contact-split-section { + display: flex; + flex-wrap: wrap; + min-height: 100vh; +} + +.contact-left-panel { + flex: 1; + background-size: cover; + background-position: center; + color: white; + padding: 40px; +} + +.contact-right-panel { + flex: 1; + background-color: #ffffff; + padding: 60px 40px; +} + +.contact-section { + margin-bottom: 30px; +} + +.section-heading { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; +} + +.section-content { + white-space: pre-line; + color: #eee; +} + +.section-highlight { + color: #00ff99; + font-weight: 500; + white-space: pre-line; +} + +.form-heading { + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; +} + +.contact-form input, +.contact-form textarea { + display: block; + width: 100%; + padding: 12px; + margin-bottom: 16px; + border: 1px solid #ccc; + border-radius: 6px; + font-size: 14px; +} + +.contact-form textarea { + height: 120px; + resize: vertical; +} + +.contact-form .btn-filled { + background-color: #00c981; + color: white; + padding: 12px 24px; + border: none; + border-radius: 6px; + cursor: pointer; +} + +.contact-page { + font-family: sans-serif; +} + +.hero-banner { + background-size: cover; + background-position: center; + padding: 80px 20px; + color: #fff; + text-align: center; +} + +.contact-three-column { + display: flex; + flex-wrap: wrap; + gap: 40px; + padding: 60px 20px; + justify-content: space-between; + background: #fff; +} + +.contact-map { + flex: 1; + min-width: 280px; + border: none; + height: 300px; +} + +.contact-info { + flex: 1; + min-width: 280px; +} + +.contact-form { + flex: 1; + min-width: 280px; +} + +.contact-form input, +.contact-form textarea { + width: 100%; + padding: 12px; + margin-bottom: 16px; + font-size: 14px; + border: 1px solid #ccc; + border-radius: 6px; +} + +.contact-form textarea { + resize: vertical; + height: 100px; +} + +.contact-form button { + background-color: #007bff; + color: #fff; + padding: 12px 24px; + border: none; + border-radius: 6px; + cursor: pointer; +} +.contact-getintouch { + background-color: #084c61; + color: #ffffff; + padding: 60px 20px; + text-align: center; +} + +.contact-getintouch h2 { + font-size: 28px; + margin-bottom: 30px; +} + +.contact-getintouch .columns { + display: flex; + flex-wrap: wrap; + gap: 30px; + justify-content: center; +} + +.contact-getintouch .column { + flex: 1 1 250px; + text-align: left; +} + +.contact-getintouch .column h4 { + margin-top: 10px; + font-size: 18px; +} + +.contact-getintouch .column p { + font-size: 14px; + margin: 4px 0; +} + +.contact-messageus { + background-color: #ffffff; + padding: 60px 20px; + display: flex; + flex-wrap: wrap; + gap: 40px; + align-items: flex-start; +} + +.contact-messageus .left-column { + flex: 1 1 300px; +} + +.contact-messageus .left-column h2 { + font-size: 24px; + font-weight: bold; + margin-bottom: 10px; +} + +.contact-messageus .left-column p { + font-size: 14px; + color: #444; + margin-bottom: 15px; +} + +.contact-messageus .form { + flex: 1 1 400px; +} + +.contact-messageus form { + display: flex; + flex-direction: column; + gap: 15px; +} + +.contact-messageus form input, +.contact-messageus form textarea { + padding: 10px; + border-radius: 4px; + border: 1px solid #ccc; +} + +.contact-messageus form button { + background-color: #c3d500; + color: #000; + border: none; + padding: 12px; + border-radius: 4px; + cursor: pointer; +} + `; diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts index c904602..9cf5201 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts @@ -20,6 +20,8 @@ export class WireframeRendererComponent implements OnInit { jsonInput = ''; + initialPrompt = ''; + id: number; @@ -45,7 +47,7 @@ export class WireframeRendererComponent implements OnInit { return Object.keys(obj || {}); } - // Step 1 + // Step 1 get data from that id fetchTreeById(id: number) { this.siteTreeService.getById(id).subscribe({ next: (res) => { @@ -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 @@ +