wireframe

This commit is contained in:
string 2025-05-28 16:30:54 +05:30
parent dce9fb86d9
commit 62dffed702
7 changed files with 1586 additions and 551 deletions

View File

@ -52,6 +52,7 @@ export class EditstepperComponent implements OnInit {
public entryForm: FormGroup;
submitted = false;
rowSelected: any = {};
modalcomplete = false;

View File

@ -135,203 +135,282 @@
<div class="wireframe-controls-sidebar"
[class.collapsed]="sidebarCollapsed"
[class.mobile-open]="sidebarOpen"
[class.visible]="sidebarOpen || !isMobile">
[class.visible]="sidebarOpen || !isMobile">
<!-- Sidebar Header -->
<div class="sidebar-header">
<div (click)="toggleSidebar()">
<i class="fas" [ngClass]="sidebarCollapsed ? 'fa-chevron-left' : 'fa-chevron-right'"></i>
<!-- Loading Overlay -->
<!-- <div *ngIf="isLoading" class="loading-overlay">
<mat-spinner diameter="40"></mat-spinner>
<div class="loading-text">Generating Pages...</div>
</div> -->
<!-- Updated Close Button -->
<div class="sidebar-header">
<button
(click)="toggleSidebar()"
class="close-btn"
style="
background: none;
border: none;
color: #060606;
cursor: pointer;
padding: 8px;
border-radius: 50%;
transition: all 0.2s ease-in-out;
font-size: 16px;
"
>
<i class="fas" [ngClass]="sidebarCollapsed ? 'fa-chevron-left' : 'fa-chevron-right'"></i>
</button>
<h2>Wireframe Controls</h2>
</div>
<!-- Main Actions Section -->
<div class="sidebar-content">
<div class="control-section search-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-search" style="margin-right: 6px; color: #17a2b8;"></i>
Search Sections
</h3>
<div class="search-container">
<input
type="text"
class="search-input"
[(ngModel)]="searchTerm"
(input)="onSearchChange()"
placeholder="Search sections..."
> </div>
</div>
<!-- Global Sections -->
<div class="control-section fade-in-left" [@fadeIn]>
<h3>
<i class="fas fa-globe" style="margin-right: 6px; color: #6f42c1;"></i>
Global Sections
</h3>
<div class="wf-section-list" *ngIf="getFilteredSections(globalSections).length">
<div class="wf-section-item"
*ngFor="let section of getFilteredSections(globalSections)"
(click)="addSectionFromTemplate(section)">
<div class="section-icon">
<i [class]="section.icon"></i>
</div>
<div class="section-info">
<div class="section-name">{{ section.type }}</div>
<div class="section-desc">{{ section.description }}</div>
</div>
</div>
</div>
</div>
<h2>
<i class="fas fa-cogs" style="margin-right: 8px; font-size: 18px;"></i>
Wireframe Controls
</h2>
<!-- Standard Sections -->
<div class="control-section fade-in-left" [@fadeIn]>
<h3>
<i class="fas fa-th-large" style="margin-right: 6px; color: #fd7e14;"></i>
Standard Sections
</h3>
<div class="wf-section-list" *ngIf="getFilteredSections(standardSections).length">
<div class="wf-section-item"
*ngFor="let section of getFilteredSections(standardSections)"
(click)="addSectionFromTemplate(section)">
<div class="section-icon">
<i [class]="section.icon"></i>
</div>
<div class="section-info">
<div class="section-name">{{ section.type }}</div>
<div class="section-desc">{{ section.description }}</div>
</div>
</div>
</div>
</div>
<!-- Main Actions Section -->
<div class="sidebar-content">
<div class="control-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-play-circle" style="margin-right: 6px; color: #28a745;"></i>
Generate Wireframe
</h3>
<button class="action-btn primary" (click)="buildWireframe(37688)" [@buttonHover]>
<i class="fas fa-hammer"></i>
Build Wireframe
</button>
<button class="action-btn primary" (click)="regenerateAllModifiedSections()">
<i class="fas fa-sync-alt"></i>
Regenerate Modified
</button>
<button class="action-btn secondary" (click)="createHtmlFiles()">
<i class="fas fa-save"></i>
Save All HTML Files
</button>
<button class="action-btn secondary" (click)="recreateWireframe()">
<i class="fas fa-redo"></i>
Recreate Wireframe
</button>
<button class="action-btn secondary">
<i class="fa-solid fa-link"></i>
{{ deployedUrl ? 'DEPLOYED URL: ' + deployedUrl : 'NO URL YET' }}
</button>
<!-- Custom Sections -->
<div class="control-section fade-in-left" [@fadeIn]>
<h3>
<i class="fas fa-puzzle-piece" style="margin-right: 6px; color: #e83e8c;"></i>
Custom Sections
</h3>
<div class="wf-section-list" *ngIf="getFilteredSections(customSections).length">
<div class="wf-section-item"
*ngFor="let section of getFilteredSections(customSections)"
(click)="addSectionFromTemplate(section)">
<div class="section-icon">
<i [class]="section.icon"></i>
</div>
<div class="section-info">
<div class="section-name">{{ section.type }}</div>
<div class="section-desc">{{ section.description }}</div>
</div>
<button class="delete-custom-btn" (click)="deleteCustomSection($event, section)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<!-- Navbar Controls Section -->
<div class="control-section fade-in-left" [@fadeIn]>
<h3>
<i class="fas fa-bars" style="margin-right: 6px; color: #17a2b8;"></i>
Navbar Editor
</h3>
<!-- Add Custom Section -->
<div class="add-custom-section">
<button class="action-btn secondary full-width" (click)="showAddCustomSectionModal()">
<i class="fas fa-plus"></i>
Add Custom Section
</button>
</div>
</div>
<!-- Additional Features Section -->
<div class="control-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-tools" style="margin-right: 6px; color: #20c997;"></i>
Additional Features
</h3>
<button class="action-btn primary" (click)="createHtmlFiles()">
<i class="fas fa-save"></i>
Save All HTML Files
</button>
<button class="action-btn secondary" (click)="recreateWireframe()">
<i class="fas fa-redo"></i>
Regenerate Wireframe
</button>
<button class="action-btn primary" (click)="showNavbarEditor()">
<i class="fas fa-bars"></i>
Navbar Editor
</button>
<button class="action-btn secondary" (click)="buildWireframe(37688)" [@buttonHover]>
<i class="fas fa-hammer"></i>
Build Wireframe
</button>
<button class="action-btn primary" (click)="regenerateAllModifiedSections()">
<i class="fas fa-sync-alt"></i>
Regenerate Modified
</button>
<button class="action-btn secondary">
<i class="fas fa-external-link-alt"></i>
{{ deployedUrl ? 'DEPLOYED URL: ' + deployedUrl : 'NO URL YET' }}
</button>
</div>
<!-- Commented out sections as requested -->
<!--
<div class="control-section bounce-in" [@fadeIn]>
<h3>
<i class="fas fa-palette" style="margin-right: 6px; color: #fd7e14;"></i>
Style Controls
</h3>
<div class="style-grid">
<div class="form-group">
<label for="navbarSelect">Select Navbar Page:</label>
<select id="navbarSelect" class="form-control" [(ngModel)]="selectedNavbarPage"
(change)="selectedNavbarPage && selectNavbar(selectedNavbarPage)"
[@focusBorder]>
<option value="">Choose a page...</option>
<option *ngFor="let page of pageRenderOrder" [value]="page">
<i class="fas fa-file-alt"></i> {{ page }}
</option>
<label>Font Size:</label>
<select class="form-control" [(ngModel)]="liveStyles.fontSize">
<option value="">Default</option>
<option *ngFor="let size of fontSizes" [value]="size">{{ size }}</option>
</select>
</div>
<div class="form-group">
<label>Font Weight:</label>
<select class="form-control" [(ngModel)]="liveStyles.fontWeight">
<option value="">Default</option>
<option *ngFor="let weight of fontWeights" [value]="weight">{{ weight }}</option>
</select>
</div>
</div>
<!-- Style Controls Section -->
<div class="control-section bounce-in" [@fadeIn]>
<h3>
<i class="fas fa-palette" style="margin-right: 6px; color: #fd7e14;"></i>
Style Controls
</h3>
<div class="color-controls">
<div class="color-input-group">
<span class="color-label">
<i class="fas fa-font" style="margin-right: 4px;"></i>
Text Color
</span>
<input type="color" [(ngModel)]="liveStyles.color" class="color-picker" />
</div>
<div class="style-grid">
<div class="form-group">
<label>Font Size:</label>
<select class="form-control" [(ngModel)]="liveStyles.fontSize">
<option value="">Default</option>
<option *ngFor="let size of fontSizes" [value]="size">{{ size }}</option>
</select>
</div>
<div class="form-group">
<label>Font Weight:</label>
<select class="form-control" [(ngModel)]="liveStyles.fontWeight">
<option value="">Default</option>
<option *ngFor="let weight of fontWeights" [value]="weight">{{ weight }}</option>
</select>
</div>
<div class="color-input-group">
<span class="color-label">
<i class="fas fa-fill-drip" style="margin-right: 4px;"></i>
Background
</span>
<input type="color" [(ngModel)]="liveStyles.background" class="color-picker" />
</div>
<div class="style-grid">
<div class="form-group">
<label>Text Align:</label>
<select class="form-control" [(ngModel)]="liveStyles.textAlign">
<option value="">Default</option>
<option value="left">Left</option>
<option value="center">Center</option>
<option value="right">Right</option>
<option value="justify">Justify</option>
</select>
</div>
<div class="form-group">
<button class="action-btn secondary small" (click)="verifyCssInSections()">
<i class="fas fa-search"></i>
Verify CSS
</button>
</div>
</div>
<div class="color-controls">
<div class="color-input-group">
<span class="color-label">
<i class="fas fa-font" style="margin-right: 4px;"></i>
Text Color
</span>
<input type="color" [(ngModel)]="liveStyles.color" class="color-picker" />
</div>
<div class="color-input-group">
<span class="color-label">
<i class="fas fa-fill-drip" style="margin-right: 4px;"></i>
Background
</span>
<input type="color" [(ngModel)]="liveStyles.background" class="color-picker" />
</div>
</div>
<button class="action-btn primary full-width" (click)="applyStyleToSelection()">
<i class="fas fa-paint-brush"></i>
Apply Style to Selection
</button>
</div>
<!-- Page Management Section -->
<div class="control-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-file-alt" style="margin-right: 6px; color: #dc3545;"></i>
Page Management
</h3>
<button class="action-btn secondary" (click)="addNewPage()">
<i class="fas fa-plus"></i>
Add New Page
</button>
<button class="action-btn secondary" (click)="duplicateCurrentPage()">
<i class="fas fa-copy"></i>
Duplicate Current Page
</button>
<button class="action-btn secondary" (click)="deleteCurrentPage()"
[disabled]="pageRenderOrder.length <= 1">
<i class="fas fa-trash"></i>
Delete Current Page
</button>
</div>
<!-- Additional Tools Section -->
<div class="control-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-tools" style="margin-right: 6px; color: #6f42c1;"></i>
Additional Tools
</h3>
<button class="action-btn secondary" (click)="exportProject()">
<i class="fas fa-download"></i>
Export Project
</button>
<button class="action-btn secondary" (click)="importProject()">
<i class="fas fa-upload"></i>
Import Project
</button>
<button class="action-btn secondary" (click)="previewMode()">
<i class="fas fa-eye"></i>
Preview Mode
</button>
<button class="action-btn secondary" (click)="addNewSectionToCurrentPage()">
<i class="fas fa-plus-square"></i>
Add Section
</button>
</div>
<button class="action-btn primary full-width" (click)="applyStyleToSelection()">
<i class="fas fa-paint-brush"></i>
Apply Style to Selection
</button>
</div>
-->
<!--
<div class="control-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-file-alt" style="margin-right: 6px; color: #dc3545;"></i>
Page Management
</h3>
<button class="action-btn secondary" (click)="addNewPage()">
<i class="fas fa-plus"></i>
Add New Page
</button>
<button class="action-btn secondary" (click)="duplicateCurrentPage()">
<i class="fas fa-copy"></i>
Duplicate Current Page
</button>
<button class="action-btn secondary" (click)="deleteCurrentPage()"
[disabled]="pageRenderOrder.length <= 1">
<i class="fas fa-trash"></i>
Delete Current Page
</button>
</div>
-->
<!--
<div class="control-section fade-in-up" [@fadeIn]>
<h3>
<i class="fas fa-tools" style="margin-right: 6px; color: #6f42c1;"></i>
Additional Tools
</h3>
<button class="action-btn secondary" (click)="exportProject()">
<i class="fas fa-download"></i>
Export Project
</button>
<button class="action-btn secondary" (click)="importProject()">
<i class="fas fa-upload"></i>
Import Project
</button>
<button class="action-btn secondary" (click)="previewMode()">
<i class="fas fa-eye"></i>
Preview Mode
</button>
<button class="action-btn secondary" (click)="addNewSectionToCurrentPage()">
<i class="fas fa-plus-square"></i>
Add Section
</button>
</div>
-->
</div>
</div>
<!-- Main Canvas Area -->
<div class="canvas-main-area" [class.sidebar-collapsed]="sidebarCollapsed">
@ -357,61 +436,63 @@
(wheel)="onWheel($event)">
<!-- Loading Overlay -->
<div *ngIf="isLoading" class="loading-overlay">
<div class="spinner"></div>
<div class="loading-text">Loading wireframe...</div>
</div>
<!-- Updated Loading Overlay -->
<div *ngIf="isLoading" class="loading-overlay" [@fadeIn]>
<div class="loading-container">
<mat-spinner diameter="50" color="accent"></mat-spinner>
<div class="loading-text">Building Interface...</div>
<div class="loading-subtext">Please wait while we prepare your components</div>
<div class="progress-bar">
<div class="progress-fill" [style.width.%]="loadProgress"></div>
</div>
</div>
</div>
<!-- Full Page Display -->
<div *ngIf="!isLoading && getCurrentPage()"
<div *ngIf="!isLoading "
class="page-display"
[style.transform]="getPageTransform()">
<!-- Connected Sections Container -->
<div class="connected-sections"
cdkDropList
cdkDropListOrientation="vertical"
[cdkDropListData]="getCurrentPageSections()"
(cdkDropListDropped)="onSectionDrop($event, getCurrentPageName())">
<!-- Update your section template -->
<div class="connected-sections"
cdkDropList
[cdkDropListData]="getCurrentPageSections()"
[cdkDropListConnectedTo]="allConnectedLists"
(cdkDropListDropped)="onSectionDrop($event)">
<div *ngFor="let sectionKey of getCurrentPageSections()"
cdkDrag
[cdkDragData]="sectionKey"
class="section-wrapper"
#dragItem>
<!-- Drag Handle -->
<div class="drag-handle" cdkDragHandle>
<i class="fas fa-grip-vertical"></i>
<div style="height: 16px !important;"></div>
</div>
<!-- Individual Connected Sections -->
<!-- Change this in your *ngFor -->
<!-- Update the section wrapper div -->
<div *ngFor="let sectionKey of getCurrentPageSections(); let i = index"
class="section-wrapper"
[class.editing]="isCurrentSectionEditing(sectionKey)"
cdkDrag
[cdkDragData]="sectionKey"
(mouseenter)="hoveredSection = sectionKey"
(mouseleave)="hoveredSection = null"
(dblclick)="toggleSectionEditing(getCurrentPageName(), sectionKey, $event)">
<!-- Section Content (Full Width, No Gaps) -->
<div class="section-content"
[innerHTML]="getSectionHtml(getCurrentPageName(), sectionKey)"
[attr.contenteditable]="isCurrentSectionEditing(sectionKey)"
(blur)="handleDirectContentEdit($event, getCurrentPageName(), sectionKey)">
</div>
<!-- Section Overlay for Hover/Edit State -->
<div class="section-overlay"
[class.visible]="hoveredSection === sectionKey || isCurrentSectionEditing(sectionKey)">
<div class="section-label">
<i class="fas fa-puzzle-piece" style="margin-right: 4px;"></i>
{{ sectionKey }}
<button *ngIf="isCurrentSectionEditing(sectionKey)"
class="btn btn-sm btn-success ms-2"
(click)="saveSectionEdit(getCurrentPageName(), sectionKey)">
<i class="fas fa-check"></i>
</button>
</div>
</div>
<!-- Section Content -->
<div class="section-content"
[innerHTML]="getSectionHtml(getCurrentPageName(), sectionKey)"
[attr.contenteditable]="isCurrentSectionEditing(sectionKey)"
(blur)="handleDirectContentEdit($event, getCurrentPageName(), sectionKey)">
</div>
<!-- Section Overlay -->
<div class="section-overlay" *ngIf="!isCurrentSectionEditing(sectionKey)">
<div class="section-label">{{sectionKey}}</div>
</div>
</div>
<!-- Drag Handle -->
<div class="drag-handle" cdkDragHandle style="display: none;">
<!-- <div class="drag-handle" cdkDragHandle style="display: none;">
<i class="fa fa-grip-lines"></i>
</div>
</div>
</div> -->
<!-- Empty State for Sections -->
<div *ngIf="getCurrentPageSections().length === 0" class="empty-sections"
@ -493,124 +574,146 @@
</button>
<!-- Navbar Editor Modal -->
<div class="modal-overlay" *ngIf="selectedNavbarPage" [@fadeIn] (click)="closeNavbarModal()">
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>
<div class="modal-header">
<h3>
<i class="fas fa-edit" style="margin-right: 8px; color: #17a2b8;"></i>
Edit Navbar Links - {{ selectedNavbarPage }}
</h3>
<button class="close-btn" (click)="closeNavbarModal()">
<i class="fas fa-times"></i>
</button>
<!-- Updated Navbar Editor Modal -->
<div class="modal-overlay" *ngIf="showNavbarEditorModal" [@fadeIn] (click)="closeNavbarEditorModal()">
<div class="modal-content navbar-editor-modal" (click)="$event.stopPropagation()" [@slideIn]>
<div class="modal-header">
<h3>
<i class="fas fa-bars" style="margin-right: 8px; color: #17a2b8;"></i>
Navbar Editor
</h3>
<button class="close-btn" (click)="closeNavbarEditorModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<!-- Page Selection -->
<div class="form-group">
<label for="navbarPageSelect">Select Page:</label>
<select id="navbarPageSelect" class="form-control" [(ngModel)]="selectedNavbarPage"
(change)="onNavbarPageChange()"
[@focusBorder]>
<option value="">Choose a page...</option>
<option *ngFor="let page of pageRenderOrder" [value]="page">
<i class="fas fa-file-alt"></i> {{ page }}
</option>
</select>
</div>
<div class="modal-body">
<div class="navbar-link-item" *ngFor="let link of navbarLinks[selectedNavbarPage]; let i = index" [@fadeIn]
style="display: flex; gap: 10px; align-items: center; margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<div class="form-group" style="flex: 1; margin-bottom: 0;">
<input type="text" [(ngModel)]="link.label" placeholder="Link Label" class="form-control">
<!-- Navbar Links -->
<div *ngIf="selectedNavbarPage" class="navbar-links-section">
<h4>Navbar Links</h4>
<div class="navbar-link-item" *ngFor="let link of currentNavbarLinks; let i = index" [@fadeIn]>
<div class="link-inputs">
<div class="form-group">
<label>Link Text:</label>
<input type="text" [(ngModel)]="link.label" placeholder="Link Label" class="form-control">
</div>
<div class="form-group">
<label>Link URL:</label>
<select [(ngModel)]="link.href" class="form-control">
<option value="">Select page...</option>
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
<option value="custom">Custom URL...</option>
</select>
<input *ngIf="link.href === 'custom'" type="text" [(ngModel)]="link.customUrl"
placeholder="Enter custom URL" class="form-control mt-2">
</div>
<button class="remove-btn" (click)="removeNavbarLink(i)" title="Remove Link">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="form-group" style="flex: 1; margin-bottom: 0;">
<select [(ngModel)]="link.href" class="form-control">
<option value="">Select page...</option>
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
</select>
</div>
<button class="remove-btn" (click)="removeLink(selectedNavbarPage, i)" title="Remove Link"
style="background: #dc3545; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">
<i class="fas fa-trash"></i>
</button>
</div>
<button class="action-btn secondary full-width add-link-btn" (click)="addLink(selectedNavbarPage)">
<button class="action-btn secondary full-width" (click)="addNavbarLink()">
<i class="fas fa-plus"></i>
Add New Link
</button>
</div>
</div>
<div class="modal-footer">
<button class="action-btn secondary" (click)="closeNavbarModal()">
<i class="fas fa-times" style="margin-right: 5px;"></i>
Cancel
</button>
<button class="action-btn primary" (click)="applyNavbarChanges(selectedNavbarPage)">
<i class="fas fa-check" style="margin-right: 5px;"></i>
Save Changes
</button>
</div>
<div class="modal-footer">
<button class="action-btn secondary" (click)="closeNavbarEditorModal()">
<i class="fas fa-times" style="margin-right: 5px;"></i>
Cancel
</button>
<button class="action-btn primary" (click)="saveNavbarChanges()">
<i class="fas fa-check" style="margin-right: 5px;"></i>
Save Changes
</button>
</div>
</div>
</div>
<!-- Modal for Adding New Section -->
<div class="modal-overlay" *ngIf="showNewSectionModal" [@fadeIn] (click)="closeNewSectionModal()">
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>
<div class="modal-header">
<h3>
<i class="fas fa-plus-circle" style="margin-right: 8px; color: #28a745;"></i>
Add New Section to {{ newSectionPage }}
</h3>
<button class="close-btn" (click)="closeNewSectionModal()">
<i class="fas fa-times"></i>
</button>
<!-- Add Custom Section Modal -->
<div class="modal-overlay" *ngIf="showAddCustomModal" [@fadeIn] (click)="closeAddCustomModal()">
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>
<div class="modal-header">
<h3>
<i class="fas fa-plus-circle" style="margin-right: 8px; color: #28a745;"></i>
Add Custom Section
</h3>
<button class="close-btn" (click)="closeAddCustomModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="customSectionName">
<i class="fas fa-tag" style="margin-right: 4px;"></i>
Section Name:
</label>
<input id="customSectionName"
type="text"
[(ngModel)]="newCustomSectionName"
placeholder="Enter section name (e.g., Custom Header, Special CTA)"
class="form-control">
</div>
<div class="modal-body">
<div class="form-group">
<label for="sectionName">
<i class="fas fa-tag" style="margin-right: 4px;"></i>
Section Name:
</label>
<input id="sectionName"
type="text"
[(ngModel)]="newSectionName"
placeholder="Enter section name (e.g., Hero, About, Contact)"
class="form-control">
</div>
<div class="form-group">
<label for="sectionType">
<i class="fas fa-layer-group" style="margin-right: 4px;"></i>
Section Type:
</label>
<select id="sectionType" [(ngModel)]="newSectionType" class="form-control">
<option value="">Select a type...</option>
<option *ngFor="let type of sectionTypes" [value]="type.value">
{{ type.label }}
</option>
</select>
</div>
<div class="form-group">
<label for="sectionDescription">
<i class="fas fa-align-left" style="margin-right: 4px;"></i>
Section Description:
</label>
<textarea id="sectionDescription"
[(ngModel)]="newSectionDescription"
rows="4"
placeholder="Describe the content and purpose of this section..."
class="form-control"></textarea>
</div>
<div class="form-group">
<label for="customSectionDesc">
<i class="fas fa-align-left" style="margin-right: 4px;"></i>
Section Description:
</label>
<textarea id="customSectionDesc"
[(ngModel)]="newCustomSectionDesc"
rows="4"
placeholder="Describe the content and purpose of this custom section..."
class="form-control"></textarea>
</div>
<div class="modal-footer">
<button class="action-btn secondary" (click)="closeNewSectionModal()">
<i class="fas fa-times" style="margin-right: 5px;"></i>
Cancel
</button>
<button class="action-btn primary"
[disabled]="!newSectionName || !newSectionType"
(click)="confirmAddSection()">
<i class="fas fa-plus" style="margin-right: 5px;"></i>
Add Section
</button>
<div class="form-group">
<label for="customSectionIcon">
<i class="fas fa-icons" style="margin-right: 4px;"></i>
Icon Class:
</label>
<input id="customSectionIcon"
type="text"
[(ngModel)]="newCustomSectionIcon"
placeholder="e.g., fas fa-star, fas fa-rocket"
class="form-control">
</div>
</div>
</div>
<div class="modal-footer">
<button class="action-btn secondary" (click)="closeAddCustomModal()">
<i class="fas fa-times" style="margin-right: 5px;"></i>
Cancel
</button>
<button class="action-btn primary"
[disabled]="!newCustomSectionName || !newCustomSectionDesc"
(click)="confirmAddCustomSection()">
<i class="fas fa-plus" style="margin-right: 5px;"></i>
Add Custom Section
</button>
</div>
</div>
</div>
<!-- Modal for Adding New Page -->
<div class="modal-overlay" *ngIf="showNewPageModal" [@fadeIn] (click)="closeNewPageModal()">
<div class="modal-content" (click)="$event.stopPropagation()" [@slideIn]>

View File

@ -149,6 +149,8 @@ $shadow-heavy: 0 8px 25px rgba(0, 0, 0, 0.15);
top: 0 ;
z-index: 10 ;
backdrop-filter: blur(10px) ;
display: flex ;
flex-direction: row;
h2 {
margin: 0 ;
@ -374,6 +376,129 @@ $shadow-heavy: 0 8px 25px rgba(0, 0, 0, 0.15);
}
}
}
.wf-section-list {
max-height: 300px !important;
overflow-y: auto !important;
margin: 12px 0 !important;
border: 1px solid #e9ecef !important;
border-radius: 8px !important;
padding: 8px !important;
background: #ffffff !important;
.wf-section-item {
display: flex !important;
align-items: center !important;
padding: 12px !important;
margin-bottom: 8px !important;
border: 1px solid #e9ecef !important;
border-radius: 8px !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
position: relative !important;
background: #f8f9fa !important;
&:hover {
border-color: #007bff !important;
background-color: #ffffff !important;
transform: translateY(-2px) !important;
box-shadow: 0 3px 6px rgba(0,0,0,0.1) !important;
}
.section-icon {
width: 40px !important;
height: 40px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
background: #ffffff !important;
border-radius: 6px !important;
margin-right: 12px !important;
border: 1px solid #dee2e6 !important;
i {
font-size: 18px !important;
color: #007bff !important;
}
}
.section-info {
flex: 1 !important;
.section-name {
font-weight: 600 !important;
font-size: 14px !important;
color: #2c3e50 !important;
margin-bottom: 4px !important;
}
.section-desc {
font-size: 12px !important;
color: #7f8c8d !important;
line-height: 1.4 !important;
}
}
.delete-custom-btn {
position: absolute !important;
top: 50% !important;
right: 12px !important;
transform: translateY(-50%) !important;
width: 28px !important;
height: 28px !important;
border: none !important;
background: #dc3545 !important;
color: white !important;
border-radius: 50% !important;
display: none !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
transition: background 0.2s ease !important;
&:hover {
background: #c82333 !important;
}
}
&:hover .delete-custom-btn {
display: flex !important;
}
}
}
// Custom Section for adding new sections
.add-custom-section {
display: flex ;
align-items: center ;
gap: 12px ;
margin-top: 20px ;
padding: 12px ;
background: rgba(255, 255, 255, 0.9) ;
border-radius: 8px ;
box-shadow: $shadow-light ;
transition: $transition-smooth ;
&:hover {
transform: translateY(-2px) ;
box-shadow: $shadow-medium ;
}
.add-icon {
font-size: 24px ;
color: $primary-black ;
cursor: pointer ;
&:hover {
color: $secondary-black ;
}
}
.add-text {
font-size: 14px ;
color: $text-gray ;
font-weight: 600 ;
}
}
}
}
}
@ -412,6 +537,62 @@ $shadow-heavy: 0 8px 25px rgba(0, 0, 0, 0.15);
}
}
/* Enhanced Loading Styles */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.95);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(5px);
.loading-container {
text-align: center;
max-width: 400px;
padding: 2rem;
background: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
.loading-text {
font-size: 1.2rem;
color: #333;
margin: 1rem 0 0.5rem;
font-weight: 500;
}
.loading-subtext {
color: #666;
font-size: 0.9rem;
margin-bottom: 1.5rem;
}
.progress-bar {
width: 100%;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
.progress-fill {
height: 100%;
background: #2196F3;
transition: width 0.3s ease;
}
}
mat-spinner {
margin: 0 auto;
}
}
}
// Main Canvas Area
.canvas-main-area {
position: relative ;
@ -537,6 +718,110 @@ $shadow-heavy: 0 8px 25px rgba(0, 0, 0, 0.15);
}
}
}
.search-section {
.search-container {
position: relative;
margin-bottom: 15px;
.search-input {
width: 100%;
padding: 12px 40px 12px 15px;
border: 2px solid #e9ecef;
border-radius: 25px;
font-size: 14px;
transition: all 0.3s ease;
background: #f8f9fa;
&:focus {
outline: none;
border-color: #007bff;
background: white;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
&::placeholder {
color: #6c757d;
font-style: italic;
}
}
.search-icon {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
color: #6c757d;
pointer-events: none;
}
}
}
.navbar-editor-modal {
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
.navbar-links-section {
margin-top: 20px;
h4 {
margin-bottom: 15px;
color: #333;
border-bottom: 1px solid #e9ecef;
padding-bottom: 5px;
}
.navbar-link-item {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
position: relative;
.link-inputs {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 15px;
align-items: end;
.form-group {
margin-bottom: 0;
label {
font-size: 12px;
font-weight: 600;
color: #666;
margin-bottom: 5px;
}
}
.remove-btn {
background: #dc3545;
color: white;
border: none;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
height: fit-content;
&:hover {
background: #c82333;
}
}
}
}
}
}
.add-custom-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px dashed #dee2e6;
}
.page-actions {
position: fixed;
@ -1035,4 +1320,140 @@ $shadow-heavy: 0 8px 25px rgba(0, 0, 0, 0.15);
i {
font-size: 12px;
}
}
}
/* Add these styles */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255,255,255,0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.hide-scroll {
overflow: hidden;
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.section-loading {
display: flex;
align-items: center;
justify-content: center;
height: 100px;
}
.page-list {
max-height: 60vh;
overflow-y: auto;
}
.page-list {
border: 1px solid #eee;
border-radius: 8px;
padding: 8px;
margin: 12px 0;
&-item {
padding: 12px;
margin: 6px 0;
background: #f8f9fa;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: space-between;
transition: transform 0.2s ease;
.handle {
cursor: move;
margin-right: 8px;
color: #6c757d;
}
&:hover {
background: #e9ecef;
}
&.cdk-drag-preview {
background: white;
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
}
&.cdk-drag-placeholder {
opacity: 0.4;
}
&.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
}
}
.cdk-drop-list-dragging .page-list-item:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
/* Add to your styles */
.connected-sections {
min-height: 100px;
position: relative;
}
/* Add these styles */
.drag-handle {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 30px;
background: rgba(224, 221, 221, 0.05);
cursor: grab;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: background 0.2s ease;
&:hover {
background: rgba(0,0,0,0.08);
}
.fa-grip-vertical {
opacity: 0.5;
transition: opacity 0.2s ease;
}
}
.cdk-drag-preview {
cursor: grabbing !important;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
opacity: 0.8;
.drag-handle {
cursor: grabbing !important;
}
}
.cdk-drag-placeholder {
opacity: 0.3;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.connected-sections.cdk-drop-list-dragging .section-wrapper:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

View File

@ -160,58 +160,58 @@ export class TreeVisualizerComponent {
// Call the actual generateJson method
const prompt = `
You are a JSON structure generator for website UI and sitemap hierarchy.
You are a JSON structure generator for website UI and sitemap hierarchy.
Your task is to generate a strictly hierarchical JSON tree that represents the full page flow of a website. The root of the tree must always be the "Home" page. Other pages (like Login, Sign Up, Dashboard, etc.) must be nested logically using a "Children" key never flatten the structure.
Your task is to generate a strictly hierarchical JSON tree that represents the full page flow of a website. The root of the tree must always be the "Home" page. Other pages (like Login, Sign Up, Dashboard, etc.) must be nested logically using a "Children" key never flatten the structure.
Each page must contain:
Each page must contain:
- 4 to 5 meaningful UI sections (e.g., "Header Section", "Form Section", "Feature Section", etc.)
- A "Navbar" and a "Footer" section (always included on every page)
- A short, clear description for each section (to support frontend development)
- 4 to 5 meaningful UI sections (e.g., "Header Section", "Form Section", "Feature Section", etc.)
- A "Navbar" and a "Footer" section (always included on every page)
- A short, clear description for each section (to support frontend development)
Constraints:
- Total number of pages (including nested children) must be between ${this.selectedPageRange}
- Do NOT create unnecessary or generic pages (e.g., no "Blog", "FAQ", etc.) unless explicitly present in the user flow
- Pages that appear after user actions (e.g., clicking a card or after login) must be nested under the relevant parent using a "Children" key
- DO NOT include any standalone section like "FAQ" or "Blog" as top-level pages unless mentioned
Constraints:
- Total number of pages (including nested children) must be between ${this.selectedPageRange}
- Do NOT create unnecessary or generic pages (e.g., no "Blog", "FAQ", etc.) unless explicitly present in the user flow
- Pages that appear after user actions (e.g., clicking a card or after login) must be nested under the relevant parent using a "Children" key
- DO NOT include any standalone section like "FAQ" or "Blog" as top-level pages unless mentioned
📘 Website Context:
${this.rawInputText}
📘 Website Context:
${this.rawInputText}
--- Reference Format (DO NOT COPY; for structure only) ---
{
"Home": {
"Navbar": "...",
"Header Section": "...",
"Feature Section": "...",
"Footer": "...",
"Children": {
"About": {
--- Reference Format (DO NOT COPY; for structure only) ---
{
"Home": {
"Navbar": "...",
"Header Section": "...",
"Feature Section": "...",
"Footer": "...",
"Children": {
"About": {
"Navbar": "...",
"Header Section": "...",
"Footer": "..."
}
}
},
"Services": {
"Navbar": "...",
"Content Section": "...",
"Footer": "..."
}
}
},
"Services": {
"Navbar": "...",
"Content Section": "...",
"Footer": "..."
}
}
---
---
🚫 DO NOT:
- Include any explanation, notes, comments, or formatting (like \`\`\` or \`json\`)
- Do NOT return escaped or markdown-formatted JSON
- Do NOT wrap the output inside code blocks
- Do NOT return anything except plain raw JSON
🚫 DO NOT:
- Include any explanation, notes, comments, or formatting (like \`\`\` or \`json\`)
- Do NOT return escaped or markdown-formatted JSON
- Do NOT wrap the output inside code blocks
- Do NOT return anything except plain raw JSON
📤 Output ONLY the final JSON structure nothing more.
`;
📤 Output ONLY the final JSON structure nothing more.
`;
const payload = {
// query: this.rawInputText + ' \n ' + suffix,