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

@ -52,13 +52,13 @@ export class EditstepperComponent implements OnInit {
public entryForm: FormGroup;
submitted = false;
rowSelected: any = {};
modalcomplete = false;
constructor(private mainService: Visa_applicationservice,
constructor(
private mainService: Visa_applicationservice,
private _fb: FormBuilder,
private router: Router,
private route: ActivatedRoute,

View File

@ -73,7 +73,7 @@
</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"
@ -81,7 +81,7 @@
<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"
@ -89,10 +89,10 @@
<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;">
<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>
@ -115,31 +115,38 @@
</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>
<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 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="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 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>
<!-- 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 class="modal" [class.show]="showAddChildModal">
<div class="modal-backdrop" *ngIf="showAddChildModal" (click)="closeAddChildModal()"></div>
@ -167,98 +174,96 @@
<div class="spinner"></div>
<div class="loading-text">Processing...</div>
</div>
<!-- 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>
<!-- 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="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
<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>
<!-- 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>
</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()">
<div class="add-child-hover-btn" (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>
<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>
<!-- 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>
</ng-template>

View File

@ -9,6 +9,8 @@ import { ToastrService } from 'ngx-toastr';
interface Section {
type: string;
content: string;
keywords?: string; // <-- Add this line
}
interface Page {
@ -140,17 +142,17 @@ 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.'
}
];
{
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(
@ -538,6 +540,18 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// If parsing fails, use the raw 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
@ -709,19 +723,19 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// Save section changes
saveSection(): void {
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;
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 {
@ -751,79 +765,79 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// Similar update for addCustomSection
addCustomSection(): void {
if (this.currentParentPage && this.customSectionType) {
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({
if (this.currentParentPage && this.customSectionType) {
const newSection: Section = {
type: this.customSectionType,
description: 'Custom section',
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.currentParentPage) {
const newSection: Section = {
type: customSection.type,
content: customSection.content
};
if (!this.availableSectionTypes.includes(customSection.type)) {
this.availableSectionTypes.push(customSection.type);
}
// 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.closeAddSectionOffcanvas();
}
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();
// 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;
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);
}
// Listen for clicks outside offcanvas
setTimeout(() => {
document.addEventListener('click', this.handleOutsideClick);
}, 10);
}
// Cancel edit
cancelEdit(): void {
@ -860,112 +874,112 @@ addCustomSectionFromList(customSection: any): void {
// Handle page drag start
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');
this.draggedPage = page;
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/html', page.name);
}
}, 0);
}
onPageDragOver(event: DragEvent): void {
event.preventDefault();
event.stopPropagation();
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move';
// Add visual feedback
setTimeout(() => {
const draggedElement = event.target as HTMLElement;
if (draggedElement) {
draggedElement.classList.add('dragging');
}
}, 0);
}
// 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');
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(event: DragEvent): void {
// Remove visual feedback
const draggedElement = event.target as HTMLElement;
if (draggedElement) {
draggedElement.classList.remove('dragging');
// 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;
}
// 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, 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}"`);
}
onPageDrop(targetPage: Page, event: DragEvent): void {
event.preventDefault();
event.stopPropagation();
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;
// 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;
}
return false;
}
// Handle section drag drop
onSectionDrop(event: CdkDragDrop<Section[]>): void {
if (event.previousContainer === event.container) {
@ -999,26 +1013,26 @@ private isDescendant(possibleDescendant: Page, ancestor: Page): boolean {
}
showAddSectionForEmptyPage(page: Page, event: Event): void {
event.stopPropagation();
this.openAddSectionOffcanvas(page, -1, event);
}
event.stopPropagation();
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 {
// 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);
}
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);
}
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 {

View File

@ -451,7 +451,8 @@
(cdkDropListDropped)="onSectionDrop($event, getCurrentPageName())">
<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 -->
<div class="drag-handle" cdkDragHandle>
@ -483,6 +484,27 @@
</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 -->
<div *ngIf="pageRenderOrder.length === 0" class="empty-pages-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;
}
.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
}

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];
}
}