wiredrame
This commit is contained in:
parent
457a3f35ca
commit
7b855d6553
@ -25,6 +25,7 @@
|
||||
<div class=" card-block container">
|
||||
|
||||
<clr-timeline [clrLayout]="layout.direction">
|
||||
|
||||
<clr-timeline-step [clrState]="timelineStyle.step0.state">
|
||||
<clr-timeline-step-header>Step 0</clr-timeline-step-header>
|
||||
<clr-timeline-step-title> <span *ngIf="!appToUpdate">Application</span><span *ngIf="appToUpdate"> Update
|
||||
@ -233,11 +234,6 @@
|
||||
<option *ngFor="let item of selectnationality" [value]="item.id">{{item.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="clr-col-sm-12">
|
||||
<label> Supplier</label>
|
||||
<select formControlName="supplier">
|
||||
|
||||
@ -61,7 +61,8 @@ export class EditstepperComponent implements OnInit {
|
||||
private _fb: FormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private toastr: ToastrService,) { }
|
||||
private toastr: ToastrService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
// this.college = new College();
|
||||
@ -150,30 +151,6 @@ export class EditstepperComponent implements OnInit {
|
||||
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() {
|
||||
@ -190,8 +167,6 @@ export class EditstepperComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
this.onCreate();
|
||||
@ -225,7 +200,7 @@ export class EditstepperComponent implements OnInit {
|
||||
this.toastr.error("Not Added");
|
||||
}
|
||||
});
|
||||
this.rowSelected = this.entryForm.value;
|
||||
this.rowSelected = this.entryForm.value;
|
||||
|
||||
setTimeout(() => {
|
||||
this.ngOnInit();
|
||||
@ -245,8 +220,6 @@ export class EditstepperComponent implements OnInit {
|
||||
tested: 1,
|
||||
updated: 1,
|
||||
published: 1,
|
||||
|
||||
|
||||
created: 1,
|
||||
|
||||
};
|
||||
@ -357,13 +330,6 @@ export class EditstepperComponent implements OnInit {
|
||||
updategenderEdit(gender: string): void { this.rowSelected.gender = gender }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
rsModaldescription = false;
|
||||
goToReplaceStringdescription(row) {
|
||||
this.rowSelected = row; this.rsModaldescription = true;
|
||||
|
||||
@ -76,16 +76,20 @@ export class SubmenuComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
this.menuservice.create1(this.entryForm.value).subscribe((data) => {
|
||||
console.log(data);
|
||||
console.log('after add ', data);
|
||||
if (data) {
|
||||
this.toastr.success('Added successfully');
|
||||
this.ngOnInit();
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
(error) => {
|
||||
console.log('Error in adding data...', +error);
|
||||
console.log('Error in adding data...', error);
|
||||
if (error) {
|
||||
this.toastr.error('Not added Data Getting Some Error');
|
||||
}
|
||||
this.getdata();
|
||||
|
||||
});
|
||||
this.modalAdd = false;
|
||||
@ -106,16 +110,19 @@ export class SubmenuComponent implements OnInit {
|
||||
this.modaldelete = false;
|
||||
console.log("in delete " + id);
|
||||
this.menuservice.delete1(id).subscribe((data) => {
|
||||
console.log(data);
|
||||
console.log('after delete', data);
|
||||
if (data) {
|
||||
this.toastr.success('Deleted successfully');
|
||||
}
|
||||
this.ngOnInit();
|
||||
},
|
||||
(error) => {
|
||||
console.log('Error in adding data...', +error);
|
||||
console.log('Error in adding data...', error);
|
||||
if (error) {
|
||||
this.toastr.error('Not Deleted Data Getting Some Error');
|
||||
}
|
||||
this.ngOnInit();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@ -126,12 +133,16 @@ export class SubmenuComponent implements OnInit {
|
||||
if (data) {
|
||||
this.toastr.success('Updated successfully');
|
||||
}
|
||||
this.getdata();
|
||||
|
||||
},
|
||||
(error) => {
|
||||
console.log('Error in adding data...', +error);
|
||||
console.log('Error in adding data...', error);
|
||||
if (error) {
|
||||
this.toastr.error('Not updated Data Getting Some Error');
|
||||
}
|
||||
this.getdata();
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -61,8 +61,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 5000px; /* Increased from 2000px */
|
||||
min-height: 3000px; /* Increased from 1000px */
|
||||
min-width: 8000px; /* Increased from 2000px */
|
||||
min-height: 5000px; /* Increased from 1000px */
|
||||
}
|
||||
|
||||
.tree-row {
|
||||
@ -760,3 +760,65 @@ textarea.form-control {
|
||||
width: 50%;
|
||||
left: 0;
|
||||
}
|
||||
.section-category {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.section-category h4 {
|
||||
color: #333;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.drop-zone-highlight {
|
||||
background-color: #e3f2fd !important;
|
||||
border: 2px dashed #2196f3 !important;
|
||||
}
|
||||
|
||||
.node-container[draggable="true"]:hover {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.node-container[draggable="true"]:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
/* Drag and Drop Styles */
|
||||
.node-container.dragging {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.95);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.drop-zone {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.drop-zone.drag-over {
|
||||
background-color: rgba(0, 123, 255, 0.1);
|
||||
border: 2px dashed #007bff;
|
||||
border-radius: 8px;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.drop-zone.drag-over .node-card {
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 10px rgba(0, 123, 255, 0.3);
|
||||
}
|
||||
|
||||
/* Section template styles */
|
||||
.no-custom-sections {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.section-templates-container h4 {
|
||||
margin: 15px 0 10px 0;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.section-templates-container h4:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
@ -73,12 +73,28 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<div class="section-templates-container">
|
||||
<div class="section-templates-container">
|
||||
<!-- Default Sections -->
|
||||
<h4>Default Sections</h4>
|
||||
<div class="section-template" *ngFor="let template of sectionTemplates"
|
||||
(click)="addSectionFromTemplate(template)">
|
||||
<div class="section-template-title">{{ template.type }}</div>
|
||||
<div class="section-template-desc">{{ template.description }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Sections -->
|
||||
<h4 style="margin-top: 20px;">Custom Sections</h4>
|
||||
<div class="section-template" *ngFor="let customSection of customSections"
|
||||
(click)="addCustomSectionFromList(customSection)">
|
||||
<div class="section-template-title">{{ customSection.type }}</div>
|
||||
<div class="section-template-desc">{{ customSection.description }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Message when no custom sections -->
|
||||
<div *ngIf="customSections.length === 0" class="no-custom-sections"
|
||||
style="padding: 15px; text-align: center; color: #666; font-style: italic; border: 1px dashed #ccc; border-radius: 4px;">
|
||||
No custom sections created yet. Create one below!
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-section-form">
|
||||
<h4>Custom Section</h4>
|
||||
@ -99,30 +115,32 @@
|
||||
</div>
|
||||
|
||||
<!-- Edit Section Offcanvas -->
|
||||
<div class="offcanvas edit-section" [class.show]="showEditSectionOffcanvas">
|
||||
<div class="offcanvas-header">
|
||||
<div class="offcanvas-title">Edit Section: {{ selectedSection?.type }}</div>
|
||||
<button class="close-btn" (click)="closeEditSectionOffcanvas()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<div class="offcanvas edit-section" [class.show]="showEditSectionOffcanvas">
|
||||
<div class="offcanvas-header">
|
||||
<div class="offcanvas-title">Edit Section: {{ selectedSection?.type }}</div>
|
||||
<button class="close-btn" (click)="closeEditSectionOffcanvas()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Section Type</label>
|
||||
<input type="text" class="form-control" [(ngModel)]="selectedSection.type" *ngIf="selectedSection"
|
||||
placeholder="Section type">
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Section Type</label>
|
||||
<input type="text" class="form-control" [(ngModel)]="selectedSection.type" *ngIf="selectedSection"
|
||||
placeholder="Section type">
|
||||
</div>
|
||||
<div class="form-group mt-3">
|
||||
<label class="form-label">Content</label>
|
||||
<textarea class="form-control" [(ngModel)]="selectedSection && selectedSection.content" rows="8"
|
||||
placeholder="Enter content"></textarea>
|
||||
</div>
|
||||
<div class="form-actions mt-4">
|
||||
<button class="btn btn-secondary" (click)="closeEditSectionOffcanvas()">Cancel</button>
|
||||
<button class="btn btn-primary" (click)="saveSection()">Save Changes</button>
|
||||
</div>
|
||||
<div class="form-group mt-3">
|
||||
<label class="form-label">Content</label>
|
||||
<!-- Fixed binding here -->
|
||||
<textarea class="form-control" [(ngModel)]="selectedSection.content" *ngIf="selectedSection" rows="8"
|
||||
placeholder="Enter content"></textarea>
|
||||
</div>
|
||||
<div class="form-actions mt-4">
|
||||
<button class="btn btn-secondary" (click)="closeEditSectionOffcanvas()">Cancel</button>
|
||||
<button class="btn btn-primary" (click)="saveSection()">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" [class.show]="showAddChildModal">
|
||||
<div class="modal-backdrop" *ngIf="showAddChildModal" (click)="closeAddChildModal()"></div>
|
||||
<div class="modal-dialog" *ngIf="showAddChildModal">
|
||||
@ -149,59 +167,98 @@
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">Processing...</div>
|
||||
</div>
|
||||
<ng-template #pageTemplate let-page>
|
||||
<div class="node-container" draggable="true" (dragstart)="onPageDragStart(page)" (dragend)="onPageDragEnd()"
|
||||
(dragover)="$event.preventDefault()" (drop)="onPageDrop(page); $event.preventDefault()">
|
||||
<div class="connection-line" *ngIf="page.parent && page.parent !== rootPage"></div>
|
||||
<div class="node-card" [class.selected]="selectedPage === page" (click)="selectPage(page)">
|
||||
<div class="node-header">
|
||||
<i class="node-icon" [class]="getNodeIcon(page)"></i>
|
||||
<span class="node-title">{{ page.name }}</span>
|
||||
<!-- Updated page template with proper connector lines -->
|
||||
<ng-template #pageTemplate let-page>
|
||||
<div class="node-container drop-zone"
|
||||
draggable="true"
|
||||
(dragstart)="onPageDragStart(page, $event)"
|
||||
(dragend)="onPageDragEnd($event)"
|
||||
(dragover)="onPageDragOver($event)"
|
||||
(dragleave)="onPageDragLeave($event)"
|
||||
(drop)="onPageDrop(page, $event)"
|
||||
style="position: relative; margin: 20px 0;">
|
||||
|
||||
<!-- Single consistent connector line from parent -->
|
||||
<div *ngIf="page.parent && page.parent !== rootPage"
|
||||
style="position: absolute; top: -20px; left: 50%; width: 2px; height: 20px; background-color: #6c757d !important; z-index: 1; transform: translateX(-50%);"></div>
|
||||
|
||||
<div class="node-card" [class.selected]="selectedPage === page" (click)="selectPage(page)">
|
||||
<div class="node-header">
|
||||
<i class="node-icon" [class]="getNodeIcon(page)"></i>
|
||||
<span class="node-title">{{ page.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sections-container" cdkDropList [cdkDropListData]="page.sections"
|
||||
(cdkDropListDropped)="onSectionDrop($event)">
|
||||
|
||||
<!-- Show message and add button for empty pages -->
|
||||
<div *ngIf="page.sections.length === 0" class="empty-page-container"
|
||||
style="padding: 20px; text-align: center; border: 2px dashed #ccc; margin: 10px 0; border-radius: 8px;">
|
||||
<p style="color: #666; margin: 0 0 10px 0; font-style: italic;">No sections added yet</p>
|
||||
<button class="btn btn-primary btn-sm" (click)="showAddSectionForEmptyPage(page, $event)"
|
||||
style="background-color: #007bff !important; border: none; padding: 8px 16px; border-radius: 4px; color: white;">
|
||||
<i class="fas fa-plus"></i> Add First Section
|
||||
</button>
|
||||
</div>
|
||||
<div class="sections-container" cdkDropList [cdkDropListData]="page.sections"
|
||||
(cdkDropListDropped)="onSectionDrop($event)">
|
||||
<!-- Update the section card template within the #pageTemplate -->
|
||||
<!-- Section card with improved hover button placement -->
|
||||
<div class="section-card" *ngFor="let section of page.sections; let i = index" cdkDrag [cdkDragData]="section"
|
||||
(click)="selectSection(section); $event.stopPropagation()">
|
||||
|
||||
<div class="section-title">{{ section.type }}</div>
|
||||
<div class="section-content" [innerHTML]="getSectionContentHtml(section.content)"></div>
|
||||
<div class="section-actions">
|
||||
<button class="section-action-btn" title="Edit Section" (click)="editSection(section, $event)">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
<button class="section-action-btn" title="Delete Section" (click)="deleteSection(section, page, $event)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Repositioned add button that appears on hover -->
|
||||
<div class="add-section-hover-btn" (click)="openAddSectionOffcanvas(page, i, $event)" title="Add Section">
|
||||
<i class="fas fa-plus"></i>
|
||||
</div>
|
||||
|
||||
<!-- Existing sections -->
|
||||
<div class="section-card" *ngFor="let section of page.sections; let i = index" cdkDrag [cdkDragData]="section"
|
||||
(click)="selectSection(section); $event.stopPropagation()">
|
||||
<div class="section-title">{{ section.type }}</div>
|
||||
<div class="section-content" [innerHTML]="getSectionContentHtml(section.content)"></div>
|
||||
<div class="section-actions">
|
||||
<button class="section-action-btn" title="Edit Section" (click)="editSection(section, $event)">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
<button class="section-action-btn" title="Delete Section" (click)="deleteSection(section, page, $event)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- <div class="add-section-btn" (click)="openSectionOffcanvas(page); $event.stopPropagation()">
|
||||
<i class="fas fa-plus"></i> Add Section
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="node-actions">
|
||||
<button class="node-action-btn" title="Add Child Page"
|
||||
(click)="showAddChildPageModal(page); $event.stopPropagation()">
|
||||
<div class="add-section-hover-btn" (click)="openAddSectionOffcanvas(page, i, $event)" title="Add Section">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
<button class="node-action-btn" title="Delete Page" (click)="deletePage(page, $event)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="add-child-hover-btn" (click)="showAddChildPageModal(page); $event.stopPropagation()">
|
||||
<i class="fas fa-plus"></i>
|
||||
|
||||
<!-- Add section button for pages with existing sections -->
|
||||
<div *ngIf="page.sections.length > 0" class="add-section-btn"
|
||||
(click)="openAddSectionOffcanvas(page, -1, $event)"
|
||||
style="padding: 10px; text-align: center; border: 1px dashed #007bff; margin-top: 10px; border-radius: 4px; cursor: pointer; color: #007bff;">
|
||||
<i class="fas fa-plus"></i> Add Section
|
||||
</div>
|
||||
</div>
|
||||
<div class="children-container" *ngIf="page.children && page.children.length > 0">
|
||||
<ng-container *ngFor="let childPage of page.children">
|
||||
<ng-container *ngTemplateOutlet="pageTemplate; context: { $implicit: childPage }"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<div class="node-actions">
|
||||
<button class="node-action-btn" title="Add Child Page"
|
||||
(click)="showAddChildPageModal(page); $event.stopPropagation()">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
<button class="node-action-btn" title="Delete Page" (click)="deletePage(page, $event)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="add-child-hover-btn" (click)="showAddChildPageModal(page); $event.stopPropagation()">
|
||||
<i class="fas fa-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- Connector to children - only show if has children -->
|
||||
<div *ngIf="page.children && page.children.length > 0">
|
||||
<!-- Vertical line down from current node -->
|
||||
<div style="position: absolute; bottom: -20px; left: 50%; width: 2px; height: 20px; background-color: #6c757d !important; z-index: 1; transform: translateX(-50%);"></div>
|
||||
|
||||
<!-- Horizontal line connecting all children (only if more than 1 child) -->
|
||||
<div *ngIf="page.children.length > 1" class="horizontal-connector"
|
||||
style="position: absolute; bottom: -20px; height: 2px; background-color: #6c757d !important; z-index: 1;"
|
||||
[style.left.px]="getHorizontalConnectorLeft(page.children.length)"
|
||||
[style.width.px]="getHorizontalConnectorWidth(page.children.length)"></div>
|
||||
</div>
|
||||
|
||||
<!-- Children container -->
|
||||
<div class="children-container" *ngIf="page.children && page.children.length > 0"
|
||||
style="display: flex; justify-content: center; gap: 40px; margin-top: 40px;">
|
||||
<ng-container *ngFor="let childPage of page.children">
|
||||
<ng-container *ngTemplateOutlet="pageTemplate; context: { $implicit: childPage }"></ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
@ -138,6 +138,21 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
customSections = [
|
||||
{
|
||||
type: 'Visa Application',
|
||||
description: 'Visa application form and process',
|
||||
content: 'Complete visa application with required documents and processing information.'
|
||||
},
|
||||
{
|
||||
type: 'Visa Application 2',
|
||||
description: 'Alternative visa application layout',
|
||||
content: 'Alternative visa application design with step-by-step guidance.'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
constructor(
|
||||
private siteTreeService: SiteTreeservice,
|
||||
private route: ActivatedRoute,
|
||||
@ -311,7 +326,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
// 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
|
||||
this.gridTranslateX = -1300; // Set left position to -900px
|
||||
|
||||
// Position vertically to place root node near top with some padding
|
||||
const topPadding = 50; // Pixels from top of viewport
|
||||
@ -694,19 +709,19 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
// Save section changes
|
||||
saveSection(): void {
|
||||
if (this.selectedSection && this.currentParentPage) {
|
||||
// Find the section in the parent page
|
||||
const index = this.currentParentPage.sections.findIndex(s =>
|
||||
s.type === this.selectedSection.type && s.content === this.selectedSection.content);
|
||||
|
||||
if (index !== -1) {
|
||||
// Update the section with edited values
|
||||
this.currentParentPage.sections[index] = { ...this.selectedSection };
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
if (this.selectedSection && this.currentParentPage) {
|
||||
// Find the original section in the parent page by reference
|
||||
const originalSection = this.currentParentPage.sections.find(s => s === this.selectedSection);
|
||||
|
||||
if (originalSection) {
|
||||
// Update the original section directly
|
||||
originalSection.type = this.selectedSection.type;
|
||||
originalSection.content = this.selectedSection.content;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
this.closeEditSectionOffcanvas();
|
||||
}
|
||||
this.closeEditSectionOffcanvas();
|
||||
}
|
||||
|
||||
// Add section from a template
|
||||
addSectionFromTemplate(template: any): void {
|
||||
@ -736,37 +751,79 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
// Similar update for addCustomSection
|
||||
addCustomSection(): void {
|
||||
if (this.currentParentPage && this.customSectionType) {
|
||||
// If in edit mode, update the selected section
|
||||
if (this.editMode === 'section' && this.selectedSection) {
|
||||
this.selectedSection.type = this.customSectionType;
|
||||
this.selectedSection.content = this.customSectionContent || '';
|
||||
} else {
|
||||
// Otherwise, create a new section
|
||||
const newSection: Section = {
|
||||
type: this.customSectionType,
|
||||
content: this.customSectionContent || ''
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
this.closeOffcanvas();
|
||||
// Add to current page
|
||||
if (this.currentSectionIndex >= 0) {
|
||||
this.currentParentPage.sections.splice(this.currentSectionIndex + 1, 0, newSection);
|
||||
} else {
|
||||
this.currentParentPage.sections.push(newSection);
|
||||
}
|
||||
|
||||
// Add to custom sections list if it doesn't exist
|
||||
const existingCustomSection = this.customSections.find(cs => cs.type === this.customSectionType);
|
||||
if (!existingCustomSection) {
|
||||
this.customSections.push({
|
||||
type: this.customSectionType,
|
||||
description: 'Custom section',
|
||||
content: this.customSectionContent || ''
|
||||
});
|
||||
}
|
||||
|
||||
// Add to available section types
|
||||
if (!this.availableSectionTypes.includes(this.customSectionType)) {
|
||||
this.availableSectionTypes.push(this.customSectionType);
|
||||
}
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
this.closeAddSectionOffcanvas();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
addCustomSectionFromList(customSection: any): void {
|
||||
if (this.currentParentPage) {
|
||||
const newSection: Section = {
|
||||
type: customSection.type,
|
||||
content: customSection.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);
|
||||
}
|
||||
|
||||
if (!this.availableSectionTypes.includes(customSection.type)) {
|
||||
this.availableSectionTypes.push(customSection.type);
|
||||
}
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
this.closeAddSectionOffcanvas();
|
||||
}
|
||||
}
|
||||
|
||||
// Edit section
|
||||
// Edit section - modify this function
|
||||
editSection(section: Section, event: Event): void {
|
||||
event.stopPropagation();
|
||||
this.openEditSectionOffcanvas(section, this.selectedPage, event);
|
||||
}
|
||||
event.stopPropagation();
|
||||
// Store reference to the original section
|
||||
this.selectedSection = section; // Don't clone, use direct reference
|
||||
this.currentParentPage = this.selectedPage;
|
||||
this.editMode = 'section';
|
||||
this.showEditSectionOffcanvas = true;
|
||||
|
||||
// Listen for clicks outside offcanvas
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', this.handleOutsideClick);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// Cancel edit
|
||||
cancelEdit(): void {
|
||||
@ -802,40 +859,113 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
// DRAG AND DROP METHODS
|
||||
|
||||
// Handle page drag start
|
||||
onPageDragStart(page: Page): void {
|
||||
this.draggedPage = page;
|
||||
onPageDragStart(page: Page, event: DragEvent): void {
|
||||
this.draggedPage = page;
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('text/html', page.name);
|
||||
}
|
||||
|
||||
// Add visual feedback
|
||||
setTimeout(() => {
|
||||
const draggedElement = event.target as HTMLElement;
|
||||
if (draggedElement) {
|
||||
draggedElement.classList.add('dragging');
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
onPageDragOver(event: DragEvent): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
}
|
||||
|
||||
// Add visual feedback
|
||||
const dropZone = event.currentTarget as HTMLElement;
|
||||
if (dropZone) {
|
||||
dropZone.classList.add('drag-over');
|
||||
}
|
||||
}
|
||||
|
||||
onPageDragLeave(event: DragEvent): void {
|
||||
const dropZone = event.currentTarget as HTMLElement;
|
||||
if (dropZone) {
|
||||
dropZone.classList.remove('drag-over');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle page drag end
|
||||
onPageDragEnd(): void {
|
||||
this.draggedPage = null;
|
||||
this.dropTargetPage = null;
|
||||
onPageDragEnd(event: DragEvent): void {
|
||||
// Remove visual feedback
|
||||
const draggedElement = event.target as HTMLElement;
|
||||
if (draggedElement) {
|
||||
draggedElement.classList.remove('dragging');
|
||||
}
|
||||
|
||||
// Remove all drop zone highlights
|
||||
document.querySelectorAll('.drop-zone').forEach(el => {
|
||||
el.classList.remove('drag-over');
|
||||
});
|
||||
|
||||
this.draggedPage = null;
|
||||
this.dropTargetPage = null;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to new parent
|
||||
if (!targetPage.children) {
|
||||
targetPage.children = [];
|
||||
}
|
||||
|
||||
// Update parent reference
|
||||
this.draggedPage.parent = targetPage;
|
||||
targetPage.children.push(this.draggedPage);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
onPageDrop(targetPage: Page, event: DragEvent): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Remove visual feedback
|
||||
const dropZone = event.currentTarget as HTMLElement;
|
||||
if (dropZone) {
|
||||
dropZone.classList.remove('drag-over');
|
||||
}
|
||||
|
||||
if (!this.draggedPage || this.draggedPage === targetPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent dropping a parent into its own child
|
||||
if (this.isDescendant(targetPage, this.draggedPage)) {
|
||||
console.warn('Cannot drop a parent into its own descendant');
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to new parent
|
||||
if (!targetPage.children) {
|
||||
targetPage.children = [];
|
||||
}
|
||||
|
||||
// Update parent reference
|
||||
this.draggedPage.parent = targetPage;
|
||||
targetPage.children.push(this.draggedPage);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
console.log(`Moved "${this.draggedPage.name}" to "${targetPage.name}"`);
|
||||
}
|
||||
|
||||
private isDescendant(possibleDescendant: Page, ancestor: Page): boolean {
|
||||
if (!ancestor.children) return false;
|
||||
|
||||
for (const child of ancestor.children) {
|
||||
if (child === possibleDescendant) return true;
|
||||
if (this.isDescendant(possibleDescendant, child)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// Handle section drag drop
|
||||
onSectionDrop(event: CdkDragDrop<Section[]>): void {
|
||||
if (event.previousContainer === event.container) {
|
||||
@ -868,6 +998,28 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
showAddSectionForEmptyPage(page: Page, event: Event): void {
|
||||
event.stopPropagation();
|
||||
this.openAddSectionOffcanvas(page, -1, event);
|
||||
}
|
||||
|
||||
// Add these helper methods to your component for proper connector positioning
|
||||
|
||||
getHorizontalConnectorLeft(childrenCount: number): number {
|
||||
// Calculate left position for horizontal connector
|
||||
const nodeWidth = 250; // Approximate node width
|
||||
const gap = 40; // Gap between nodes
|
||||
const totalWidth = (childrenCount * nodeWidth) + ((childrenCount - 1) * gap);
|
||||
return (totalWidth / 2) - (totalWidth / childrenCount / 2);
|
||||
}
|
||||
|
||||
getHorizontalConnectorWidth(childrenCount: number): number {
|
||||
// Calculate width of horizontal connector
|
||||
const nodeWidth = 250; // Approximate node width
|
||||
const gap = 40; // Gap between nodes
|
||||
return (childrenCount - 1) * (nodeWidth + gap);
|
||||
}
|
||||
|
||||
// Save tree data
|
||||
saveTreeData(silent: boolean = false): void {
|
||||
const outputData = this.generateOutputJson();
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
export const COMMON_CSS = `
|
||||
|
||||
body {
|
||||
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -1068,6 +1070,7 @@ body {
|
||||
background: #f8f8f8;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding:20px;
|
||||
}
|
||||
|
||||
.footer-1-section {
|
||||
@ -1204,12 +1207,14 @@ body {
|
||||
.footer-2-wrapper {
|
||||
font-family: sans-serif;
|
||||
background: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.footer-2-section {
|
||||
background: #fff;
|
||||
padding: 40px 30px 20px;
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
}
|
||||
|
||||
.footer-2-top {
|
||||
@ -1769,6 +1774,358 @@ body {
|
||||
background-color: #005fcc;
|
||||
}
|
||||
|
||||
|
||||
/* Master Reset & Base Styles */
|
||||
html, body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
min-height: 100% !important;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 1.6 !important;
|
||||
color: #333 !important;
|
||||
background-color: #fff !important;
|
||||
box-sizing: border-box !important;
|
||||
overflow-x: hidden !important;
|
||||
text-align: left !important; /* Override center alignment */
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Common Container */
|
||||
.container {
|
||||
max-width: 1200px !important;
|
||||
width: 100% !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 20px !important;
|
||||
}
|
||||
|
||||
/* Section Styling for Consistent Flow */
|
||||
section {
|
||||
padding: 80px 0 !important;
|
||||
position: relative !important;
|
||||
width: 100% !important;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
section:nth-child(even) {
|
||||
background-color: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
/* Typography Hierarchy */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0.5em !important;
|
||||
line-height: 1.3 !important;
|
||||
font-weight: 700 !important;
|
||||
color: #111 !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 1rem !important;
|
||||
color: #555 !important;
|
||||
}
|
||||
|
||||
/* Link Styling */
|
||||
a {
|
||||
color: #0066cc !important;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.3s ease !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #004499 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* Button System */
|
||||
.btn, button,
|
||||
.hero-1-btn, .hero-2-btn, .grid-11-btn, .grid-12-btn,
|
||||
.split-8-btn, .split-10-btn, .text-1-btn, .text-2-btn,
|
||||
.cta-3-btn, .faq-4-footer-btn, .faq-5-contact-btn,
|
||||
.faq-6-view-more-btn, .visa-order-btn, .nav-1-btn {
|
||||
display: inline-block !important;
|
||||
padding: 12px 24px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
text-align: center !important;
|
||||
border-radius: 6px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.3s ease !important;
|
||||
border: 2px solid transparent !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.btn-primary, .hero-1-solid, .hero-2-solid, .text-1-solid, .text-2-solid, .cta-3-solid, .nav-1-solid {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
border-color: #0066cc !important;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .hero-1-solid:hover, .hero-2-solid:hover, .text-1-solid:hover, .text-2-solid:hover, .cta-3-solid:hover, .nav-1-solid:hover {
|
||||
background-color: #004499 !important;
|
||||
border-color: #004499 !important;
|
||||
}
|
||||
|
||||
.btn-outline, .hero-1-outline, .hero-2-outline, .text-1-outline, .text-2-outline, .cta-3-outline, .nav-1-outline {
|
||||
background-color: transparent !important;
|
||||
color: #0066cc !important;
|
||||
border-color: #0066cc !important;
|
||||
}
|
||||
|
||||
.btn-outline:hover, .hero-1-outline:hover, .hero-2-outline:hover, .text-1-outline:hover, .text-2-outline:hover, .cta-3-outline:hover, .nav-1-outline:hover {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Image Styling */
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
input, textarea, select {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
padding: 12px !important;
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: 6px !important;
|
||||
width: 100% !important;
|
||||
transition: border-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus {
|
||||
outline: none !important;
|
||||
border-color: #0066cc !important;
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Navigation Override */
|
||||
.nav-1-navbar {
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05) !important;
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
|
||||
/* Blog Section Fixes */
|
||||
.tn1-blog-section, .blog-2-section {
|
||||
background-color: inherit !important;
|
||||
padding: 80px 20px !important;
|
||||
}
|
||||
|
||||
.tn1-blog-card, .blog-2-card {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.tn1-blog-card:hover, .blog-2-card:hover {
|
||||
transform: translateY(-5px) !important;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.12) !important;
|
||||
}
|
||||
|
||||
/* FAQ Sections */
|
||||
.faq-4-section, .faq-5-section, .faq-6-block, .faq-7-container {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.faq-4-question, .faq-5-question, .faq-6-question, .faq-7-question {
|
||||
background-color: #f1f1f1 !important;
|
||||
transition: background-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
.faq-4-question:hover, .faq-5-question:hover, .faq-6-question:hover, .faq-7-question:hover {
|
||||
background-color: #e7e7e7 !important;
|
||||
}
|
||||
|
||||
/* Footer Override */
|
||||
.footer-1-section, .footer-2-section {
|
||||
background-color: #f8f8f8 !important;
|
||||
border-top: 1px solid #eee !important;
|
||||
}
|
||||
|
||||
/* Contact Section */
|
||||
.contact-1-section {
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.contact-1-submit-btn {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.contact-1-submit-btn:hover {
|
||||
background-color: #004499 !important;
|
||||
}
|
||||
|
||||
/* Hero Section Enhancements */
|
||||
.hero-1-two-column, .hero-2-centered-hero, .hero-9-section {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.hero-1-placeholder, .hero-2-placeholder, .hero-9-placeholder {
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important;
|
||||
overflow: hidden !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* Split Sections */
|
||||
.split-8-section, .split-10-section {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.split-8-placeholder, .split-10-placeholder {
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Testimonial Styling */
|
||||
testimonial {
|
||||
display: block !important;
|
||||
max-width: 800px !important;
|
||||
margin: 40px auto !important;
|
||||
padding: 35px !important;
|
||||
background-color: #fff !important;
|
||||
border-radius: 10px !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08) !important;
|
||||
font-style: italic !important;
|
||||
color: #444 !important;
|
||||
position: relative !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 1.1rem !important;
|
||||
border-left: 5px solid #0066cc !important;
|
||||
}
|
||||
|
||||
testimonial::before {
|
||||
content: '"' !important;
|
||||
font-size: 5rem !important;
|
||||
color: #0066cc !important;
|
||||
position: absolute !important;
|
||||
left: 15px !important;
|
||||
top: -10px !important;
|
||||
opacity: 0.2 !important;
|
||||
font-family: Georgia, serif !important;
|
||||
}
|
||||
|
||||
section:nth-child(even) testimonial {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* Custom styles container */
|
||||
styles {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 992px) {
|
||||
h1 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 60px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
section {
|
||||
padding: 50px 0 !important;
|
||||
}
|
||||
|
||||
.hero-1-two-column, .split-8-section, .split-10-section {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.btn, button {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.hero-1-button-group, .hero-2-button-group {
|
||||
flex-direction: column !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
body {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 40px 0 !important;
|
||||
}
|
||||
|
||||
testimonial {
|
||||
padding: 25px !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations and Transitions */
|
||||
.fade-in {
|
||||
animation: fadeIn 1s ease-in-out !important;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0 !important;
|
||||
transform: translateY(20px) !important;
|
||||
}
|
||||
to {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure smooth scrolling */
|
||||
html {
|
||||
scroll-behavior: smooth !important;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,94 @@
|
||||
export const COMMON_CSS2 = `
|
||||
/* Reset and base styles */
|
||||
html, body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
min-height: 100% !important;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
line-height: 1.6 !important;
|
||||
color: #333 !important;
|
||||
background-color: #f9f9f9 !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Content container to ensure consistent padding/margins */
|
||||
|
||||
/* Alternate section background for visual separation */
|
||||
section:nth-child(even) {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* Typography enhancements */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0 !important;
|
||||
line-height: 1.2 !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
section {
|
||||
padding: 40px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Smooth scrolling for a more unified experience */
|
||||
html {
|
||||
scroll-behavior: smooth !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Button consistency */
|
||||
.button, button, input[type="button"], input[type="submit"] {
|
||||
display: inline-block !important;
|
||||
padding: 10px 20px !important;
|
||||
border-radius: 4px !important;
|
||||
background-color: #0066cc !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
cursor: pointer !important;
|
||||
transition: background-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
.button:hover, button:hover, input[type="button"]:hover, input[type="submit"]:hover {
|
||||
background-color: #004499 !important;
|
||||
}
|
||||
|
||||
|
||||
/* Testimonial styling */
|
||||
testimonial {
|
||||
display: block !important;
|
||||
max-width: 800px !important;
|
||||
margin: 40px auto !important;
|
||||
padding: 30px !important;
|
||||
background-color: #fff !important;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
|
||||
font-style: italic !important;
|
||||
color: #555 !important;
|
||||
position: relative !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 1.1rem !important;
|
||||
}
|
||||
|
||||
testimonial::before {
|
||||
content: '"' !important;
|
||||
font-size: 4rem !important;
|
||||
color: #0066cc !important;
|
||||
position: absolute !important;
|
||||
left: 10px !important;
|
||||
top: -10px !important;
|
||||
opacity: 0.2 !important;
|
||||
font-family: Georgia, serif !important;
|
||||
}
|
||||
|
||||
/* For dark backgrounds */
|
||||
section:nth-child(even) testimonial {
|
||||
background-color: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
|
||||
.section-preview {
|
||||
/* ===== RESET & BASE ===== */
|
||||
|
||||
@ -1,5 +1,390 @@
|
||||
export const Download_Css = `
|
||||
body {
|
||||
|
||||
/* Master Reset & Base Styles */
|
||||
html, body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
min-height: 100% !important;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 1.6 !important;
|
||||
color: #333 !important;
|
||||
background-color: #fff !important;
|
||||
box-sizing: border-box !important;
|
||||
overflow-x: hidden !important;
|
||||
text-align: left !important; /* Override center alignment */
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Common Container */
|
||||
.container {
|
||||
max-width: 1200px !important;
|
||||
width: 100% !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 20px !important;
|
||||
}
|
||||
|
||||
/* Section Styling for Consistent Flow */
|
||||
section {
|
||||
padding: 80px 0 !important;
|
||||
position: relative !important;
|
||||
width: 100% !important;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
section:nth-child(even) {
|
||||
background-color: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
/* Typography Hierarchy */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0.5em !important;
|
||||
line-height: 1.3 !important;
|
||||
font-weight: 700 !important;
|
||||
color: #111 !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 1rem !important;
|
||||
color: #555 !important;
|
||||
}
|
||||
|
||||
/* Link Styling */
|
||||
a {
|
||||
color: #0066cc !important;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.3s ease !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #004499 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* Button System */
|
||||
.btn, button,
|
||||
.hero-1-btn, .hero-2-btn, .grid-11-btn, .grid-12-btn,
|
||||
.split-8-btn, .split-10-btn, .text-1-btn, .text-2-btn,
|
||||
.cta-3-btn, .faq-4-footer-btn, .faq-5-contact-btn,
|
||||
.faq-6-view-more-btn, .visa-order-btn, .nav-1-btn {
|
||||
display: inline-block !important;
|
||||
padding: 12px 24px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
text-align: center !important;
|
||||
border-radius: 6px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.3s ease !important;
|
||||
border: 2px solid transparent !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.btn-primary, .hero-1-solid, .hero-2-solid, .text-1-solid, .text-2-solid, .cta-3-solid, .nav-1-solid {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
border-color: #0066cc !important;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .hero-1-solid:hover, .hero-2-solid:hover, .text-1-solid:hover, .text-2-solid:hover, .cta-3-solid:hover, .nav-1-solid:hover {
|
||||
background-color: #004499 !important;
|
||||
border-color: #004499 !important;
|
||||
}
|
||||
|
||||
.btn-outline, .hero-1-outline, .hero-2-outline, .text-1-outline, .text-2-outline, .cta-3-outline, .nav-1-outline {
|
||||
background-color: transparent !important;
|
||||
color: #0066cc !important;
|
||||
border-color: #0066cc !important;
|
||||
}
|
||||
|
||||
.btn-outline:hover, .hero-1-outline:hover, .hero-2-outline:hover, .text-1-outline:hover, .text-2-outline:hover, .cta-3-outline:hover, .nav-1-outline:hover {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Image Styling */
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
input, textarea, select {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
padding: 12px !important;
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: 6px !important;
|
||||
width: 100% !important;
|
||||
transition: border-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus {
|
||||
outline: none !important;
|
||||
border-color: #0066cc !important;
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Navigation Override */
|
||||
.nav-1-navbar {
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05) !important;
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
|
||||
/* Blog Section Fixes */
|
||||
.tn1-blog-section, .blog-2-section {
|
||||
background-color: inherit !important;
|
||||
padding: 80px 20px !important;
|
||||
}
|
||||
|
||||
.tn1-blog-card, .blog-2-card {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.tn1-blog-card:hover, .blog-2-card:hover {
|
||||
transform: translateY(-5px) !important;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.12) !important;
|
||||
}
|
||||
|
||||
/* FAQ Sections */
|
||||
.faq-4-section, .faq-5-section, .faq-6-block, .faq-7-container {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.faq-4-question, .faq-5-question, .faq-6-question, .faq-7-question {
|
||||
background-color: #f1f1f1 !important;
|
||||
transition: background-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
.faq-4-question:hover, .faq-5-question:hover, .faq-6-question:hover, .faq-7-question:hover {
|
||||
background-color: #e7e7e7 !important;
|
||||
}
|
||||
|
||||
/* Footer Override */
|
||||
.footer-1-section, .footer-2-section {
|
||||
background-color: #f8f8f8 !important;
|
||||
border-top: 1px solid #eee !important;
|
||||
}
|
||||
|
||||
/* Contact Section */
|
||||
.contact-1-section {
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.contact-1-submit-btn {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.contact-1-submit-btn:hover {
|
||||
background-color: #004499 !important;
|
||||
}
|
||||
|
||||
/* Hero Section Enhancements */
|
||||
.hero-1-two-column, .hero-2-centered-hero, .hero-9-section {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.hero-1-placeholder, .hero-2-placeholder, .hero-9-placeholder {
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important;
|
||||
overflow: hidden !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* Split Sections */
|
||||
.split-8-section, .split-10-section {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.split-8-placeholder, .split-10-placeholder {
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Testimonial Styling */
|
||||
testimonial {
|
||||
display: block !important;
|
||||
max-width: 800px !important;
|
||||
margin: 40px auto !important;
|
||||
padding: 35px !important;
|
||||
background-color: #fff !important;
|
||||
border-radius: 10px !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08) !important;
|
||||
font-style: italic !important;
|
||||
color: #444 !important;
|
||||
position: relative !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 1.1rem !important;
|
||||
border-left: 5px solid #0066cc !important;
|
||||
}
|
||||
|
||||
testimonial::before {
|
||||
content: '"' !important;
|
||||
font-size: 5rem !important;
|
||||
color: #0066cc !important;
|
||||
position: absolute !important;
|
||||
left: 15px !important;
|
||||
top: -10px !important;
|
||||
opacity: 0.2 !important;
|
||||
font-family: Georgia, serif !important;
|
||||
}
|
||||
|
||||
section:nth-child(even) testimonial {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* Custom styles container */
|
||||
styles {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 992px) {
|
||||
h1 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 60px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
section {
|
||||
padding: 50px 0 !important;
|
||||
}
|
||||
|
||||
.hero-1-two-column, .split-8-section, .split-10-section {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.btn, button {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.hero-1-button-group, .hero-2-button-group {
|
||||
flex-direction: column !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
body {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 40px 0 !important;
|
||||
}
|
||||
|
||||
testimonial {
|
||||
padding: 25px !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations and Transitions */
|
||||
.fade-in {
|
||||
animation: fadeIn 1s ease-in-out !important;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0 !important;
|
||||
transform: translateY(20px) !important;
|
||||
}
|
||||
to {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure smooth scrolling */
|
||||
html {
|
||||
scroll-behavior: smooth !important;
|
||||
}
|
||||
|
||||
/* Testimonial styling */
|
||||
testimonial {
|
||||
display: block !important;
|
||||
max-width: 800px !important;
|
||||
margin: 40px auto !important;
|
||||
padding: 30px !important;
|
||||
background-color: #fff !important;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
|
||||
font-style: italic !important;
|
||||
color: #555 !important;
|
||||
position: relative !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 1.1rem !important;
|
||||
}
|
||||
|
||||
testimonial::before {
|
||||
content: '"' !important;
|
||||
font-size: 4rem !important;
|
||||
color: #0066cc !important;
|
||||
position: absolute !important;
|
||||
left: 10px !important;
|
||||
top: -10px !important;
|
||||
opacity: 0.2 !important;
|
||||
font-family: Georgia, serif !important;
|
||||
}
|
||||
|
||||
/* For dark backgrounds */
|
||||
section:nth-child(even) testimonial {
|
||||
background-color: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
@ -13,6 +13,357 @@ body {
|
||||
color: #222;
|
||||
line-height: 1.6;
|
||||
}
|
||||
/* Master Reset & Base Styles */
|
||||
html, body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
min-height: 100% !important;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 1.6 !important;
|
||||
color: #333 !important;
|
||||
background-color: #fff !important;
|
||||
box-sizing: border-box !important;
|
||||
overflow-x: hidden !important;
|
||||
text-align: left !important; /* Override center alignment */
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Common Container */
|
||||
.container {
|
||||
max-width: 1200px !important;
|
||||
width: 100% !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 20px !important;
|
||||
}
|
||||
|
||||
/* Section Styling for Consistent Flow */
|
||||
section {
|
||||
padding: 80px 0 !important;
|
||||
position: relative !important;
|
||||
width: 100% !important;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
section:nth-child(even) {
|
||||
background-color: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
/* Typography Hierarchy */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0.5em !important;
|
||||
line-height: 1.3 !important;
|
||||
font-weight: 700 !important;
|
||||
color: #111 !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 1rem !important;
|
||||
color: #555 !important;
|
||||
}
|
||||
|
||||
/* Link Styling */
|
||||
a {
|
||||
color: #0066cc !important;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.3s ease !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #004499 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* Button System */
|
||||
.btn, button,
|
||||
.hero-1-btn, .hero-2-btn, .grid-11-btn, .grid-12-btn,
|
||||
.split-8-btn, .split-10-btn, .text-1-btn, .text-2-btn,
|
||||
.cta-3-btn, .faq-4-footer-btn, .faq-5-contact-btn,
|
||||
.faq-6-view-more-btn, .visa-order-btn, .nav-1-btn {
|
||||
display: inline-block !important;
|
||||
padding: 12px 24px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
text-align: center !important;
|
||||
border-radius: 6px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.3s ease !important;
|
||||
border: 2px solid transparent !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.btn-primary, .hero-1-solid, .hero-2-solid, .text-1-solid, .text-2-solid, .cta-3-solid, .nav-1-solid {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
border-color: #0066cc !important;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .hero-1-solid:hover, .hero-2-solid:hover, .text-1-solid:hover, .text-2-solid:hover, .cta-3-solid:hover, .nav-1-solid:hover {
|
||||
background-color: #004499 !important;
|
||||
border-color: #004499 !important;
|
||||
}
|
||||
|
||||
.btn-outline, .hero-1-outline, .hero-2-outline, .text-1-outline, .text-2-outline, .cta-3-outline, .nav-1-outline {
|
||||
background-color: transparent !important;
|
||||
color: #0066cc !important;
|
||||
border-color: #0066cc !important;
|
||||
}
|
||||
|
||||
.btn-outline:hover, .hero-1-outline:hover, .hero-2-outline:hover, .text-1-outline:hover, .text-2-outline:hover, .cta-3-outline:hover, .nav-1-outline:hover {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Image Styling */
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
input, textarea, select {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
padding: 12px !important;
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: 6px !important;
|
||||
width: 100% !important;
|
||||
transition: border-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus {
|
||||
outline: none !important;
|
||||
border-color: #0066cc !important;
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Navigation Override */
|
||||
.nav-1-navbar {
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05) !important;
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
|
||||
/* Blog Section Fixes */
|
||||
.tn1-blog-section, .blog-2-section {
|
||||
background-color: inherit !important;
|
||||
padding: 80px 20px !important;
|
||||
}
|
||||
|
||||
.tn1-blog-card, .blog-2-card {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.tn1-blog-card:hover, .blog-2-card:hover {
|
||||
transform: translateY(-5px) !important;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.12) !important;
|
||||
}
|
||||
|
||||
/* FAQ Sections */
|
||||
.faq-4-section, .faq-5-section, .faq-6-block, .faq-7-container {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.faq-4-question, .faq-5-question, .faq-6-question, .faq-7-question {
|
||||
background-color: #f1f1f1 !important;
|
||||
transition: background-color 0.3s ease !important;
|
||||
}
|
||||
|
||||
.faq-4-question:hover, .faq-5-question:hover, .faq-6-question:hover, .faq-7-question:hover {
|
||||
background-color: #e7e7e7 !important;
|
||||
}
|
||||
|
||||
/* Footer Override */
|
||||
.footer-1-section, .footer-2-section {
|
||||
background-color: #f8f8f8 !important;
|
||||
border-top: 1px solid #eee !important;
|
||||
}
|
||||
|
||||
/* Contact Section */
|
||||
.contact-1-section {
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.contact-1-submit-btn {
|
||||
background-color: #0066cc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.contact-1-submit-btn:hover {
|
||||
background-color: #004499 !important;
|
||||
}
|
||||
|
||||
/* Hero Section Enhancements */
|
||||
.hero-1-two-column, .hero-2-centered-hero, .hero-9-section {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.hero-1-placeholder, .hero-2-placeholder, .hero-9-placeholder {
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important;
|
||||
overflow: hidden !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* Split Sections */
|
||||
.split-8-section, .split-10-section {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
.split-8-placeholder, .split-10-placeholder {
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Testimonial Styling */
|
||||
testimonial {
|
||||
display: block !important;
|
||||
max-width: 800px !important;
|
||||
margin: 40px auto !important;
|
||||
padding: 35px !important;
|
||||
background-color: #fff !important;
|
||||
border-radius: 10px !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08) !important;
|
||||
font-style: italic !important;
|
||||
color: #444 !important;
|
||||
position: relative !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 1.1rem !important;
|
||||
border-left: 5px solid #0066cc !important;
|
||||
}
|
||||
|
||||
testimonial::before {
|
||||
content: '"' !important;
|
||||
font-size: 5rem !important;
|
||||
color: #0066cc !important;
|
||||
position: absolute !important;
|
||||
left: 15px !important;
|
||||
top: -10px !important;
|
||||
opacity: 0.2 !important;
|
||||
font-family: Georgia, serif !important;
|
||||
}
|
||||
|
||||
section:nth-child(even) testimonial {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* Custom styles container */
|
||||
styles {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 992px) {
|
||||
h1 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 60px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
section {
|
||||
padding: 50px 0 !important;
|
||||
}
|
||||
|
||||
.hero-1-two-column, .split-8-section, .split-10-section {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.btn, button {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.hero-1-button-group, .hero-2-button-group {
|
||||
flex-direction: column !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
body {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 40px 0 !important;
|
||||
}
|
||||
|
||||
testimonial {
|
||||
padding: 25px !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations and Transitions */
|
||||
.fade-in {
|
||||
animation: fadeIn 1s ease-in-out !important;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0 !important;
|
||||
transform: translateY(20px) !important;
|
||||
}
|
||||
to {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure smooth scrolling */
|
||||
html {
|
||||
scroll-behavior: smooth !important;
|
||||
}
|
||||
|
||||
/* 🧱 Extended for Mega Navbar Dropdown */
|
||||
.mega-navbar {
|
||||
|
||||
@ -113,250 +113,557 @@
|
||||
</div> -->
|
||||
|
||||
|
||||
<!-- wireframe-editor.component.html -->
|
||||
|
||||
<div class="action-bar">
|
||||
<div class="left-controls">
|
||||
<button (click)="regenerateAllModifiedSections()" class="action-btn regenerate-btn">
|
||||
♻️ Regenerate Modified
|
||||
</button>
|
||||
<!-- <button (click)="uploadHtmlFiles(11096)" class="action-btn save-btn"> -->
|
||||
<button (click)="createHtmlFiles()" class="action-btn save-btn">
|
||||
💾 Save All HTML Files
|
||||
</button>
|
||||
|
||||
<button (click)="buildWireframe(37688)" class="action-btn save-btn">
|
||||
|
||||
💾 build wireframe
|
||||
</button>
|
||||
|
||||
<button class="btn btn-primary" (click)="recreateWireframe()">🔄 Recreate Wireframe</button>
|
||||
|
||||
<div class="wireframe-editor">
|
||||
|
||||
|
||||
<div class="navbar-controls">
|
||||
<select [(ngModel)]="selectedNavbarPage" (change)="selectedNavbarPage && selectNavbar(selectedNavbarPage)">
|
||||
<option value="">Select Navbar Page</option>
|
||||
<option *ngFor="let page of pageRenderOrder" [value]="page">{{ page }}</option>
|
||||
</select>
|
||||
<div class="zoom-controls-new">
|
||||
<button class="zoom-btn" (click)="zoomIn()" title="Zoom In">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
<button class="zoom-btn" (click)="zoomOut()" title="Zoom Out">
|
||||
<i class="fa fa-minus"></i>
|
||||
</button>
|
||||
<button class="zoom-btn" (click)="resetZoom()" title="Reset Zoom">
|
||||
<i class="fa fa-expand"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Fixed Right Sidebar -->
|
||||
<div class="wireframe-controls-sidebar"
|
||||
[class.collapsed]="sidebarCollapsed"
|
||||
[class.mobile-open]="sidebarOpen"
|
||||
[class.visible]="sidebarOpen || !isMobile">
|
||||
|
||||
<!-- Sidebar Header -->
|
||||
<div class="sidebar-header">
|
||||
<div (click)="toggleSidebar()">
|
||||
<i class="fas" [ngClass]="sidebarCollapsed ? 'fa-chevron-left' : 'fa-chevron-right'"></i>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<h2>
|
||||
|
||||
<i class="fas fa-cogs" style="margin-right: 8px; font-size: 18px;"></i>
|
||||
|
||||
Wireframe Controls
|
||||
</h2>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Main Actions Section -->
|
||||
<div class="sidebar-content">
|
||||
<div class="control-section fade-in-up" [@fadeIn]>
|
||||
<h3>
|
||||
<i class="fas fa-play-circle" style="margin-right: 6px; color: #28a745;"></i>
|
||||
Generate Wireframe
|
||||
</h3>
|
||||
|
||||
<button class="action-btn primary" (click)="buildWireframe(37688)" [@buttonHover]>
|
||||
<i class="fas fa-hammer"></i>
|
||||
Build Wireframe
|
||||
</button>
|
||||
|
||||
<button class="action-btn primary" (click)="regenerateAllModifiedSections()">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
Regenerate Modified
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary" (click)="createHtmlFiles()">
|
||||
<i class="fas fa-save"></i>
|
||||
Save All HTML Files
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary" (click)="recreateWireframe()">
|
||||
<i class="fas fa-redo"></i>
|
||||
Recreate Wireframe
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary">
|
||||
<i class="fa-solid fa-link"></i>
|
||||
{{ deployedUrl ? 'DEPLOYED URL: ' + deployedUrl : 'NO URL YET' }}
|
||||
</button>
|
||||
|
||||
<!-- Navbar Editor Modal -->
|
||||
<div class="modal" *ngIf="selectedNavbarPage">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>Edit Navbar Links - {{ selectedNavbarPage }}</h3>
|
||||
<button class="close-btn" (click)="selectedNavbarPage = ''">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="link-editor" *ngFor="let link of navbarLinks[selectedNavbarPage]; let i = index">
|
||||
<input type="text" [(ngModel)]="link.label" placeholder="Link Label">
|
||||
<select [(ngModel)]="link.href">
|
||||
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
|
||||
<!-- Navbar Controls Section -->
|
||||
<div class="control-section fade-in-left" [@fadeIn]>
|
||||
<h3>
|
||||
<i class="fas fa-bars" style="margin-right: 6px; color: #17a2b8;"></i>
|
||||
Navbar Editor
|
||||
</h3>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="navbarSelect">Select Navbar Page:</label>
|
||||
<select id="navbarSelect" class="form-control" [(ngModel)]="selectedNavbarPage"
|
||||
(change)="selectedNavbarPage && selectNavbar(selectedNavbarPage)"
|
||||
[@focusBorder]>
|
||||
<option value="">Choose a page...</option>
|
||||
<option *ngFor="let page of pageRenderOrder" [value]="page">
|
||||
<i class="fas fa-file-alt"></i> {{ page }}
|
||||
</option>
|
||||
</select>
|
||||
<button class="remove-btn" (click)="removeLink(selectedNavbarPage, i)">×</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Style Controls Section -->
|
||||
<div class="control-section bounce-in" [@fadeIn]>
|
||||
<h3>
|
||||
<i class="fas fa-palette" style="margin-right: 6px; color: #fd7e14;"></i>
|
||||
Style Controls
|
||||
</h3>
|
||||
|
||||
<div class="style-grid">
|
||||
<div class="form-group">
|
||||
<label>Font Size:</label>
|
||||
<select class="form-control" [(ngModel)]="liveStyles.fontSize">
|
||||
<option value="">Default</option>
|
||||
<option *ngFor="let size of fontSizes" [value]="size">{{ size }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Font Weight:</label>
|
||||
<select class="form-control" [(ngModel)]="liveStyles.fontWeight">
|
||||
<option value="">Default</option>
|
||||
<option *ngFor="let weight of fontWeights" [value]="weight">{{ weight }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="action-btn add-link-btn" (click)="addLink(selectedNavbarPage)">
|
||||
➕ Add New Link
|
||||
<div class="style-grid">
|
||||
<div class="form-group">
|
||||
<label>Text Align:</label>
|
||||
<select class="form-control" [(ngModel)]="liveStyles.textAlign">
|
||||
<option value="">Default</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="center">Center</option>
|
||||
<option value="right">Right</option>
|
||||
<option value="justify">Justify</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="action-btn secondary small" (click)="verifyCssInSections()">
|
||||
<i class="fas fa-search"></i>
|
||||
Verify CSS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-controls">
|
||||
<div class="color-input-group">
|
||||
<span class="color-label">
|
||||
<i class="fas fa-font" style="margin-right: 4px;"></i>
|
||||
Text Color
|
||||
</span>
|
||||
<input type="color" [(ngModel)]="liveStyles.color" class="color-picker" />
|
||||
</div>
|
||||
|
||||
<div class="color-input-group">
|
||||
<span class="color-label">
|
||||
<i class="fas fa-fill-drip" style="margin-right: 4px;"></i>
|
||||
Background
|
||||
</span>
|
||||
<input type="color" [(ngModel)]="liveStyles.background" class="color-picker" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="action-btn primary full-width" (click)="applyStyleToSelection()">
|
||||
<i class="fas fa-paint-brush"></i>
|
||||
Apply Style to Selection
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="action-btn" (click)="applyNavbarChanges(selectedNavbarPage)">
|
||||
Save Changes
|
||||
<!-- Page Management Section -->
|
||||
<div class="control-section fade-in-up" [@fadeIn]>
|
||||
<h3>
|
||||
<i class="fas fa-file-alt" style="margin-right: 6px; color: #dc3545;"></i>
|
||||
Page Management
|
||||
</h3>
|
||||
|
||||
<button class="action-btn secondary" (click)="addNewPage()">
|
||||
<i class="fas fa-plus"></i>
|
||||
Add New Page
|
||||
</button>
|
||||
<button class="action-btn cancel-btn" (click)="selectedNavbarPage = ''">
|
||||
Cancel
|
||||
|
||||
<button class="action-btn secondary" (click)="duplicateCurrentPage()">
|
||||
<i class="fas fa-copy"></i>
|
||||
Duplicate Current Page
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary" (click)="deleteCurrentPage()"
|
||||
[disabled]="pageRenderOrder.length <= 1">
|
||||
<i class="fas fa-trash"></i>
|
||||
Delete Current Page
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Additional Tools Section -->
|
||||
<div class="control-section fade-in-up" [@fadeIn]>
|
||||
<h3>
|
||||
<i class="fas fa-tools" style="margin-right: 6px; color: #6f42c1;"></i>
|
||||
Additional Tools
|
||||
</h3>
|
||||
|
||||
<button class="action-btn secondary" (click)="exportProject()">
|
||||
<i class="fas fa-download"></i>
|
||||
Export Project
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary" (click)="importProject()">
|
||||
<i class="fas fa-upload"></i>
|
||||
Import Project
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary" (click)="previewMode()">
|
||||
<i class="fas fa-eye"></i>
|
||||
Preview Mode
|
||||
</button>
|
||||
|
||||
<button class="action-btn secondary" (click)="addNewSectionToCurrentPage()">
|
||||
<i class="fas fa-plus-square"></i>
|
||||
Add Section
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="right-controls">
|
||||
<select [(ngModel)]="liveStyles.fontSize" class="style-control">
|
||||
<option value="">Font Size</option>
|
||||
<option *ngFor="let size of ['12px', '14px', '16px', '18px', '20px', '24px', '28px']" [value]="size">{{ size }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<select [(ngModel)]="liveStyles.fontWeight" class="style-control">
|
||||
<option value="">Font Weight</option>
|
||||
<option *ngFor="let weight of ['normal', 'bold', 'lighter']" [value]="weight">{{ weight }}</option>
|
||||
</select>
|
||||
|
||||
<select [(ngModel)]="liveStyles.textAlign" class="style-control">
|
||||
<option value="">Align</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="center">Center</option>
|
||||
<option value="right">Right</option>
|
||||
</select>
|
||||
|
||||
<input type="color" [(ngModel)]="liveStyles.color" title="Text Color" class="color-picker" />
|
||||
<input type="color" [(ngModel)]="liveStyles.background" title="Background Color" class="color-picker" />
|
||||
|
||||
<button (click)="applyStyleToSelection()" class="action-btn style-btn">
|
||||
🎨 Apply Style
|
||||
</button>
|
||||
|
||||
<button (click)="verifyCssInSections()" class="action-btn">🔍 Verify CSS</button>
|
||||
</div>
|
||||
<!-- Main Canvas Area -->
|
||||
<div class="canvas-main-area" [class.sidebar-collapsed]="sidebarCollapsed">
|
||||
<div class="wireframe-controls-sidebar-overlay"
|
||||
*ngIf="sidebarOpen && !isMobile"
|
||||
(click)="sidebarOpen = false">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Grid and Wireframe Canvas Area -->
|
||||
<div class="visual-editor-container">
|
||||
<!-- Zoom Controls -->
|
||||
<div class="zoom-controls">
|
||||
<button class="zoom-btn" (click)="zoomIn()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
<button class="zoom-btn" (click)="zoomOut()">
|
||||
<i class="fa fa-minus"></i>
|
||||
</button>
|
||||
<button class="zoom-btn" (click)="resetZoom()">
|
||||
<i class="fa fa-expand"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Infinite Grid Background -->
|
||||
<div class="infinite-grid" [style.transform]="getGridTransform()"></div>
|
||||
|
||||
<!-- Canvas Area with Grid -->
|
||||
<div class="canvas-wrapper" cdkScrollable>
|
||||
<div class="canvas-boundary" [style.transform]="getTransform()" (mousedown)="startPan($event)"
|
||||
(mousemove)="doPan($event)" (mouseup)="endPan()" (mouseleave)="endPan()">
|
||||
<!-- Zoom Controls -->
|
||||
|
||||
<!-- Grid Background -->
|
||||
<div class="grid-background"></div>
|
||||
<!-- Canvas Container -->
|
||||
<div class="canvas-container"
|
||||
[class.panning]="isPanning"
|
||||
(mousedown)="startPan($event)"
|
||||
(mousemove)="doPan($event)"
|
||||
(mouseup)="endPan()"
|
||||
(mouseleave)="endPan()"
|
||||
(wheel)="onWheel($event)">
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div *ngIf="isLoading" class="loading-overlay">
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">Loading wireframe...</div>
|
||||
</div>
|
||||
|
||||
<!-- Full Page Display -->
|
||||
<div *ngIf="!isLoading && getCurrentPage()"
|
||||
class="page-display"
|
||||
[style.transform]="getPageTransform()">
|
||||
|
||||
<!-- Pages Container - Draggable Pages -->
|
||||
<div *ngIf="!isLoading" class="pages-container" cdkDropList cdkDropListOrientation="vertical"
|
||||
(cdkDropListDropped)="onPageDrop($event)">
|
||||
<!-- Connected Sections Container -->
|
||||
<div class="connected-sections"
|
||||
cdkDropList
|
||||
cdkDropListOrientation="vertical"
|
||||
[cdkDropListData]="getCurrentPageSections()"
|
||||
(cdkDropListDropped)="onSectionDrop($event, getCurrentPageName())">
|
||||
|
||||
<div *ngFor="let pageName of pageRenderOrder" class="page-card" cdkDrag
|
||||
[class.page-hovered]="hoveredPage === pageName" (mouseenter)="showPageTools(pageName)"
|
||||
(mouseleave)="hidePageTools(pageName)">
|
||||
<!-- Individual Connected Sections -->
|
||||
<!-- Change this in your *ngFor -->
|
||||
<!-- Update the section wrapper div -->
|
||||
<div *ngFor="let sectionKey of getCurrentPageSections(); let i = index"
|
||||
class="section-wrapper"
|
||||
[class.editing]="isCurrentSectionEditing(sectionKey)"
|
||||
cdkDrag
|
||||
[cdkDragData]="sectionKey"
|
||||
(mouseenter)="hoveredSection = sectionKey"
|
||||
(mouseleave)="hoveredSection = null"
|
||||
(dblclick)="toggleSectionEditing(getCurrentPageName(), sectionKey, $event)">
|
||||
|
||||
<!-- Page Header with Tools -->
|
||||
<div class="page-header">
|
||||
<h3 class="page-title">{{ pageName }}</h3>
|
||||
<div class="page-tools" [class.visible]="hoveredPage === pageName">
|
||||
<button class="tool-btn" (click)="regenerateWireframe(pageName)" title="Regenerate">♻️</button>
|
||||
<button class="tool-btn" (click)="copyToClipboard(pageName)" title="Copy HTML">📋</button>
|
||||
<button class="tool-btn" (click)="downloadHtml(pageName)" title="Download HTML">⬇️</button>
|
||||
<!-- Section Content (Full Width, No Gaps) -->
|
||||
<div class="section-content"
|
||||
[innerHTML]="getSectionHtml(getCurrentPageName(), sectionKey)"
|
||||
[attr.contenteditable]="isCurrentSectionEditing(sectionKey)"
|
||||
(blur)="handleDirectContentEdit($event, getCurrentPageName(), sectionKey)">
|
||||
</div>
|
||||
|
||||
<!-- Section Overlay for Hover/Edit State -->
|
||||
<div class="section-overlay"
|
||||
[class.visible]="hoveredSection === sectionKey || isCurrentSectionEditing(sectionKey)">
|
||||
<div class="section-label">
|
||||
<i class="fas fa-puzzle-piece" style="margin-right: 4px;"></i>
|
||||
{{ sectionKey }}
|
||||
<button *ngIf="isCurrentSectionEditing(sectionKey)"
|
||||
class="btn btn-sm btn-success ms-2"
|
||||
(click)="saveSectionEdit(getCurrentPageName(), sectionKey)">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drag Handle -->
|
||||
<div class="drag-handle" cdkDragHandle style="display: none;">
|
||||
<i class="fa fa-grip-lines"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sections Container - Draggable Sections -->
|
||||
<div class="sections-container" cdkDropList [id]="'section-list-' + pageName"
|
||||
[cdkDropListData]="getSectionsData(pageName)" (cdkDropListDropped)="onSectionDrop($event, pageName)">
|
||||
|
||||
<!-- Individual Draggable Sections -->
|
||||
<div *ngFor="let sectionKey of getSectionKeys(pageName); let i = index" class="section-card"
|
||||
[attr.data-section-id]="sectionKey" cdkDrag [cdkDragData]="sectionKey"
|
||||
(mouseenter)="showSectionTools(pageName, sectionKey)" (mouseleave)="hideSectionTools()">
|
||||
|
||||
<!-- Section Content -->
|
||||
<div class="section-content">
|
||||
<!-- Section Header -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">{{ sectionKey }}</span>
|
||||
|
||||
<!-- Section Actions -->
|
||||
<div class="section-actions" [class.visible]="hoveredSection === sectionKey">
|
||||
<button class="section-btn" title="Toggle Edit Mode"
|
||||
(click)="toggleSectionEditing(pageName, sectionKey, $event)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</button>
|
||||
<button class="section-btn" title="Remove Section" (click)="removeSection(pageName, sectionKey)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section Preview with COMMON_CSS applied -->
|
||||
<!-- Change all [innerHTML] bindings to use $any() to bypass strict checks -->
|
||||
<div class="section-preview" [innerHTML]="$any(getSectionHtml(pageName, sectionKey))"
|
||||
(dblclick)="toggleSectionEditing(pageName, sectionKey, $event)"
|
||||
(blur)="handleDirectContentEdit($event, pageName, sectionKey)"
|
||||
[attr.contenteditable]="isSectionEditable(pageName, sectionKey)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Section Button (appears on hover) -->
|
||||
<div class="add-section-btn" *ngIf="hoveredSection === sectionKey"
|
||||
(click)="addNewSection(pageName, sectionKey)">
|
||||
<i class="fa fa-plus"></i>
|
||||
</div>
|
||||
|
||||
<!-- Drag Handle -->
|
||||
<div class="drag-handle" cdkDragHandle>
|
||||
<i class="fa fa-grip-lines"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State / Add First Section -->
|
||||
<div *ngIf="getSectionKeys(pageName).length === 0" class="empty-sections">
|
||||
<button class="add-first-section" (click)="addNewSection(pageName)">
|
||||
<i class="fa fa-plus"></i> Add First Section
|
||||
<!-- Empty State for Sections -->
|
||||
<div *ngIf="getCurrentPageSections().length === 0" class="empty-sections"
|
||||
style="padding: 100px; text-align: center; color: #6c757d;">
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-puzzle-piece" style="font-size: 48px; margin-bottom: 20px; opacity: 0.5;"></i>
|
||||
<h3>No Sections Yet</h3>
|
||||
<p>Add your first section to start building this page.</p>
|
||||
<button class="action-btn primary" (click)="addNewSectionToCurrentPage()">
|
||||
<i class="fas fa-plus"></i>
|
||||
Add First Section
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State for Pages -->
|
||||
<div *ngIf="!isLoading && pageRenderOrder.length === 0" class="empty-pages"
|
||||
style="display: flex; align-items: center; justify-content: center; height: 100vh;">
|
||||
<div class="empty-state" style="text-align: center; color: #6c757d;">
|
||||
<i class="fas fa-file-plus" style="font-size: 64px; margin-bottom: 30px; opacity: 0.5;"></i>
|
||||
<h2>No Pages Yet</h2>
|
||||
<p style="font-size: 16px; margin-bottom: 30px;">Create your first page to get started with wireframing.</p>
|
||||
<button class="action-btn primary" (click)="createFirstPage()">
|
||||
<i class="fas fa-plus"></i>
|
||||
Create First Page
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button class="action-btn primary" (click)="copyPageToClipboard()">
|
||||
<i class="fas fa-copy"></i>
|
||||
Copy Page
|
||||
</button>
|
||||
|
||||
<button class="action-btn success" (click)="downloadHtml(getCurrentPageName())">
|
||||
<i class="fas fa-download"></i>
|
||||
Download Page
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Modal for Adding New Section -->
|
||||
<div class="modal" *ngIf="showNewSectionModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>Add New Section to {{ newSectionPage }}</h3>
|
||||
<button class="close-btn" (click)="cancelAddSection()">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Section Name:</label>
|
||||
<input type="text" [(ngModel)]="newSectionName" placeholder="Enter section name">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Section Type:</label>
|
||||
<select [(ngModel)]="newSectionType">
|
||||
<option value="">Select a type</option>
|
||||
<option value="header">Header</option>
|
||||
<option value="hero">Hero</option>
|
||||
<option value="features">Features</option>
|
||||
<option value="gallery">Gallery</option>
|
||||
<option value="pricing">Pricing</option>
|
||||
<option value="testimonials">Testimonials</option>
|
||||
<option value="contact">Contact</option>
|
||||
<option value="footer">Footer</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Section Description:</label>
|
||||
<textarea [(ngModel)]="newSectionDescription" rows="4"
|
||||
placeholder="Describe the content for this section"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="action-btn" [disabled]="!newSectionName || !newSectionType" (click)="confirmAddSection()">
|
||||
Add Section
|
||||
|
||||
<!-- Page Navigation Controls -->
|
||||
<div *ngIf="!isLoading && pageRenderOrder.length > 0" class="page-navigation">
|
||||
<button class="nav-btn"
|
||||
(click)="previousPage()"
|
||||
[disabled]="currentPageIndex <= 0"
|
||||
title="Previous Page">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
Previous
|
||||
</button>
|
||||
<button class="action-btn cancel-btn" (click)="cancelAddSection()">
|
||||
Cancel
|
||||
|
||||
<div class="page-indicator">
|
||||
<span class="page-number">{{ currentPageIndex + 1 }}</span>
|
||||
<span class="page-separator">/</span>
|
||||
<span class="total-pages">{{ pageRenderOrder.length }}</span>
|
||||
<div class="page-title">{{ getCurrentPageName() }}</div>
|
||||
</div>
|
||||
|
||||
<button class="nav-btn"
|
||||
(click)="nextPage()"
|
||||
[disabled]="currentPageIndex >= pageRenderOrder.length - 1"
|
||||
title="Next Page">
|
||||
Next
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Sidebar Toggle (Hidden on Desktop) -->
|
||||
<button class="mobile-sidebar-toggle"
|
||||
(click)="toggleMobileSidebar()"
|
||||
[class.active]="sidebarOpen"
|
||||
style="display: none; position: fixed; top: 20px; right: 20px; z-index: 1600; background: #212529; color: white; border: none; padding: 12px; border-radius: 50%; font-size: 16px;"
|
||||
*ngIf="isMobile">
|
||||
<i class="fas" [ngClass]="sidebarOpen ? 'fa-times' : 'fa-bars'"></i>
|
||||
</button>
|
||||
|
||||
<!-- Navbar Editor Modal -->
|
||||
<div class="modal-overlay" *ngIf="selectedNavbarPage" [@fadeIn] (click)="closeNavbarModal()">
|
||||
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>
|
||||
|
||||
<div class="modal-header">
|
||||
<h3>
|
||||
<i class="fas fa-edit" style="margin-right: 8px; color: #17a2b8;"></i>
|
||||
Edit Navbar Links - {{ selectedNavbarPage }}
|
||||
</h3>
|
||||
<button class="close-btn" (click)="closeNavbarModal()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="navbar-link-item" *ngFor="let link of navbarLinks[selectedNavbarPage]; let i = index" [@fadeIn]
|
||||
style="display: flex; gap: 10px; align-items: center; margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
|
||||
<div class="form-group" style="flex: 1; margin-bottom: 0;">
|
||||
<input type="text" [(ngModel)]="link.label" placeholder="Link Label" class="form-control">
|
||||
</div>
|
||||
<div class="form-group" style="flex: 1; margin-bottom: 0;">
|
||||
<select [(ngModel)]="link.href" class="form-control">
|
||||
<option value="">Select page...</option>
|
||||
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="remove-btn" (click)="removeLink(selectedNavbarPage, i)" title="Remove Link"
|
||||
style="background: #dc3545; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="action-btn secondary full-width add-link-btn" (click)="addLink(selectedNavbarPage)">
|
||||
<i class="fas fa-plus"></i>
|
||||
Add New Link
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="action-btn secondary" (click)="closeNavbarModal()">
|
||||
<i class="fas fa-times" style="margin-right: 5px;"></i>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="action-btn primary" (click)="applyNavbarChanges(selectedNavbarPage)">
|
||||
<i class="fas fa-check" style="margin-right: 5px;"></i>
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal for Adding New Section -->
|
||||
<div class="modal-overlay" *ngIf="showNewSectionModal" [@fadeIn] (click)="closeNewSectionModal()">
|
||||
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>
|
||||
|
||||
<div class="modal-header">
|
||||
<h3>
|
||||
<i class="fas fa-plus-circle" style="margin-right: 8px; color: #28a745;"></i>
|
||||
Add New Section to {{ newSectionPage }}
|
||||
</h3>
|
||||
<button class="close-btn" (click)="closeNewSectionModal()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="sectionName">
|
||||
<i class="fas fa-tag" style="margin-right: 4px;"></i>
|
||||
Section Name:
|
||||
</label>
|
||||
<input id="sectionName"
|
||||
type="text"
|
||||
[(ngModel)]="newSectionName"
|
||||
placeholder="Enter section name (e.g., Hero, About, Contact)"
|
||||
class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sectionType">
|
||||
<i class="fas fa-layer-group" style="margin-right: 4px;"></i>
|
||||
Section Type:
|
||||
</label>
|
||||
<select id="sectionType" [(ngModel)]="newSectionType" class="form-control">
|
||||
<option value="">Select a type...</option>
|
||||
<option *ngFor="let type of sectionTypes" [value]="type.value">
|
||||
{{ type.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sectionDescription">
|
||||
<i class="fas fa-align-left" style="margin-right: 4px;"></i>
|
||||
Section Description:
|
||||
</label>
|
||||
<textarea id="sectionDescription"
|
||||
[(ngModel)]="newSectionDescription"
|
||||
rows="4"
|
||||
placeholder="Describe the content and purpose of this section..."
|
||||
class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="action-btn secondary" (click)="closeNewSectionModal()">
|
||||
<i class="fas fa-times" style="margin-right: 5px;"></i>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="action-btn primary"
|
||||
[disabled]="!newSectionName || !newSectionType"
|
||||
(click)="confirmAddSection()">
|
||||
<i class="fas fa-plus" style="margin-right: 5px;"></i>
|
||||
Add Section
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal for Adding New Page -->
|
||||
<div class="modal-overlay" *ngIf="showNewPageModal" [@fadeIn] (click)="closeNewPageModal()">
|
||||
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>
|
||||
|
||||
<div class="modal-header">
|
||||
<h3>
|
||||
<i class="fas fa-file-plus" style="margin-right: 8px; color: #28a745;"></i>
|
||||
Add New Page
|
||||
</h3>
|
||||
<button class="close-btn" (click)="closeNewPageModal()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="pageName">
|
||||
<i class="fas fa-file-alt" style="margin-right: 4px;"></i>
|
||||
Page Name:
|
||||
</label>
|
||||
<input id="pageName"
|
||||
type="text"
|
||||
[(ngModel)]="newPageName"
|
||||
placeholder="Enter page name (e.g., Home, About, Contact)"
|
||||
class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pageDescription">
|
||||
<i class="fas fa-align-left" style="margin-right: 4px;"></i>
|
||||
Page Description:
|
||||
</label>
|
||||
<textarea id="pageDescription"
|
||||
[(ngModel)]="newPageDescription"
|
||||
rows="3"
|
||||
placeholder="Describe the purpose and content of this page..."
|
||||
class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="action-btn secondary" (click)="closeNewPageModal()">
|
||||
<i class="fas fa-times" style="margin-right: 5px;"></i>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="action-btn primary"
|
||||
[disabled]="!newPageName.trim()"
|
||||
(click)="confirmAddPage()">
|
||||
<i class="fas fa-plus" style="margin-right: 5px;"></i>
|
||||
Add Page
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
|
||||
import { Component, Input, OnInit, SecurityContext } from '@angular/core';
|
||||
import { Component, Input, OnInit, SecurityContext, ChangeDetectorRef } from '@angular/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service';
|
||||
@ -9,11 +9,44 @@ import * as sha256 from 'crypto-js/sha256';
|
||||
import SHA256 from 'crypto-js/sha256';
|
||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { Download_Css } from './download-css';
|
||||
import { trigger, transition, style, animate, state } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-wireframe-renderer',
|
||||
templateUrl: './wireframe-renderer.component.html',
|
||||
styleUrls: ['./wireframe-renderer.component.scss']
|
||||
styleUrls: ['./wireframe-renderer.component.scss'],
|
||||
animations: [
|
||||
trigger('fadeIn', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate('200ms ease-out', style({ opacity: 1 }))
|
||||
]),
|
||||
transition(':leave', [
|
||||
animate('200ms ease-in', style({ opacity: 0 }))
|
||||
])
|
||||
]),
|
||||
trigger('slideIn', [
|
||||
transition(':enter', [
|
||||
style({ transform: 'scale(0.9)', opacity: 0 }),
|
||||
animate('300ms ease-out', style({ transform: 'scale(1)', opacity: 1 }))
|
||||
]),
|
||||
transition(':leave', [
|
||||
animate('200ms ease-in', style({ transform: 'scale(0.9)', opacity: 0 }))
|
||||
])
|
||||
]),
|
||||
trigger('buttonHover', [
|
||||
state('void', style({ transform: 'translateY(0)' })),
|
||||
state('hover', style({ transform: 'translateY(-2px)', boxShadow: '0 4px 8px rgba(0,0,0,0.2)' }))
|
||||
]),
|
||||
trigger('focusBorder', [
|
||||
state('void', style({ borderColor: '#ccc' })),
|
||||
state('focus', style({ borderColor: '#1976d2', boxShadow: '0 0 0 2px rgba(25,118,210,0.2)' }))
|
||||
]),
|
||||
trigger('pulseOnHover', [
|
||||
state('void', style({ backgroundColor: 'transparent' })),
|
||||
state('hover', style({ backgroundColor: 'rgba(0,0,0,0.05)' }))
|
||||
])
|
||||
]
|
||||
})
|
||||
|
||||
export class WireframeRendererComponent implements OnInit {
|
||||
@ -30,6 +63,13 @@ export class WireframeRendererComponent implements OnInit {
|
||||
sectionHtmls: Record<string, Record<string, SafeHtml>> = {};
|
||||
isLoading: boolean = true;
|
||||
sitename = '';
|
||||
|
||||
sidebarCollapsed: boolean = false;
|
||||
|
||||
// Add toggle method
|
||||
|
||||
|
||||
deployedUrl: string | null = null;
|
||||
// Added properties for grid and drag functionality
|
||||
hoveredPage: string | null = null;
|
||||
hoveredSection: string | null = null;
|
||||
@ -39,7 +79,8 @@ export class WireframeRendererComponent implements OnInit {
|
||||
isPanning: boolean = false;
|
||||
lastX: number = 0;
|
||||
lastY: number = 0;
|
||||
|
||||
isDragging = false;
|
||||
startPanPosition = { x: 0, y: 0 };
|
||||
// Edit mode properties
|
||||
editMode: boolean = false;
|
||||
editingSection: { page: string, section: string } | null = null;
|
||||
@ -53,15 +94,68 @@ export class WireframeRendererComponent implements OnInit {
|
||||
newSectionDescription: string = '';
|
||||
newSectionAfter: string | null = null;
|
||||
|
||||
|
||||
|
||||
sidebarOpen = false;
|
||||
// Add this property to fix template errors
|
||||
|
||||
// Page and section management
|
||||
pageRenderOrder: string[] = [];
|
||||
|
||||
// need help with all the errors
|
||||
|
||||
// Style controls
|
||||
liveStyles = {
|
||||
fontSize: '',
|
||||
fontWeight: '',
|
||||
textAlign: '',
|
||||
color: '#000000',
|
||||
background: '#ffffff'
|
||||
};
|
||||
|
||||
// Data arrays
|
||||
fontSizes = ['12px', '14px', '16px', '18px', '20px', '24px', '28px'];
|
||||
fontWeights = ['normal', 'bold', 'lighter'];
|
||||
|
||||
sectionTypes = [
|
||||
{ value: 'header', label: 'Header' },
|
||||
{ value: 'hero', label: 'Hero' },
|
||||
{ value: 'features', label: 'Features' },
|
||||
{ value: 'gallery', label: 'Gallery' },
|
||||
{ value: 'pricing', label: 'Pricing' },
|
||||
{ value: 'testimonials', label: 'Testimonials' },
|
||||
{ value: 'contact', label: 'Contact' },
|
||||
{ value: 'footer', label: 'Footer' }
|
||||
];
|
||||
|
||||
// Mock data - replace with your actual data structures
|
||||
navbarLinks: { [key: string]: any[] } = {};
|
||||
availablePages: { href: string, label: string }[] = [];
|
||||
|
||||
zoomLevel = 1;
|
||||
currentPageIndex = 0;
|
||||
sectionOrderMap: { [key: string]: string[] } = {};
|
||||
|
||||
// Add these new properties
|
||||
draggedSection: string | null = null;
|
||||
lastDeletedSection: { page: string; section: string; content: SafeHtml } | null = null;
|
||||
gridColumns = 12;
|
||||
gridGap = 16;
|
||||
|
||||
constructor(
|
||||
private siteTreeService: SiteTreeservice,
|
||||
private route: ActivatedRoute,
|
||||
private sanitizer: DomSanitizer,
|
||||
private toastr: ToastrService,
|
||||
private cdr: ChangeDetectorRef
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
// sectionOrderMap: Record<string, string[]> = {};
|
||||
|
||||
// Initialize in ngOnInit()
|
||||
ngOnInit(): void {
|
||||
|
||||
this.sectionOrderMap = {};
|
||||
this.id = this.route.snapshot.params['id'];
|
||||
console.log('id is: ', this.id);
|
||||
this.fetchTreeById(this.id);
|
||||
@ -84,33 +178,17 @@ export class WireframeRendererComponent implements OnInit {
|
||||
}
|
||||
|
||||
toggleSectionEditing(pageName: string, sectionKey: string, event: Event): void {
|
||||
event.stopPropagation();
|
||||
const previewElement = event.target as HTMLElement;
|
||||
const sectionPreview = previewElement.closest('.section-preview') as HTMLElement;
|
||||
|
||||
if (sectionPreview) {
|
||||
const isEditable = sectionPreview.getAttribute('contenteditable') === 'true';
|
||||
|
||||
// Toggle edit mode
|
||||
sectionPreview.setAttribute('contenteditable', !isEditable + '');
|
||||
|
||||
if (!isEditable) {
|
||||
// Add specific class for styling when editing
|
||||
sectionPreview.classList.add('editing-active');
|
||||
|
||||
// Ensure CSS is available for editing
|
||||
this.ensureCommonCssForAllSections();
|
||||
|
||||
sectionPreview.focus();
|
||||
} else {
|
||||
// Remove editing class
|
||||
sectionPreview.classList.remove('editing-active');
|
||||
|
||||
// Save changes
|
||||
this.saveSectionChanges(pageName, sectionKey, sectionPreview.innerHTML);
|
||||
}
|
||||
}
|
||||
}
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.isCurrentSectionEditing(sectionKey)) {
|
||||
this.editingSection = null;
|
||||
} else {
|
||||
this.editingSection = { page: pageName, section: sectionKey };
|
||||
}
|
||||
|
||||
// Force change detection
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
verifyCssInSections(): void {
|
||||
// First ensure common CSS is in the head
|
||||
@ -167,14 +245,16 @@ export class WireframeRendererComponent implements OnInit {
|
||||
}
|
||||
saveSectionChanges(pageName: string, sectionKey: string, html: string): void {
|
||||
// Remove temporary style tags
|
||||
const cleanedHtml = html.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '');
|
||||
const cleanedHtml = html.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '');
|
||||
|
||||
this.sectionHtmls[pageName][sectionKey] =
|
||||
this.sanitizer.bypassSecurityTrustHtml(cleanedHtml);
|
||||
// this.sectionHtmls[pageName][sectionKey] =
|
||||
// this.sanitizer.bypassSecurityTrustHtml(cleanedHtml);
|
||||
|
||||
this.updateFullPageHtml(pageName);
|
||||
this.toastr.success(`Updated section: ${sectionKey}`);
|
||||
}
|
||||
this.sectionHtmls[pageName][sectionKey] = cleanedHtml;
|
||||
|
||||
this.updateFullPageHtml(pageName);
|
||||
this.toastr.success(`Updated section: ${sectionKey}`);
|
||||
}
|
||||
|
||||
// Step 1: Get data from tree by ID
|
||||
fetchTreeById(id: number) {
|
||||
@ -189,6 +269,8 @@ this.toastr.success(`Updated section: ${sectionKey}`);
|
||||
const maybeParsed = typeof res.model === 'string' ? JSON.parse(res.model) : res.model;
|
||||
this.jsonInput = JSON.stringify(maybeParsed, null, 2);
|
||||
this.sitename = res.name;
|
||||
this.deployedUrl = res.deployedUrl;
|
||||
console.log("Deployed Url "+res.deployedUrl);
|
||||
this.generatePromptsForAllSections(this.jsonInput);
|
||||
this.isLoading = false;
|
||||
} catch (err) {
|
||||
@ -215,11 +297,15 @@ this.toastr.success(`Updated section: ${sectionKey}`);
|
||||
console.error('❌ Invalid JSON format', e);
|
||||
}
|
||||
}
|
||||
|
||||
isCurrentSectionEditing(sectionKey: string): boolean {
|
||||
const currentPage = this.getCurrentPageName();
|
||||
return this.editingSection?.page === currentPage &&
|
||||
this.editingSection?.section === sectionKey;
|
||||
}
|
||||
// Step 3
|
||||
pageRenderOrder: string[] = []; // 🚀 New array to hold correct page order
|
||||
|
||||
// 🔄 Maintain strict page and section render order
|
||||
sectionOrderMap: Record<string, string[]> = {}; // 🆕 Added to track section order
|
||||
missingHtmlPages: string[] = [];
|
||||
|
||||
async processPages(treeJson: any,forceRegenerate = false) {
|
||||
@ -227,7 +313,7 @@ this.toastr.success(`Updated section: ${sectionKey}`);
|
||||
|
||||
for (const [pageName, sections] of Object.entries(treeJson)) {
|
||||
this.pageRenderOrder.push(pageName); // ✅ Track the order of pages
|
||||
this.sectionOrderMap[pageName] = []; // ✅ Init section order array
|
||||
this.sectionOrderMap[pageName] = this.sectionOrderMap[pageName] || [];
|
||||
|
||||
console.log('process page is : ',pageName)
|
||||
|
||||
@ -448,6 +534,20 @@ HTML Only. No CSS.
|
||||
});
|
||||
}
|
||||
}
|
||||
nextPage(): void {
|
||||
if (this.currentPageIndex < this.pageRenderOrder.length - 1) {
|
||||
this.currentPageIndex++;
|
||||
this.resetZoom();
|
||||
}
|
||||
}
|
||||
|
||||
previousPage(): void {
|
||||
if (this.currentPageIndex > 0) {
|
||||
this.currentPageIndex--;
|
||||
this.resetZoom();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process page sections
|
||||
async processPageSections(pageName: string, sectionMap: Record<string, string>) {
|
||||
@ -464,7 +564,9 @@ HTML Only. No CSS.
|
||||
sectionHtmls.push(html);
|
||||
|
||||
// Store individual section HTML
|
||||
this.sectionHtmls[pageName][sectionName] = this.sanitizer.bypassSecurityTrustHtml(html);
|
||||
// this.sectionHtmls[pageName][sectionName] = this.sanitizer.bypassSecurityTrustHtml(html);
|
||||
this.sectionHtmls[pageName][sectionName] = html
|
||||
console.log("Processing Html in process page "+sectionHtmls)
|
||||
});
|
||||
|
||||
await Promise.all(htmlTasks);
|
||||
@ -478,7 +580,7 @@ HTML Only. No CSS.
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${pageName}</title>
|
||||
<style>
|
||||
${Download_Css}
|
||||
${COMMON_CSS}
|
||||
|
||||
html, body {
|
||||
margin: 0 !important;
|
||||
@ -500,6 +602,28 @@ HTML Only. No CSS.
|
||||
const safeHtml = this.sanitizer.bypassSecurityTrustHtml(finalHtml);
|
||||
this.pageSections[pageName] = { FullPage: safeHtml };
|
||||
}
|
||||
|
||||
// async processPageSections(pageName: string, sectionMap: Record<string, string>) {
|
||||
// // Initialize section order map
|
||||
// this.sectionOrderMap[pageName] = this.sectionOrderMap[pageName] || [];
|
||||
|
||||
// // Clear existing sections
|
||||
// this.sectionHtmls[pageName] = {};
|
||||
|
||||
// const htmlTasks = Object.entries(sectionMap).map(async ([sectionName, jsonPrompt]) => {
|
||||
// const html = await this.generateJson({ sectionType: sectionName, jsonStructure: jsonPrompt });
|
||||
|
||||
// // Store with proper ordering
|
||||
// this.sectionOrderMap[pageName].push(sectionName);
|
||||
// this.sectionHtmls[pageName][sectionName] = this.sanitizer.bypassSecurityTrustHtml(html);
|
||||
// });
|
||||
|
||||
// await Promise.all(htmlTasks);
|
||||
|
||||
// // Update full page HTML with all sections
|
||||
// this.updateFullPageHtml(pageName);
|
||||
// }
|
||||
|
||||
// Generate JSON to HTML
|
||||
async generateJson(data: { jsonStructure: any, sectionType: string }): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
@ -520,6 +644,8 @@ HTML Only. No CSS.
|
||||
next: (res) => {
|
||||
console.log('✅ Response from HTML API:', res);
|
||||
if (res && res.msg) {
|
||||
console.log("CHECKING DEPLOYED URL ",res.deployedUrl)
|
||||
this.deployedUrl = res.deployedUrl;
|
||||
resolve(res.msg);
|
||||
} else {
|
||||
resolve('⚠️ No response received from HTML API.');
|
||||
@ -545,10 +671,74 @@ HTML Only. No CSS.
|
||||
});
|
||||
}
|
||||
|
||||
// Download HTML
|
||||
downloadHtml(pageName: string) {
|
||||
// Make sure we're using the sections in the correct order
|
||||
const orderedSectionKeys = this.sectionOrderMap[pageName] || this.getSectionKeys(pageName);
|
||||
|
||||
// Update the sectionHtmls order to match orderedSectionKeys
|
||||
const reorderedSectionHtmls: Record<string, SafeHtml> = {};
|
||||
for (const sectionKey of orderedSectionKeys) {
|
||||
if (this.sectionHtmls[pageName][sectionKey]) {
|
||||
reorderedSectionHtmls[sectionKey] = this.sectionHtmls[pageName][sectionKey];
|
||||
}
|
||||
}
|
||||
this.sectionHtmls[pageName] = reorderedSectionHtmls;
|
||||
|
||||
// Now update the full page HTML with correct order
|
||||
this.updateFullPageHtml(pageName);
|
||||
|
||||
const fullHtml = this.pageSections[pageName].FullPage.toString();
|
||||
const blob = new Blob([fullHtml], { type: 'text/html' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${pageName}.html`;
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/*
|
||||
///// commited code //// download
|
||||
// Download HTML
|
||||
downloadHtml(pageName: string) {
|
||||
const fullHtml = Object.values(this.pageSections[pageName]).join('\n');
|
||||
const blob = new Blob([fullHtml], { type: 'text/html' });
|
||||
const sectionOrder = this.sectionOrderMap[pageName] || Object.keys(this.sectionHtmls[pageName] || {});
|
||||
let fullHtml = '';
|
||||
|
||||
for (const sectionName of sectionOrder) {
|
||||
const html = this.sectionHtmls[pageName]?.[sectionName];
|
||||
if (html) {
|
||||
fullHtml += html + '\n\n';
|
||||
}
|
||||
}
|
||||
// const fullHtml = Object.values(this.pageSections[pageName]).join('\n');
|
||||
|
||||
const finalHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${pageName}</title>
|
||||
<style>
|
||||
${COMMON_CSS}
|
||||
|
||||
html, body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
min-height: 100% !important;
|
||||
width: 100% !important;
|
||||
font-family: 'Segoe UI', sans-serif !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${fullHtml}
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
// const fullHtml = Object.values(this.pageSections[pageName]).join('\n');
|
||||
const blob = new Blob([finalHtml], { type: 'text/html' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
@ -556,6 +746,7 @@ HTML Only. No CSS.
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
*/
|
||||
// Upload HTML files
|
||||
buildWireframe(projId: number) {
|
||||
const pageHtmlMap: Record<string, string> = {};
|
||||
@ -588,6 +779,8 @@ HTML Only. No CSS.
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
createHtmlFiles() {
|
||||
const pageHtmlMap: Record<string, string> = {};
|
||||
for (const [pageName, section] of Object.entries(this.pageSections)) {
|
||||
@ -793,15 +986,15 @@ return new Promise((resolve) => {
|
||||
|
||||
// Style properties
|
||||
activeEditTarget: string | null = null;
|
||||
liveStyles = {
|
||||
background: '',
|
||||
color: '',
|
||||
fontSize: '',
|
||||
padding: '',
|
||||
margin: '',
|
||||
fontWeight: '',
|
||||
textAlign: '',
|
||||
};
|
||||
// liveStyles = {
|
||||
// background: '',
|
||||
// color: '',
|
||||
// fontSize: '',
|
||||
// padding: '',
|
||||
// margin: '',
|
||||
// fontWeight: '',
|
||||
// textAlign: '',
|
||||
// };
|
||||
|
||||
// Select editable element
|
||||
selectEditable(pageName: string) {
|
||||
@ -1039,51 +1232,29 @@ return new Promise((resolve) => {
|
||||
|
||||
// Handle section drop
|
||||
onSectionDrop(event: CdkDragDrop<string[]>, pageName: string) {
|
||||
// Get current section keys
|
||||
const sectionKeys = this.getSectionKeys(pageName);
|
||||
|
||||
// Move item in the array
|
||||
moveItemInArray(sectionKeys, event.previousIndex, event.currentIndex);
|
||||
|
||||
// Create new section map in correct order
|
||||
const newSectionMap: Record<string, string> = {};
|
||||
sectionKeys.forEach(key => {
|
||||
newSectionMap[key] = this.allPagePrompts[pageName][key];
|
||||
});
|
||||
|
||||
// Update section map
|
||||
this.allPagePrompts[pageName] = newSectionMap;
|
||||
|
||||
// Update section HTMLs order
|
||||
const newSectionHtmls: Record<string, SafeHtml> = {};
|
||||
sectionKeys.forEach(key => {
|
||||
newSectionHtmls[key] = this.sectionHtmls[pageName][key];
|
||||
});
|
||||
|
||||
// Update section HTMLs
|
||||
this.sectionHtmls[pageName] = newSectionHtmls;
|
||||
|
||||
// Update the full page HTML
|
||||
this.updateFullPageHtml(pageName);
|
||||
|
||||
this.toastr.success(`Reordered sections in ${pageName}`);
|
||||
}
|
||||
const currentOrder = this.sectionOrderMap[pageName] || [];
|
||||
moveItemInArray(currentOrder, event.previousIndex, event.currentIndex);
|
||||
this.sectionOrderMap[pageName] = [...currentOrder];
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Zoom control methods
|
||||
zoomIn() {
|
||||
this.scale += 0.1;
|
||||
this.scale = Math.min(this.scale, 2); // Max zoom 200%
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.scale -= 0.1;
|
||||
this.scale = Math.max(this.scale, 0.5); // Min zoom 50%
|
||||
}
|
||||
zoomIn() {
|
||||
this.zoomLevel = Math.min(2, this.zoomLevel + 0.1);
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.zoomLevel = Math.max(0.5, this.zoomLevel - 0.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
selectedNavbarPage: string = '';
|
||||
navbarLinks: Record<string, { label: string, href: string }[]> = {};
|
||||
availablePages: { label: string, href: string }[] = [];
|
||||
// navbarLinks: Record<string, { label: string, href: string }[]> = {};
|
||||
// availablePages: { label: string, href: string }[] = [];
|
||||
|
||||
// Select Navbar Section
|
||||
selectNavbar(pageName: string) {
|
||||
@ -1132,6 +1303,81 @@ return new Promise((resolve) => {
|
||||
this.navbarLinks[pageName].splice(index, 1);
|
||||
}
|
||||
|
||||
toggleSidebar() {
|
||||
this.sidebarCollapsed = !this.sidebarCollapsed;
|
||||
this.updateGridLayout();
|
||||
}
|
||||
|
||||
private updateGridLayout() {
|
||||
setTimeout(() => {
|
||||
const canvas = document.querySelector('.canvas-main-area') as HTMLElement;
|
||||
if (canvas) {
|
||||
canvas.style.transition = 'margin 0.3s ease';
|
||||
canvas.style.marginLeft = this.sidebarCollapsed ? '0' : '300px';
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Export project as JSON file
|
||||
exportProject(): void {
|
||||
const exportData = {
|
||||
sitename: this.sitename,
|
||||
jsonInput: this.jsonInput,
|
||||
allPagePrompts: this.allPagePrompts,
|
||||
sectionHtmls: this.sectionHtmls,
|
||||
pageSections: this.pageSections,
|
||||
sectionOrderMap: this.sectionOrderMap,
|
||||
pageRenderOrder: this.pageRenderOrder,
|
||||
};
|
||||
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${this.sitename || 'project'}.json`;
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
this.toastr.success('Project exported as JSON');
|
||||
}
|
||||
|
||||
// Import project from JSON file
|
||||
importProject(): void {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.json,application/json';
|
||||
input.onchange = async (event: any) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = async (e: any) => {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result);
|
||||
this.sitename = data.sitename || '';
|
||||
this.jsonInput = data.jsonInput || '';
|
||||
this.allPagePrompts = data.allPagePrompts || {};
|
||||
this.sectionHtmls = data.sectionHtmls || {};
|
||||
this.pageSections = data.pageSections || {};
|
||||
this.sectionOrderMap = data.sectionOrderMap || {};
|
||||
this.pageRenderOrder = data.pageRenderOrder || [];
|
||||
this.toastr.success('Project imported successfully');
|
||||
// Optionally, re-render or trigger change detection
|
||||
await this.processAllPagesLiveUpdate();
|
||||
} catch (err) {
|
||||
this.toastr.error('Failed to import project: Invalid JSON');
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
|
||||
// Toggle preview mode (simple implementation)
|
||||
previewMode(): void {
|
||||
this.sidebarOpen = false;
|
||||
this.toastr.info('Preview mode toggled');
|
||||
}
|
||||
|
||||
// Apply Changes to navbar
|
||||
applyNavbarChanges(pageName: string): void {
|
||||
const links = this.navbarLinks[pageName] || [];
|
||||
@ -1160,12 +1406,9 @@ return new Promise((resolve) => {
|
||||
this.pageSections[pageName].FullPage = this.sanitizer.bypassSecurityTrustHtml(updatedHtml);
|
||||
this.toastr.success('Navbar updated successfully');
|
||||
}
|
||||
|
||||
resetZoom() {
|
||||
this.scale = 1;
|
||||
this.panX = 0;
|
||||
this.panY = 0;
|
||||
}
|
||||
resetZoom() {
|
||||
this.zoomLevel = 1;
|
||||
}
|
||||
|
||||
// Get transform CSS for pan and zoom
|
||||
getTransform(): string {
|
||||
@ -1174,51 +1417,27 @@ return new Promise((resolve) => {
|
||||
|
||||
// Pan methods
|
||||
startPan(event: MouseEvent): void {
|
||||
// Start panning with any mouse button or when space key is pressed
|
||||
if (event.button === 0 || event.button === 1 || event.ctrlKey || event.metaKey) {
|
||||
// Don't start panning if clicking on an editable element
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.hasAttribute('contenteditable') && target.getAttribute('contenteditable') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isPanning = true;
|
||||
this.lastX = event.clientX;
|
||||
this.lastY = event.clientY;
|
||||
|
||||
// Change cursor to indicate panning
|
||||
const canvasBoundary = document.querySelector('.canvas-boundary') as HTMLElement;
|
||||
if (canvasBoundary) {
|
||||
canvasBoundary.style.cursor = 'grabbing';
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
doPan(event: MouseEvent) {
|
||||
if (!this.isPanning) return;
|
||||
|
||||
const deltaX = (event.clientX - this.lastX) / this.scale;
|
||||
const deltaY = (event.clientY - this.lastY) / this.scale;
|
||||
|
||||
this.panX += deltaX;
|
||||
this.panY += deltaY;
|
||||
|
||||
this.lastX = event.clientX;
|
||||
this.lastY = event.clientY;
|
||||
}
|
||||
|
||||
endPan(): void {
|
||||
if (this.isPanning) {
|
||||
this.isPanning = false;
|
||||
|
||||
// Restore cursor
|
||||
const canvasBoundary = document.querySelector('.canvas-boundary') as HTMLElement;
|
||||
if (canvasBoundary) {
|
||||
canvasBoundary.style.cursor = 'move';
|
||||
}
|
||||
this.isDragging = true;
|
||||
this.startPanPosition = { x: event.clientX, y: event.clientY };
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
doPan(event: MouseEvent): void {
|
||||
if (!this.isDragging) return;
|
||||
|
||||
const deltaX = event.clientX - this.startPanPosition.x;
|
||||
const deltaY = event.clientY - this.startPanPosition.y;
|
||||
|
||||
this.panX += deltaX;
|
||||
this.panY += deltaY;
|
||||
|
||||
this.startPanPosition = { x: event.clientX, y: event.clientY };
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
endPan(): void {
|
||||
this.isDragging = false;
|
||||
}
|
||||
|
||||
// Edit section
|
||||
@ -1256,17 +1475,26 @@ if (this.isPanning) {
|
||||
onSectionEdit(event: any) {
|
||||
// This is handled in saveSectionEdit
|
||||
}
|
||||
copyPageToClipboard(): void {
|
||||
const currentPage = this.getCurrentPageName();
|
||||
const html = this.pageSections[currentPage]?.FullPage?.toString() || '';
|
||||
navigator.clipboard.writeText(html).then(() => {
|
||||
this.toastr.success(`Copied ${currentPage} to clipboard`);
|
||||
});
|
||||
}
|
||||
|
||||
handleDirectContentEdit(event: FocusEvent, pageName: string, sectionKey: string): void {
|
||||
const element = event.target as HTMLElement;
|
||||
if (element.hasAttribute('contenteditable') && element.getAttribute('contenteditable') === 'true') {
|
||||
// Save the content when the element loses focus
|
||||
this.saveSectionChanges(pageName, sectionKey, element.innerHTML);
|
||||
|
||||
// Turn off contenteditable
|
||||
element.setAttribute('contenteditable', 'false');
|
||||
}
|
||||
}
|
||||
downloadCurrentPage(): void {
|
||||
const currentPage = this.getCurrentPageName();
|
||||
this.downloadHtml(currentPage);
|
||||
}
|
||||
|
||||
// Update existing download method
|
||||
handleDirectContentEdit(event: Event, page: string, section: string) {
|
||||
const target = event.target as HTMLElement;
|
||||
const content = target.innerHTML;
|
||||
this.sectionHtmls[page][section] = this.sanitizer.bypassSecurityTrustHtml(content);
|
||||
this.updateFullPageHtml(page);
|
||||
}
|
||||
|
||||
// Close edit mode
|
||||
closeEditMode() {
|
||||
@ -1337,6 +1565,13 @@ if (this.isPanning) {
|
||||
};
|
||||
}
|
||||
|
||||
// Add this to initialize the current page
|
||||
|
||||
|
||||
getPageTransform(): string {
|
||||
return `scale(${this.scale})`;
|
||||
}
|
||||
|
||||
refreshAllSectionCSS(): void {
|
||||
// Refresh CSS in all section previews
|
||||
for (const pageName of this.objectKeys(this.pageSections)) {
|
||||
@ -1356,7 +1591,7 @@ if (this.isPanning) {
|
||||
// Ensure CSS is applied to the DOM
|
||||
this.ensureCommonCssForAllSections();
|
||||
|
||||
this.toastr.success('Refreshed CSS for all sections');
|
||||
// this.toastr.success('Refreshed CSS for all sections');
|
||||
}
|
||||
|
||||
// Remove section
|
||||
@ -1369,9 +1604,171 @@ if (this.isPanning) {
|
||||
// Update the full page HTML
|
||||
this.updateFullPageHtml(pageName);
|
||||
|
||||
this.toastr.success(`Removed section: ${sectionKey}`);
|
||||
// this.toastr.success(`Removed section: ${sectionKey}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Modal state for new page
|
||||
showNewPageModal: boolean = false;
|
||||
|
||||
// Track current page index for navigation and actions
|
||||
// currentPageIndex: number = 0;
|
||||
|
||||
// Used for new page creation
|
||||
newPageName: string = '';
|
||||
|
||||
// Add a new page (shows modal)
|
||||
addNewPage(): void {
|
||||
this.showNewPageModal = true;
|
||||
this.newPageName = '';
|
||||
}
|
||||
|
||||
// Close the new page modal
|
||||
closeNewPageModal(): void {
|
||||
this.showNewPageModal = !this.showNewPageModal;
|
||||
this.newPageName = '';
|
||||
}
|
||||
|
||||
// Confirm adding a new page
|
||||
confirmAddNewPage(): void {
|
||||
if (!this.newPageName.trim()) {
|
||||
this.toastr.error('Please enter a page name');
|
||||
return;
|
||||
}
|
||||
if (this.pageRenderOrder.includes(this.newPageName)) {
|
||||
this.toastr.error('Page name already exists');
|
||||
return;
|
||||
}
|
||||
this.pageRenderOrder.push(this.newPageName);
|
||||
this.allPagePrompts[this.newPageName] = {};
|
||||
this.sectionHtmls[this.newPageName] = {};
|
||||
this.pageSections[this.newPageName] = { FullPage:''};
|
||||
this.sectionOrderMap[this.newPageName] = [];
|
||||
this.closeNewPageModal();
|
||||
this.toastr.success(`Added new page: ${this.newPageName}`);
|
||||
}
|
||||
|
||||
// Duplicate the current page
|
||||
duplicateCurrentPage(): void {
|
||||
const currentPage = this.getCurrentPage();
|
||||
if (!currentPage) {
|
||||
this.toastr.error('No page selected to duplicate');
|
||||
return;
|
||||
}
|
||||
let newPageName = currentPage + '_Copy';
|
||||
let count = 1;
|
||||
while (this.pageRenderOrder.includes(newPageName)) {
|
||||
newPageName = `${currentPage}_Copy${count++}`;
|
||||
}
|
||||
// Deep clone all structures
|
||||
this.pageRenderOrder.push(newPageName);
|
||||
this.allPagePrompts[newPageName] = JSON.parse(JSON.stringify(this.allPagePrompts[currentPage]));
|
||||
this.sectionHtmls[newPageName] = JSON.parse(JSON.stringify(this.sectionHtmls[currentPage]));
|
||||
this.pageSections[newPageName] = { FullPage: this.pageSections[currentPage].FullPage };
|
||||
this.sectionOrderMap[newPageName] = [...(this.sectionOrderMap[currentPage] || [])];
|
||||
this.toastr.success(`Duplicated page: ${currentPage}`);
|
||||
}
|
||||
|
||||
// Delete the current page
|
||||
deleteCurrentPage(): void {
|
||||
const currentPage = this.getCurrentPage();
|
||||
if (!currentPage) {
|
||||
this.toastr.error('No page selected to delete');
|
||||
return;
|
||||
}
|
||||
if (this.pageRenderOrder.length === 1) {
|
||||
this.toastr.error('At least one page must remain');
|
||||
return;
|
||||
}
|
||||
if (confirm(`Are you sure you want to delete the page "${currentPage}"?`)) {
|
||||
// Remove from all structures
|
||||
this.pageRenderOrder = this.pageRenderOrder.filter(p => p !== currentPage);
|
||||
delete this.allPagePrompts[currentPage];
|
||||
delete this.sectionHtmls[currentPage];
|
||||
delete this.pageSections[currentPage];
|
||||
delete this.sectionOrderMap[currentPage];
|
||||
// Adjust currentPageIndex if needed
|
||||
if (this.currentPageIndex >= this.pageRenderOrder.length) {
|
||||
this.currentPageIndex = this.pageRenderOrder.length - 1;
|
||||
}
|
||||
this.toastr.success(`Deleted page: ${currentPage}`);
|
||||
}
|
||||
}
|
||||
|
||||
getGridSize(): string {
|
||||
// Example: returns a 20px grid, adjust as needed
|
||||
return `translate(${this.panX}px, ${this.panY}px) scale(${this.scale})`;
|
||||
}
|
||||
|
||||
// Add a new section to the current page
|
||||
addNewSectionToCurrentPage() {
|
||||
const page = this.getCurrentPageName();
|
||||
if (!page) return;
|
||||
|
||||
const newSection = `section-${Date.now()}`;
|
||||
this.sectionOrderMap[page] = [...(this.sectionOrderMap[page] || []), newSection];
|
||||
this.sectionHtmls[page][newSection] = this.sanitizer.bypassSecurityTrustHtml('<div>New Section</div>');
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
// Get the transform for the grid (alias for getTransform)
|
||||
getGridTransform(): string {
|
||||
return `translate(${this.panX}px, ${this.panY}px) scale(${this.scale})`;
|
||||
}
|
||||
|
||||
deleteSection(page: string, section: string) {
|
||||
this.lastDeletedSection = {
|
||||
page,
|
||||
section,
|
||||
content: this.sectionHtmls[page][section]
|
||||
};
|
||||
|
||||
this.sectionOrderMap[page] = this.sectionOrderMap[page].filter(s => s !== section);
|
||||
delete this.sectionHtmls[page][section];
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
// Add this method to fix template errors
|
||||
|
||||
|
||||
|
||||
// Add this method to your component
|
||||
getCurrentPageSections(): string[] {
|
||||
const currentPage = this.getCurrentPageName();
|
||||
return currentPage ?
|
||||
this.sectionOrderMap[currentPage] || Object.keys(this.sectionHtmls[currentPage] || {})
|
||||
: [];
|
||||
}
|
||||
|
||||
// Also add this helper method if missing
|
||||
getCurrentPageName(): string {
|
||||
return this.pageRenderOrder[this.currentPageIndex] || '';
|
||||
}
|
||||
// Handle mouse wheel for zooming
|
||||
onWheel(event: WheelEvent): void {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
event.preventDefault();
|
||||
if (event.deltaY < 0) {
|
||||
this.zoomIn();
|
||||
} else {
|
||||
this.zoomOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current page name
|
||||
getCurrentPage(): string | null {
|
||||
if (this.pageRenderOrder.length === 0) return null;
|
||||
this.currentPageIndex = Math.min(this.currentPageIndex, this.pageRenderOrder.length - 1);
|
||||
return this.pageRenderOrder[this.currentPageIndex];
|
||||
}
|
||||
|
||||
// Update the canvas transform methods
|
||||
|
||||
|
||||
// Detect if the device is mobile (simple check)
|
||||
get isMobile(): boolean {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
// Open new section modal
|
||||
addNewSection(pageName: string, afterSection: string = null) {
|
||||
@ -1390,6 +1787,16 @@ if (this.isPanning) {
|
||||
this.newSectionType = '';
|
||||
this.newSectionDescription = '';
|
||||
}
|
||||
|
||||
undoDelete() {
|
||||
if (this.lastDeletedSection) {
|
||||
const { page, section, content } = this.lastDeletedSection;
|
||||
this.sectionOrderMap[page].push(section);
|
||||
this.sectionHtmls[page][section] = content;
|
||||
this.lastDeletedSection = null;
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm adding new section
|
||||
async confirmAddSection() {
|
||||
@ -1471,9 +1878,15 @@ if (this.isPanning) {
|
||||
this.newSectionType = '';
|
||||
this.newSectionDescription = '';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // wireframe-renderer.component.ts
|
||||
// import { Component, Input, OnInit } from '@angular/core';
|
||||
// import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
|
||||
@ -58,8 +58,7 @@
|
||||
<!-- Floating Square Button -->
|
||||
<!-- Tab Section Container -->
|
||||
|
||||
|
||||
<div style="margin-top: 10px; padding: 20px;">
|
||||
<div style="margin-top: -30px; padding: 20px;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px;">
|
||||
|
||||
<!-- Left: Floating Square Button -->
|
||||
@ -77,11 +76,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Generate Wireframe Button -->
|
||||
<!-- <button (click)="activeTab = 'wireframe'" style="background: #28a745; color: white; border: none; padding: 8px 14px; border-radius: 4px; cursor: pointer;">
|
||||
Generate Wireframe
|
||||
</button> -->
|
||||
|
||||
<!-- Right: Generate Wireframe Button -->
|
||||
<button (click)="generateWireframe()"
|
||||
style="background: #28a745; color: white; border: none; padding: 8px 14px; border-radius: 4px; cursor: pointer;">
|
||||
@ -101,67 +95,101 @@
|
||||
<!-- Wireframe Content -->
|
||||
<div *ngIf="activeTab === 'wireframe'" style="margin-top: 20px; max-width: 100%; overflow-x: auto;">
|
||||
<div style="min-width: 800px;">
|
||||
<!-- <app-wireframe-renderer *ngIf="treeData"></app-wireframe-renderer> -->
|
||||
<app-wireframe-renderer *ngIf="isWireframeLoaded && treeData"></app-wireframe-renderer>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Popup Dialog -->
|
||||
<!-- <div *ngIf="showPopup"
|
||||
style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 1000;">
|
||||
<div style="background: white; padding: 20px; width: 500px; border-radius: 8px; position: relative;">
|
||||
|
||||
<button (click)="showPopup = false"
|
||||
style="position: absolute; top: 10px; right: 10px; background: #ff4d4f; border: none; color: white; border-radius: 50%; width: 30px; height: 30px; font-size: 18px; cursor: pointer;">
|
||||
×
|
||||
</button>
|
||||
|
||||
<h2 style="margin-bottom: 10px;">Generate JSON</h2>
|
||||
|
||||
<textarea [(ngModel)]="rawInputText" rows="6"
|
||||
style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;"></textarea>
|
||||
|
||||
<button (click)="generateJson()"
|
||||
style="margin-top: 10px; padding: 8px 16px; background: green; color: white; border: none; border-radius: 4px;">
|
||||
Generate
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
|
||||
<!-- Popup Dialog with improved z-index and animations -->
|
||||
<div *ngIf="showPopup" [@fadeIn] class="modal-overlay"
|
||||
style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.75); display: flex; align-items: center; justify-content: center; z-index: 1000;">
|
||||
<div class="chart-box" [@slideIn]
|
||||
style="width: 600px; max-height: none; padding: 24px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); border-radius: 8px; border: none; transform-origin: center; background-color: snow">
|
||||
|
||||
<!-- Enhanced Popup Dialog with Loading States -->
|
||||
<div *ngIf="showPopup" class="modal-overlay">
|
||||
|
||||
<!-- Main Content State -->
|
||||
<div *ngIf="!isLoading && !showError" class="popup-content">
|
||||
<!-- Close button -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
||||
<h3 style="margin: 0; color: #333; font-weight: 500;">Generate Site Structure</h3>
|
||||
<button (click)="showPopup = false" [@pulseOnHover]
|
||||
style="background: transparent; border: none; color: #666; font-size: 24px; cursor: pointer; padding: 4px; height: 36px; width: 36px; border-radius: 50%; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; color: #f5f5f5;">
|
||||
×
|
||||
</button>
|
||||
<button (click)="closePopup()" class="close-btn">×</button>
|
||||
</div>
|
||||
|
||||
<p style="margin-bottom: 16px; color: #666;">Describe your website structure in plain language:</p>
|
||||
|
||||
<textarea [(ngModel)]="rawInputText" rows="8" [@focusBorder]
|
||||
class="tag-input"
|
||||
style="width: 100%; margin-bottom: 20px; font-family: inherit; resize: vertical; transition: border 0.3s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.05);"></textarea>
|
||||
<textarea [(ngModel)]="rawInputText" rows="8" class="input-textarea"></textarea>
|
||||
|
||||
<!-- Dropdown for number of pages -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="pageCount" style="display: block; margin-bottom: 6px; color: #333; font-weight: 500;">Select Number of Pages:</label>
|
||||
<select id="pageCount" [(ngModel)]="selectedPageRange" class="page-select">
|
||||
<option value="5-10">5 - 10</option>
|
||||
<option value="10-15">10 - 15</option>
|
||||
<option value="15-20">15 - 20</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
||||
<button (click)="showPopup = false" [@buttonHover]
|
||||
style="padding: 8px 16px; background: #f5f5f5; color: #333; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; transition: all 0.2s ease;">
|
||||
Cancel
|
||||
</button>
|
||||
<button (click)="generateJson()" [@buttonHover]
|
||||
style="padding: 8px 18px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">
|
||||
Generate Structure
|
||||
</button>
|
||||
<button (click)="closePopup()" class="cancel-btn">Cancel</button>
|
||||
<button (click)="generateJsonWithLoading()" class="generate-btn">Generate Structure</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div *ngIf="isLoading" class="loading-container">
|
||||
<!-- Animated Background Pattern -->
|
||||
<div class="bg-pattern"></div>
|
||||
|
||||
<!-- Main Loader -->
|
||||
<div class="loader-content">
|
||||
<!-- Spinning Brain Icon -->
|
||||
<div class="thinking-icon">
|
||||
<div class="brain-emoji">🧠</div>
|
||||
</div>
|
||||
|
||||
<!-- Thinking Text with Typewriter Effect -->
|
||||
<h2 class="thinking-text">
|
||||
Thinking<span class="dots">...</span>
|
||||
</h2>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<p class="thinking-subtitle">Crafting your perfect site structure</p>
|
||||
|
||||
<!-- Progress Dots -->
|
||||
<div class="progress-dots">
|
||||
<div class="dot dot-1"></div>
|
||||
<div class="dot dot-2"></div>
|
||||
<div class="dot dot-3"></div>
|
||||
<div class="dot dot-4"></div>
|
||||
<div class="dot dot-5"></div>
|
||||
</div>
|
||||
|
||||
<!-- Floating Particles -->
|
||||
<div class="particles">
|
||||
<div class="particle particle-1"></div>
|
||||
<div class="particle particle-2"></div>
|
||||
<div class="particle particle-3"></div>
|
||||
<div class="particle particle-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div *ngIf="showError" class="error-container">
|
||||
<!-- Error Icon -->
|
||||
<div class="error-icon">
|
||||
<div style="font-size: 36px;">⚠️</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Message -->
|
||||
<h2 class="error-title">Something went wrong</h2>
|
||||
|
||||
<p class="error-message">
|
||||
We couldn't generate your site structure. Please check your input and try again.
|
||||
</p>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div style="display: flex; gap: 12px; justify-content: center;">
|
||||
<button (click)="retryGeneration()" class="retry-btn">Try Again</button>
|
||||
<button (click)="closePopup()" class="error-close-btn">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -140,7 +140,411 @@
|
||||
// }
|
||||
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.75);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(8px);
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* Main Popup Content */
|
||||
.popup-content {
|
||||
width: 600px;
|
||||
max-height: none;
|
||||
padding: 24px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.25);
|
||||
border-radius: 8px;
|
||||
background-color: snow;
|
||||
animation: slideIn 0.4s ease-out;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #666;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.input-textarea {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
transition: border 0.3s ease;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.input-textarea:focus {
|
||||
border-color: #1976d2;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
|
||||
}
|
||||
|
||||
.page-select {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
font-size: 15px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
padding: 8px 16px;
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
background: #e0e0e0;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
padding: 8px 18px;
|
||||
background: #1976d2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.generate-btn:hover {
|
||||
background: #1565c0;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* Loading Container */
|
||||
.loading-container {
|
||||
width: 400px;
|
||||
padding: 40px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
animation: slideIn 0.4s ease-out;
|
||||
}
|
||||
|
||||
.bg-pattern {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.1;
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
rgba(255,255,255,0.1) 10px,
|
||||
rgba(255,255,255,0.1) 20px
|
||||
);
|
||||
}
|
||||
|
||||
.loader-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.thinking-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto 24px;
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
|
||||
animation: spinPulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.brain-emoji {
|
||||
font-size: 36px;
|
||||
animation: brainThink 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.thinking-text {
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.dots {
|
||||
animation: typewriter 1.5s infinite;
|
||||
}
|
||||
|
||||
.thinking-subtitle {
|
||||
color: rgba(255,255,255,0.9);
|
||||
font-size: 16px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.progress-dots {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.4);
|
||||
animation: dotPulse 1.4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.dot-1 { animation-delay: 0s; }
|
||||
.dot-2 { animation-delay: 0.2s; }
|
||||
.dot-3 { animation-delay: 0.4s; }
|
||||
.dot-4 { animation-delay: 0.6s; }
|
||||
.dot-5 { animation-delay: 0.8s; }
|
||||
|
||||
.particles {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.6);
|
||||
animation: float 3s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.particle-1 {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
top: 20%;
|
||||
left: 15%;
|
||||
animation-duration: 3s;
|
||||
}
|
||||
|
||||
.particle-2 {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: rgba(255,255,255,0.4);
|
||||
top: 60%;
|
||||
right: 20%;
|
||||
animation-duration: 4s;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
.particle-3 {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
background: rgba(255,255,255,0.7);
|
||||
bottom: 30%;
|
||||
left: 25%;
|
||||
animation-duration: 2.5s;
|
||||
}
|
||||
|
||||
.particle-4 {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background: rgba(255,255,255,0.5);
|
||||
top: 40%;
|
||||
right: 15%;
|
||||
animation-duration: 3.5s;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
/* Error Container */
|
||||
.error-container {
|
||||
width: 450px;
|
||||
padding: 32px;
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
animation: slideIn 0.4s ease-out;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto 24px;
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: errorShake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: rgba(255,255,255,0.9);
|
||||
font-size: 16px;
|
||||
margin-bottom: 32px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
padding: 12px 20px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
color: white;
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: rgba(255,255,255,0.3);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.error-close-btn {
|
||||
padding: 12px 20px;
|
||||
background: white;
|
||||
color: #ee5a52;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.error-close-btn:hover {
|
||||
background: #f8f8f8;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Keyframe Animations */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px) scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinPulse {
|
||||
0%, 100% {
|
||||
transform: rotate(0deg) scale(1);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg) scale(1.1);
|
||||
box-shadow: 0 12px 48px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes brainThink {
|
||||
0%, 100% { transform: scale(1) rotate(0deg); }
|
||||
25% { transform: scale(1.1) rotate(-5deg); }
|
||||
75% { transform: scale(0.95) rotate(5deg); }
|
||||
}
|
||||
|
||||
@keyframes typewriter {
|
||||
0%, 20% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
80%, 100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes dotPulse {
|
||||
0%, 20%, 80%, 100% {
|
||||
transform: scale(1);
|
||||
background: rgba(255,255,255,0.4);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.5);
|
||||
background: rgba(255,255,255,0.9);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-20px) rotate(180deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes errorShake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-10px); }
|
||||
75% { transform: translateX(10px); }
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.popup-content, .loading-container, .error-container {
|
||||
width: 90%;
|
||||
margin: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Your existing styles */
|
||||
.controls {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@ -171,51 +575,6 @@
|
||||
background-color: #1565c0;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 1rem;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 300px;
|
||||
height: 120px;
|
||||
font-family: monospace;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #303f9f;
|
||||
}
|
||||
|
||||
mat-tab-group {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 1rem;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 10px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.active-tab {
|
||||
border-bottom: 2px solid #007bff;
|
||||
color: #007bff;
|
||||
@ -228,30 +587,16 @@ mat-tab-group {
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
color: black; /* 👈 Text color black */
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
border-bottom: 2px solid #000;
|
||||
background: #e0e0e0;
|
||||
color: black; /* 👈 Ensure active tab also stays black */
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
.editor-container {
|
||||
position: relative; /* 🔑 Important to contain absolute button */
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.modal-overlay *{
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
||||
:host {
|
||||
--zoom-level: 1;
|
||||
--primary-color: #2196f3;
|
||||
@ -262,5 +607,4 @@ mat-tab-group {
|
||||
--level: 0;
|
||||
--primary-color: #2196f3;
|
||||
--border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
}
|
||||
@ -122,9 +122,11 @@ export class TreeVisualizerComponent {
|
||||
}`;
|
||||
treeData: any = null;
|
||||
rawInputText: string = `make a website , where we can login , sign up.
|
||||
inside that there is a menu of icard where we can add a icard detail iacardname, description
|
||||
after click this icard inside we upload excel of student data, icard template, after click convert it fetch all data of excel and replace the key value of icard template..`;
|
||||
inside that there is a menu of icard where we can add a icard detail iacardname, description
|
||||
after click this icard inside we upload excel of student data, icard template, after click convert it fetch all data of excel and replace the key value of icard template..`;
|
||||
id: number;
|
||||
selectedPageRange: string = '5-10'; // 👈 Default value
|
||||
|
||||
|
||||
constructor(private treeService: treeVisualizerService,
|
||||
private siteTreeService: SiteTreeservice,
|
||||
@ -143,7 +145,41 @@ after click this icard inside we upload excel of student data, icard template, a
|
||||
}
|
||||
|
||||
|
||||
// Add these properties to your component class
|
||||
isLoading = false;
|
||||
showError = false;
|
||||
|
||||
// Method to handle the enhanced generate flow
|
||||
generateJsonWithLoading() {
|
||||
this.isLoading = true;
|
||||
this.showError = false;
|
||||
|
||||
// Your existing generateJson logic here
|
||||
// Replace with your actual API call
|
||||
setTimeout(() => {
|
||||
// Success case
|
||||
this.isLoading = false;
|
||||
this.showPopup = false;
|
||||
this.activeTab = 'sitemap';
|
||||
|
||||
// For error case, use this instead:
|
||||
// this.isLoading = false;
|
||||
// this.showError = true;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Enhanced close method
|
||||
closePopup() {
|
||||
this.showPopup = false;
|
||||
this.isLoading = false;
|
||||
this.showError = false;
|
||||
}
|
||||
|
||||
// Retry method for error state
|
||||
retryGeneration() {
|
||||
this.showError = false;
|
||||
this.generateJsonWithLoading();
|
||||
}
|
||||
convertJson() {
|
||||
try {
|
||||
const parsed = JSON.parse(this.inputJson);
|
||||
@ -223,7 +259,7 @@ after click this icard inside we upload excel of student data, icard template, a
|
||||
generateJson() {
|
||||
console.log('genearte json start ')
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user