tree
This commit is contained in:
parent
32c8b6acd4
commit
0b8f762a63
@ -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,
|
||||
|
||||
Binary file not shown.
@ -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>
|
||||
@ -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 {
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user