This commit is contained in:
string 2025-06-11 18:48:33 +05:30
parent 32c8b6acd4
commit 0b8f762a63
7 changed files with 512 additions and 308 deletions

View File

@ -57,8 +57,8 @@ export class EditstepperComponent implements OnInit {
rowSelected: any = {}; rowSelected: any = {};
modalcomplete = false; modalcomplete = false;
constructor(private mainService: Visa_applicationservice, constructor(
private mainService: Visa_applicationservice,
private _fb: FormBuilder, private _fb: FormBuilder,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,

View File

@ -73,7 +73,7 @@
</button> </button>
</div> </div>
<div class="offcanvas-body"> <div class="offcanvas-body">
<div class="section-templates-container"> <div class="section-templates-container">
<!-- Default Sections --> <!-- Default Sections -->
<h4>Default Sections</h4> <h4>Default Sections</h4>
<div class="section-template" *ngFor="let template of sectionTemplates" <div class="section-template" *ngFor="let template of sectionTemplates"
@ -92,7 +92,7 @@
<!-- Message when no custom sections --> <!-- Message when no custom sections -->
<div *ngIf="customSections.length === 0" class="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;"> 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! No custom sections created yet. Create one below!
</div> </div>
</div> </div>
@ -115,31 +115,38 @@
</div> </div>
<!-- Edit Section Offcanvas --> <!-- Edit Section Offcanvas -->
<div class="offcanvas edit-section" [class.show]="showEditSectionOffcanvas"> <div class="offcanvas edit-section" [class.show]="showEditSectionOffcanvas">
<div class="offcanvas-header"> <div class="offcanvas-header">
<div class="offcanvas-title">Edit Section: {{ selectedSection?.type }}</div> <div class="offcanvas-title">Edit Section: {{ selectedSection?.type }}</div>
<button class="close-btn" (click)="closeEditSectionOffcanvas()"> <button class="close-btn" (click)="closeEditSectionOffcanvas()">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
</button> </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>
<div class="form-group mt-3"> <div class="offcanvas-body">
<label class="form-label">Content</label> <div class="form-group">
<!-- Fixed binding here --> <label class="form-label">Section Type</label>
<textarea class="form-control" [(ngModel)]="selectedSection.content" *ngIf="selectedSection" rows="8" <input type="text" class="form-control" [(ngModel)]="selectedSection.type" *ngIf="selectedSection"
placeholder="Enter content"></textarea> placeholder="Section type">
</div> </div>
<div class="form-actions mt-4"> <div class="form-group mt-3">
<button class="btn btn-secondary" (click)="closeEditSectionOffcanvas()">Cancel</button> <label class="form-label">Content</label>
<button class="btn btn-primary" (click)="saveSection()">Save Changes</button> <!-- Fixed binding here -->
<textarea class="form-control" [(ngModel)]="selectedSection.content" *ngIf="selectedSection" rows="8"
placeholder="Enter content"></textarea>
</div>
<div class="form-group mt-3">
<label class="form-label">Keywords (comma separated)</label>
<input type="text" class="form-control" [(ngModel)]="selectedSection.keywords"
placeholder="e.g. technology, business" *ngIf="selectedSection">
</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> </div>
</div>
<div class="modal" [class.show]="showAddChildModal"> <div class="modal" [class.show]="showAddChildModal">
<div class="modal-backdrop" *ngIf="showAddChildModal" (click)="closeAddChildModal()"></div> <div class="modal-backdrop" *ngIf="showAddChildModal" (click)="closeAddChildModal()"></div>
@ -167,98 +174,96 @@
<div class="spinner"></div> <div class="spinner"></div>
<div class="loading-text">Processing...</div> <div class="loading-text">Processing...</div>
</div> </div>
<!-- Updated page template with proper connector lines --> <!-- Updated page template with proper connector lines -->
<ng-template #pageTemplate let-page> <ng-template #pageTemplate let-page>
<div class="node-container drop-zone" <div class="node-container drop-zone" draggable="true" (dragstart)="onPageDragStart(page, $event)"
draggable="true" (dragend)="onPageDragEnd($event)" (dragover)="onPageDragOver($event)" (dragleave)="onPageDragLeave($event)"
(dragstart)="onPageDragStart(page, $event)" (drop)="onPageDrop(page, $event)" style="position: relative; margin: 20px 0;">
(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 --> <!-- Single consistent connector line from parent -->
<div *ngIf="page.parent && page.parent !== rootPage" <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> style="position: absolute; top: -20px; left: 50%; width: 2px; height: 20px; background-color: #6c757d !important; z-index: 1; transform: translateX(-50%);">
<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>
<div class="sections-container" cdkDropList [cdkDropListData]="page.sections" <div class="node-card" [class.selected]="selectedPage === page" (click)="selectPage(page)">
(cdkDropListDropped)="onSectionDrop($event)"> <div class="node-header">
<i class="node-icon" [class]="getNodeIcon(page)"></i>
<span class="node-title">{{ page.name }}</span>
</div>
<!-- Show message and add button for empty pages --> <div class="sections-container" cdkDropList [cdkDropListData]="page.sections"
<div *ngIf="page.sections.length === 0" class="empty-page-container" (cdkDropListDropped)="onSectionDrop($event)">
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> <!-- Show message and add button for empty pages -->
<button class="btn btn-primary btn-sm" (click)="showAddSectionForEmptyPage(page, $event)" <div *ngIf="page.sections.length === 0" class="empty-page-container"
style="background-color: #007bff !important; border: none; padding: 8px 16px; border-radius: 4px; color: white;"> style="padding: 20px; text-align: center; border: 2px dashed #ccc; margin: 10px 0; border-radius: 8px;">
<i class="fas fa-plus"></i> Add First Section <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>
<!-- 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-hover-btn" (click)="openAddSectionOffcanvas(page, i, $event)" title="Add Section">
<i class="fas fa-plus"></i>
</div>
</div>
<!-- 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="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> </button>
</div> </div>
<div class="add-child-hover-btn" (click)="showAddChildPageModal(page); $event.stopPropagation()">
<!-- 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-hover-btn" (click)="openAddSectionOffcanvas(page, i, $event)" title="Add Section">
<i class="fas fa-plus"></i>
</div>
</div>
<!-- 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="node-actions">
<button class="node-action-btn" title="Add Child Page"
(click)="showAddChildPageModal(page); $event.stopPropagation()">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
</button> </div>
<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> <!-- 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>
</div> </div>
</ng-template>
<!-- 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>

View File

@ -9,6 +9,8 @@ import { ToastrService } from 'ngx-toastr';
interface Section { interface Section {
type: string; type: string;
content: string; content: string;
keywords?: string; // <-- Add this line
} }
interface Page { interface Page {
@ -140,17 +142,17 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
customSections = [ customSections = [
{ {
type: 'Visa Application', type: 'Visa Application',
description: 'Visa application form and process', description: 'Visa application form and process',
content: 'Complete visa application with required documents and processing information.' content: 'Complete visa application with required documents and processing information.'
}, },
{ {
type: 'Visa Application 2', type: 'Visa Application 2',
description: 'Alternative visa application layout', description: 'Alternative visa application layout',
content: 'Alternative visa application design with step-by-step guidance.' content: 'Alternative visa application design with step-by-step guidance.'
} }
]; ];
constructor( constructor(
@ -538,6 +540,18 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// If parsing fails, use the raw content // If parsing fails, use the raw content
pageObj[section.type] = section.content; pageObj[section.type] = section.content;
} }
// ✅ Save keywords if present
if (section.keywords) {
if (typeof pageObj[section.type] === 'object') {
pageObj[section.type].keywords = section.keywords;
} else {
pageObj[section.type] = {
text: pageObj[section.type],
keywords: section.keywords
};
}
}
}); });
// Add children // Add children
@ -709,19 +723,19 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// Save section changes // Save section changes
saveSection(): void { saveSection(): void {
if (this.selectedSection && this.currentParentPage) { if (this.selectedSection && this.currentParentPage) {
// Find the original section in the parent page by reference // Find the original section in the parent page by reference
const originalSection = this.currentParentPage.sections.find(s => s === this.selectedSection); const originalSection = this.currentParentPage.sections.find(s => s === this.selectedSection);
if (originalSection) { if (originalSection) {
// Update the original section directly // Update the original section directly
originalSection.type = this.selectedSection.type; originalSection.type = this.selectedSection.type;
originalSection.content = this.selectedSection.content; originalSection.content = this.selectedSection.content;
this.hasUnsavedChanges = true; this.hasUnsavedChanges = true;
}
} }
this.closeEditSectionOffcanvas();
} }
this.closeEditSectionOffcanvas();
}
// Add section from a template // Add section from a template
addSectionFromTemplate(template: any): void { addSectionFromTemplate(template: any): void {
@ -751,79 +765,79 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// Similar update for addCustomSection // Similar update for addCustomSection
addCustomSection(): void { addCustomSection(): void {
if (this.currentParentPage && this.customSectionType) { if (this.currentParentPage && this.customSectionType) {
const newSection: Section = { const newSection: Section = {
type: this.customSectionType,
content: this.customSectionContent || ''
};
// 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, type: this.customSectionType,
description: 'Custom section',
content: this.customSectionContent || '' content: this.customSectionContent || ''
}); };
// 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();
} }
// 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);
}
addCustomSectionFromList(customSection: any): void { if (!this.availableSectionTypes.includes(customSection.type)) {
if (this.currentParentPage) { this.availableSectionTypes.push(customSection.type);
const newSection: Section = { }
type: customSection.type,
content: customSection.content
};
// Insert at specific position if provided, otherwise add to end this.hasUnsavedChanges = true;
if (this.currentSectionIndex >= 0) { this.closeAddSectionOffcanvas();
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
// Edit section - modify this function // Edit section - modify this function
editSection(section: Section, event: Event): void { editSection(section: Section, event: Event): void {
event.stopPropagation(); event.stopPropagation();
// Store reference to the original section // Store reference to the original section
this.selectedSection = section; // Don't clone, use direct reference this.selectedSection = section; // Don't clone, use direct reference
this.currentParentPage = this.selectedPage; this.currentParentPage = this.selectedPage;
this.editMode = 'section'; this.editMode = 'section';
this.showEditSectionOffcanvas = true; this.showEditSectionOffcanvas = true;
// Listen for clicks outside offcanvas // Listen for clicks outside offcanvas
setTimeout(() => { setTimeout(() => {
document.addEventListener('click', this.handleOutsideClick); document.addEventListener('click', this.handleOutsideClick);
}, 10); }, 10);
} }
// Cancel edit // Cancel edit
cancelEdit(): void { cancelEdit(): void {
@ -860,112 +874,112 @@ addCustomSectionFromList(customSection: any): void {
// Handle page drag start // Handle page drag start
onPageDragStart(page: Page, event: DragEvent): void { onPageDragStart(page: Page, event: DragEvent): void {
this.draggedPage = page; this.draggedPage = page;
if (event.dataTransfer) { if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/html', page.name); 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 { // Add visual feedback
event.preventDefault(); setTimeout(() => {
event.stopPropagation(); const draggedElement = event.target as HTMLElement;
if (draggedElement) {
if (event.dataTransfer) { draggedElement.classList.add('dragging');
event.dataTransfer.dropEffect = 'move'; }
}, 0);
} }
// Add visual feedback onPageDragOver(event: DragEvent): void {
const dropZone = event.currentTarget as HTMLElement; event.preventDefault();
if (dropZone) { event.stopPropagation();
dropZone.classList.add('drag-over');
}
}
onPageDragLeave(event: DragEvent): void { if (event.dataTransfer) {
const dropZone = event.currentTarget as HTMLElement; event.dataTransfer.dropEffect = 'move';
if (dropZone) { }
dropZone.classList.remove('drag-over');
// 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 // Handle page drag end
onPageDragEnd(event: DragEvent): void { onPageDragEnd(event: DragEvent): void {
// Remove visual feedback // Remove visual feedback
const draggedElement = event.target as HTMLElement; const draggedElement = event.target as HTMLElement;
if (draggedElement) { if (draggedElement) {
draggedElement.classList.remove('dragging'); 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;
} }
// 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 // Handle page drop
onPageDrop(targetPage: Page, event: DragEvent): void { onPageDrop(targetPage: Page, event: DragEvent): void {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Remove visual feedback // Remove visual feedback
const dropZone = event.currentTarget as HTMLElement; const dropZone = event.currentTarget as HTMLElement;
if (dropZone) { if (dropZone) {
dropZone.classList.remove('drag-over'); 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);
} }
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}"`);
} }
// Add to new parent private isDescendant(possibleDescendant: Page, ancestor: Page): boolean {
if (!targetPage.children) { if (!ancestor.children) return false;
targetPage.children = [];
for (const child of ancestor.children) {
if (child === possibleDescendant) return true;
if (this.isDescendant(possibleDescendant, child)) return true;
}
return false;
} }
// 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 // Handle section drag drop
onSectionDrop(event: CdkDragDrop<Section[]>): void { onSectionDrop(event: CdkDragDrop<Section[]>): void {
if (event.previousContainer === event.container) { if (event.previousContainer === event.container) {
@ -999,26 +1013,26 @@ private isDescendant(possibleDescendant: Page, ancestor: Page): boolean {
} }
showAddSectionForEmptyPage(page: Page, event: Event): void { showAddSectionForEmptyPage(page: Page, event: Event): void {
event.stopPropagation(); event.stopPropagation();
this.openAddSectionOffcanvas(page, -1, event); this.openAddSectionOffcanvas(page, -1, event);
} }
// Add these helper methods to your component for proper connector positioning // Add these helper methods to your component for proper connector positioning
getHorizontalConnectorLeft(childrenCount: number): number { getHorizontalConnectorLeft(childrenCount: number): number {
// Calculate left position for horizontal connector // Calculate left position for horizontal connector
const nodeWidth = 250; // Approximate node width const nodeWidth = 250; // Approximate node width
const gap = 40; // Gap between nodes const gap = 40; // Gap between nodes
const totalWidth = (childrenCount * nodeWidth) + ((childrenCount - 1) * gap); const totalWidth = (childrenCount * nodeWidth) + ((childrenCount - 1) * gap);
return (totalWidth / 2) - (totalWidth / childrenCount / 2); return (totalWidth / 2) - (totalWidth / childrenCount / 2);
} }
getHorizontalConnectorWidth(childrenCount: number): number { getHorizontalConnectorWidth(childrenCount: number): number {
// Calculate width of horizontal connector // Calculate width of horizontal connector
const nodeWidth = 250; // Approximate node width const nodeWidth = 250; // Approximate node width
const gap = 40; // Gap between nodes const gap = 40; // Gap between nodes
return (childrenCount - 1) * (nodeWidth + gap); return (childrenCount - 1) * (nodeWidth + gap);
} }
// Save tree data // Save tree data
saveTreeData(silent: boolean = false): void { saveTreeData(silent: boolean = false): void {

View File

@ -451,7 +451,8 @@
(cdkDropListDropped)="onSectionDrop($event, getCurrentPageName())"> (cdkDropListDropped)="onSectionDrop($event, getCurrentPageName())">
<div *ngFor="let sectionKey of getPageSections(getCurrentPageName())" cdkDrag [cdkDragData]="sectionKey" <div *ngFor="let sectionKey of getPageSections(getCurrentPageName())" cdkDrag [cdkDragData]="sectionKey"
class="section-container" [class.editing]="isCurrentSectionEditing(sectionKey)"> class="section-container" [class.editing]="isCurrentSectionEditing(sectionKey)"
(click)="openCssEditor(getCurrentPageName(), sectionKey)">
<!-- Drag Handle --> <!-- Drag Handle -->
<div class="drag-handle" cdkDragHandle> <div class="drag-handle" cdkDragHandle>
@ -483,6 +484,27 @@
</div> </div>
</div> </div>
<!-- CSS Editor Modal -->
<div *ngIf="showCssEditorModal" class="css-editor-modal">
<h3>Edit CSS for Section: {{ selectedSection?.section }}</h3>
<div *ngFor="let className of objectKeys(classCssMap)">
<h4>.{{ className }}</h4>
<div *ngFor="let prop of objectKeys(classCssMap[className])">
<label>{{ prop }}</label>
<input [(ngModel)]="classCssMap[className][prop]" />
<button (click)="removeCssProp(className, prop)">🗑️</button>
</div>
<button (click)="addCssProp(className)"> Add Property</button>
</div>
<button (click)="saveClassCss()">💾 Save</button>
<button (click)="closeCssEditor()">Cancel</button>
</div>
<!-- Empty State for No Pages --> <!-- Empty State for No Pages -->
<div *ngIf="pageRenderOrder.length === 0" class="empty-pages-modern"> <div *ngIf="pageRenderOrder.length === 0" class="empty-pages-modern">
<div class="empty-state-modern"> <div class="empty-state-modern">

View File

@ -1920,6 +1920,73 @@ $shadow-heavy: 0 8px 25px rgba(0, 0, 0, 0.15);
margin-left: 10px; margin-left: 10px;
} }
.css-editor-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
}
// css editor
.css-editor-modal-overlay {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.css-editor-modal {
background: white;
padding: 20px;
width: 600px;
max-height: 80vh;
overflow-y: auto;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
z-index: 1001;
}
.css-editor-modal input {
width: 100%;
padding: 6px 8px;
margin-bottom: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
.css-editor-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.css-editor-header .close-btn {
background: transparent;
border: none;
font-size: 18px;
cursor: pointer;
}
.css-editor-footer {
margin-top: 20px;
display: flex;
gap: 10px;
justify-content: flex-end;
}
// chow code css end // chow code css end
} }

View File

@ -3741,9 +3741,105 @@ async deployAllPages() {
} }
// css editor
showCssEditorModal = false;
cssEditorStyles: any = {};
selectedSection: { page: string; section: string } | null = null;
closeCssEditor(): void {
this.showCssEditorModal = false;
this.selectedSection = null;
this.cssEditorStyles = {};
}
// new
openCssEditor(page: string, section: string): void {
const rawHtml = this.sectionHtmls[page]?.[section]?.toString() || '';
// ✅ Step 1: Parse all class names recursively
const tempDiv = document.createElement('div');
tempDiv.innerHTML = rawHtml;
const classSet = new Set<string>();
// Traverse all elements recursively and extract classList
const elements = tempDiv.querySelectorAll('*');
elements.forEach(el => {
const classList = Array.from(el.classList);
classList.forEach(cls => classSet.add(cls));
});
const allClasses = Array.from(classSet);
// ✅ Step 2: Extract CSS rules for all these classes
const cssMap: Record<string, Record<string, string>> = {};
allClasses.forEach(cls => {
const rules = this.extractCssForClass(cls, this.finalCssBundle); // use your full css bundle
cssMap[cls] = rules;
});
// ✅ Step 3: Store in modal
this.selectedSection = { page, section };
this.classCssMap = cssMap;
this.showCssEditorModal = true;
}
extractClassesFromHtml(html: string): string[] {
const match = html.match(/class=["']([^"']+)["']/);
if (!match) return [];
return match[1].split(/\s+/).filter(cls => cls.trim());
}
extractCssForClass(className: string, cssText: string): Record<string, string> {
const result: Record<string, string> = {};
const regex = new RegExp(`\\.${className}\\s*{([^}]*)}`, 'g');
let match;
while ((match = regex.exec(cssText)) !== null) {
const props = match[1].split(';').map(p => p.trim()).filter(Boolean);
props.forEach(prop => {
const [key, val] = prop.split(':');
if (key && val) result[key.trim()] = val.trim();
});
}
return result;
}
classCssMap: Record<string, Record<string, string>> = {};
saveClassCss(): void {
let cssBundle = '';
for (const [className, props] of Object.entries(this.classCssMap)) {
const block = Object.entries(props)
.map(([k, v]) => ` ${k}: ${v};`)
.join('\n');
cssBundle += `.${className} {\n${block}\n}\n\n`;
}
const { page, section } = this.selectedSection!;
this.allPageCss[page] = this.allPageCss[page] || {};
this.allPageCss[page][section] = cssBundle;
this.updateFullPageHtml(page);
this.toastr.success(`CSS updated for ${section}`);
this.closeCssEditor();
}
addCssProp(className: string): void {
this.classCssMap[className]['new-property'] = '';
}
removeCssProp(className: string, prop: string): void {
delete this.classCssMap[className][prop];
}
} }