sitebuilder

This commit is contained in:
string 2025-05-13 09:11:08 +05:30
parent 0137a30e7b
commit 2caa796523
18 changed files with 15482 additions and 4156 deletions

View File

@ -50,6 +50,9 @@
</ng-container></clr-dg-column>
<clr-dg-column [clrDgField]="'html'"> <ng-container *clrDgHideableColumn="{hidden: false}"> Html
</ng-container></clr-dg-column>
<clr-dg-column [clrDgField]="'Json'"> <ng-container *clrDgHideableColumn="{hidden: false}"> htmljson
</ng-container></clr-dg-column>
@ -91,7 +94,9 @@
<clr-dg-cell (click)="goToReplaceStringhtmljson (user.html)"
style="cursor: pointer; align-items: center;"><clr-icon shape="details"></clr-icon>
</clr-dg-cell>
<clr-dg-cell (click)="goToReplaceStringhtmljson (user.htmljson)"
style="cursor: pointer; align-items: center;"><clr-icon shape="details"></clr-icon>
@ -128,8 +133,8 @@
</clr-signpost-content>
</clr-signpost>
<!-- View HTML Button -->
<button class="btn btn-sm btn-outline" (click)="openHtmlPreview(user.id)">
<clr-icon shape="eye"></clr-icon>
<button class="btn btn-sm btn-outline" (click)="openHtmlPreview(user.id)" title="Open Preview in New Page">
<clr-icon shape="eye"></clr-icon> Preview
</button>
</div>
</clr-dg-cell>
@ -359,6 +364,10 @@
</div>
<div class="clr-col-sm-12">
<label> Html</label>
<textarea cols="10" rows="2" [(ngModel)]="rowSelected.html" name="html " placeholder="Textarea"> </textarea>
</div>
<div class="clr-col-sm-12">
<label> Json</label>
<textarea cols="10" rows="2" [(ngModel)]="rowSelected.htmljson" name="htmljson "
@ -482,6 +491,10 @@
<input type="checkbox" formControlName="active" clrToggle />
</div>
<div class="clr-col-sm-12">
<label> Html</label>
<textarea cols="15" rows="3" formControlName="html" placeholder="Textarea"> </textarea>
</div>
<div class="clr-col-sm-12">
<label> Json</label>
<textarea cols="15" rows="3" formControlName="htmljson" placeholder="Textarea"> </textarea>

View File

@ -9,6 +9,8 @@ import { Design_lbrarycardvariable } from './Design_lbrary_cardvariable';
import { UserInfoService } from 'src/app/services/user-info.service';
import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service';
import { COMMON_CSS } from '../WireframesUi/common-css';
import { Download_Css } from '../WireframesUi/download-css';
// import { Download_Css } from '../WireframesUi/download-css';
declare var JsBarcode: any;
@Component({
selector: 'app-Design_lbrary',
@ -79,11 +81,13 @@ export class Design_lbraryComponent implements OnInit {
htmljson: [null],
css: [null],
templatetype:[null],
uitype:[null],
templatetype: [null],
uitype: [null],
javacode: [null],
typerender: [null],
techstack: [null],
html: [null],
@ -352,12 +356,16 @@ export class Design_lbraryComponent implements OnInit {
// this.modalHtmlPreview = true;
// });
// }
openHtmlPreview(id: number): void {
console.log('preview html start..');
this.siteTreeService.generateHtmlwithcss(id).subscribe((response: any) => {
const bodyContent = response.msg || '<div>Empty</div>'; // fallback
// Store raw content for download/copy
const rawHtmlContent = bodyContent;
// Full HTML with styles and controls
const fullHtml = `
<!DOCTYPE html>
<html lang="en">
@ -366,38 +374,204 @@ export class Design_lbraryComponent implements OnInit {
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML Preview</title>
<style>
${COMMON_CSS}
${Download_Css}
/* Additional styles for controls */
.preview-controls {
position: fixed !important;
top: 10px !important;
right: 10px !important;
background-color: rgba(255, 255, 255, 0.9) !important;
border: 1px solid #ccc !important;
border-radius: 4px !important;
padding: 10px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2) !important;
z-index: 1000 !important;
}
.preview-controls button {
margin: 5px !important;
padding: 8px 12px !important;
background-color: #0072a3 !important;
color: white !important;
border: none !important;
border-radius: 3px !important;
cursor: pointer !important;
font-size: 14px !important;
}
.preview-controls button:hover {
background-color: #005a80 !important;
}
.toast {
position: fixed !important;
top: 70px !important;
right: 10px !important;
padding: 10px 20px !important;
background-color: #4CAF50 !important;
color: white !important;
border-radius: 4px !important;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2) !important;
opacity: 0 !important;
transition: opacity 0.3s !important;
z-index: 1001 !important;
}
.toast.show {
opacity: 1 !important;
}
.html-source {
display: none !important;
margin-top: 20px !important;
width: 100% !important;
height: 300px !important;
font-family: monospace !important;
resize: vertical !important;
white-space: pre !important;
overflow: auto !important;
background-color: #f5f5f5 !important;
border: 1px solid #ddd !important;
padding: 10px !important;
}
.show-source {
display: block !important;
}
</style>
</head>
<body>
<div class="preview-controls">
<button id="download-btn">Download HTML</button>
<button id="copy-btn">Copy to Clipboard</button>
<button id="toggle-source-btn">View Source</button>
</div>
<div id="toast" class="toast">Copied to clipboard!</div>
<div id="content-preview">
${bodyContent}
</div>
<pre id="html-source" class="html-source"></pre>
<script>
// Format HTML for display in the source view
function formatHtml(html) {
return html.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
// Store the raw HTML content
const rawHtml = \`${rawHtmlContent.replace(/`/g, '\\`')}\`;
// Download functionality with full HTML and CSS
document.getElementById('download-btn').addEventListener('click', function() {
const fullHtmlContent = \`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Downloaded HTML</title>
<style>
${Download_Css}
</style>
</head>
<body>
\${rawHtml}
</body>
</html>
\`;
const blob = new Blob([fullHtmlContent], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'preview-' + new Date().toISOString().slice(0, 10) + '.html';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
// Copy to clipboard functionality
document.getElementById('copy-btn').addEventListener('click', function() {
navigator.clipboard.writeText(rawHtml).then(function() {
const toast = document.getElementById('toast');
toast.textContent = 'Copied to clipboard!';
toast.classList.add('show');
setTimeout(function() {
toast.classList.remove('show');
}, 2000);
}).catch(function(err) {
console.error('Could not copy text: ', err);
const toast = document.getElementById('toast');
toast.textContent = 'Failed to copy to clipboard';
toast.style.backgroundColor = '#f44336';
toast.classList.add('show');
setTimeout(function() {
toast.classList.remove('show');
}, 2000);
});
});
// Toggle source view
document.getElementById('toggle-source-btn').addEventListener('click', function() {
const sourceElem = document.getElementById('html-source');
if (!sourceElem.classList.contains('show-source')) {
sourceElem.textContent = rawHtml;
sourceElem.innerHTML = formatHtml(rawHtml);
sourceElem.classList.add('show-source');
this.textContent = 'Hide Source';
} else {
sourceElem.classList.remove('show-source');
this.textContent = 'View Source';
}
});
</script>
</body>
</html>
`;
this.htmlContent = fullHtml;
console.log('html code ..', this.htmlContent);
this.modalHtmlPreview = true;
// Open the HTML in a new window/tab
const newWindow = window.open('', '_blank');
if (newWindow) {
newWindow.document.write(fullHtml);
newWindow.document.close();
} else {
// If popup is blocked, inform the user
this.toastr.warning('Pop-up blocked. Please allow pop-ups for this site to see the preview.');
}
}, error => {
this.htmlContent = `
// Handle error by opening a new window with error message
const errorHtml = `
<!DOCTYPE html>
<html><head><style>${COMMON_CSS}</style></head>
<body><p style="color:red;">Error loading HTML.</p></body></html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error Preview</title>
<style>${COMMON_CSS}</style>
</head>
<body>
<div style="padding: 20px; text-align: center;">
<h2 style="color:red;">Error Loading Preview</h2>
<p>The preview content could not be loaded. Error details:</p>
<pre style="background-color: #f8f8f8; padding: 10px; border-radius: 5px; text-align: left;">${error ? JSON.stringify(error, null, 2) : 'Unknown error'}</pre>
</div>
</body>
</html>
`;
this.modalHtmlPreview = true;
const newWindow = window.open('', '_blank');
if (newWindow) {
newWindow.document.write(errorHtml);
newWindow.document.close();
} else {
this.toastr.error('Error loading preview. Pop-up blocked. Please allow pop-ups for this site.');
}
});
}
}

View File

@ -64,7 +64,18 @@ export class SiteTreeservice {
return this.apiRequest.post(`sureops/deploy?projId=` + projId, data);
}
createHtmlfile(siteNmae: String, data: any): Observable<any> {
const _http = this.baseURL + "/createFile?siteBuilderName=" + siteNmae;
return this.apiRequest.post(_http, data);
}
// updateaction
readPages(siteNmae: String, pageNmae: String): Observable<any> {
const _http = this.baseURL + "/read?siteBuilderName=" + siteNmae + '&filename=' + pageNmae;
return this.apiRequest.get(_http, undefined, 'text');
}
// updateactionƒ
}

View File

@ -198,7 +198,15 @@
margin-bottom: 8px;
color: #333;
}
.offcanvas-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.3);
z-index: 1040;
}
.section-content {
font-size: 13px;
color: #555;
@ -385,12 +393,15 @@
}
.btn {
padding: 8px 15px;
border-radius: 4px;
padding: 10px 16px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
border: 1px solid transparent;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
}
.btn-primary {
@ -398,10 +409,19 @@
color: white;
}
.btn-primary:hover {
.btn-primary:hover:not([disabled]) {
background-color: #0069d9;
transform: translateY(-1px);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.btn-primary[disabled] {
background-color: #7fb6ff;
cursor: not-allowed;
}
.btn-secondary {
background-color: #6c757d;
color: white;
@ -409,20 +429,70 @@
.btn-secondary:hover {
background-color: #5a6268;
transform: translateY(-1px);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.mt-3 {
margin-top: 1rem;
}
.mt-4 {
margin-top: 1.5rem;
}
@keyframes slideIn {
from { right: -400px; }
to { right: 0; }
}
@keyframes slideOut {
from { right: 0; }
to { right: -400px; }
}
.offcanvas.show {
animation: slideIn 0.3s forwards;
}
.offcanvas:not(.show) {
animation: slideOut 0.3s forwards;
}
.form-control {
display: block;
width: 100%;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border-radius: 6px;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
padding: 10px 12px;
transition: all 0.2s ease;
}
.form-control:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}
textarea.form-control {
min-height: 120px;
resize: vertical;
}
.custom-section-form {
margin-top: 30px;
padding-top: 20px;
border-top: 1px dashed #ddd;
}
.custom-section-form h4 {
margin-bottom: 15px;
color: #444;
font-size: 16px;
font-weight: 600;
}
.mb-3 {
@ -438,7 +508,7 @@
background-color: white;
box-shadow: -5px 0 15px rgba(0,0,0,0.1);
z-index: 1050;
transition: right 0.3s ease;
transition: all 0.3s cubic-bezier(0.25, 1, 0.5, 1);
overflow-y: auto;
}
@ -450,27 +520,47 @@
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 20px;
border-bottom: 1px solid #ddd;
padding: 18px 20px;
border-bottom: 1px solid #eaeaea;
background-color: #f8f9fa;
}
.offcanvas-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.close-btn:hover {
background-color: #f1f1f1;
color: #333;
transform: rotate(90deg);
}
.offcanvas-body {
padding: 20px;
}
.section-templates-container {
max-height: 50vh;
overflow-y: auto;
padding-right: 5px;
}
.section-template {
padding: 15px;
border: 1px solid #ddd;
@ -478,11 +568,30 @@
margin-bottom: 15px;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
overflow: hidden;
}
.section-template:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 0;
background-color: #007bff;
transition: height 0.3s ease;
}
.section-template:hover {
background-color: #f8f9fa;
border-color: #007bff;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.05);
}
.section-template:hover:before {
height: 100%;
}
.section-template-title {

View File

@ -47,31 +47,40 @@
</div>
</div>
</div>
<div class="editor-form" *ngIf="editMode">
<!-- <div class="editor-form" *ngIf="editMode">
<div *ngIf="editMode === 'section' && selectedSection && selectedPage">
<div class="editor-title">Edit Section: {{ selectedSection.type }}</div>
<div class="form-group">
<label class="form-label">Content</label>
<textarea class="form-textarea" [(ngModel)]="selectedSection.content"></textarea>
<textarea
class="form-textarea"
[(ngModel)]="selectedSection.content"></textarea>
</div>
<div class="form-actions">
<button class="btn btn-secondary" (click)="cancelEdit()">Cancel</button>
<button class="btn btn-primary" (click)="saveEdit()">Save Changes</button>
</div>
</div>
</div>
<div class="offcanvas" [class.show]="showOffcanvas">
</div> -->
<!-- Replace your current offcanvas with these two separate ones -->
<!-- Add Section Offcanvas -->
<div class="offcanvas add-section" [class.show]="showAddSectionOffcanvas">
<div class="offcanvas-header">
<div class="offcanvas-title">Add Section</div>
<button class="close-btn" (click)="closeOffcanvas()">&times;</button>
<button class="close-btn" (click)="closeAddSectionOffcanvas()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="offcanvas-body">
<div class="section-templates-container">
<div class="section-template" *ngFor="let template of sectionTemplates"
(click)="addSectionFromTemplate(template)">
<div class="section-template-title">{{ template.type }}</div>
<div class="section-template-desc">{{ template.description }}</div>
</div>
<div class="form-group" style="margin-top: 20px;">
</div>
<div class="custom-section-form">
<h4>Custom Section</h4>
<div class="mb-3">
<label class="form-label">Section Type</label>
@ -88,6 +97,32 @@
</div>
</div>
</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>
<div class="form-group mt-3">
<label class="form-label">Content</label>
<textarea class="form-control" [(ngModel)]="selectedSection && selectedSection.content" rows="8"
placeholder="Enter content"></textarea>
</div>
<div class="form-actions mt-4">
<button class="btn btn-secondary" (click)="closeEditSectionOffcanvas()">Cancel</button>
<button class="btn btn-primary" (click)="saveSection()">Save Changes</button>
</div>
</div>
</div>
<div class="modal" [class.show]="showAddChildModal">
<div class="modal-backdrop" *ngIf="showAddChildModal" (click)="closeAddChildModal()"></div>
<div class="modal-dialog" *ngIf="showAddChildModal">
@ -123,11 +158,13 @@
<i class="node-icon" [class]="getNodeIcon(page)"></i>
<span class="node-title">{{ page.name }}</span>
</div>
<div class="sections-container" cdkDropList [cdkDropListData]="page.sections">
<div class="sections-container" cdkDropList [cdkDropListData]="page.sections"
(cdkDropListDropped)="onSectionDrop($event)">
<!-- Update the section card template within the #pageTemplate -->
<!-- Section card with improved hover button placement -->
<div class="section-card" *ngFor="let section of page.sections; let i = index" cdkDrag
<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">
@ -139,7 +176,7 @@
</button>
</div>
<!-- Repositioned add button that appears on hover -->
<div class="add-section-hover-btn" (click)="openSectionOffcanvas(page, i, $event)" title="Add Section">
<div class="add-section-hover-btn" (click)="openAddSectionOffcanvas(page, i, $event)" title="Add Section">
<i class="fas fa-plus"></i>
</div>
</div>

View File

@ -73,6 +73,11 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
currentSectionIndex: number = -1;
//off canvas
showAddSectionOffcanvas = false;
showEditSectionOffcanvas = false;
// Section templates with better descriptions
sectionTemplates = [
@ -131,8 +136,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
constructor(
private siteTreeService: SiteTreeservice,
private route: ActivatedRoute,
private toastr: ToastrService
private toastr: ToastrService,
) { }
@ -157,6 +161,9 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
}
ngOnDestroy(): void {
// Remove event listener if component is destroyed while offcanvas is open
document.removeEventListener('click', this.handleOutsideClick);
// Auto-save changes before component destruction
if (this.hasUnsavedChanges) {
this.saveTreeData(true);
@ -622,6 +629,80 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
this.currentSectionIndex = -1;
}
openAddSectionOffcanvas(page: Page, sectionIndex: number = -1, event?: Event): void {
if (event) {
event.stopPropagation();
}
this.currentParentPage = page;
this.currentSectionIndex = sectionIndex;
this.customSectionType = '';
this.customSectionContent = '';
this.showAddSectionOffcanvas = true;
// Listen for clicks outside offcanvas
setTimeout(() => {
document.addEventListener('click', this.handleOutsideClick);
}, 10);
}
// Open edit section offcanvas
openEditSectionOffcanvas(section: Section, page: Page, event: Event): void {
event.stopPropagation();
this.selectedSection = { ...section }; // Clone to avoid direct mutation until save
this.currentParentPage = page;
this.editMode = 'section';
this.showEditSectionOffcanvas = true;
// Listen for clicks outside offcanvas
setTimeout(() => {
document.addEventListener('click', this.handleOutsideClick);
}, 10);
}
handleOutsideClick = (event: MouseEvent): void => {
const addOffcanvas = document.querySelector('.offcanvas.add-section');
const editOffcanvas = document.querySelector('.offcanvas.edit-section');
if (addOffcanvas && !addOffcanvas.contains(event.target as Node) && this.showAddSectionOffcanvas) {
this.closeAddSectionOffcanvas();
}
if (editOffcanvas && !editOffcanvas.contains(event.target as Node) && this.showEditSectionOffcanvas) {
this.closeEditSectionOffcanvas();
}
}
closeAddSectionOffcanvas(): void {
this.showAddSectionOffcanvas = false;
this.currentParentPage = null;
this.currentSectionIndex = -1;
document.removeEventListener('click', this.handleOutsideClick);
}
// Close edit section offcanvas
closeEditSectionOffcanvas(): void {
this.showEditSectionOffcanvas = false;
this.selectedSection = null;
this.editMode = null;
document.removeEventListener('click', this.handleOutsideClick);
}
// Save section changes
saveSection(): void {
if (this.selectedSection && this.currentParentPage) {
// Find the section in the parent page
const index = this.currentParentPage.sections.findIndex(s =>
s.type === this.selectedSection.type && s.content === this.selectedSection.content);
if (index !== -1) {
// Update the section with edited values
this.currentParentPage.sections[index] = { ...this.selectedSection };
this.hasUnsavedChanges = true;
}
}
this.closeEditSectionOffcanvas();
}
// Add section from a template
addSectionFromTemplate(template: any): void {
if (this.currentParentPage) {
@ -651,6 +732,12 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
// Similar update for addCustomSection
addCustomSection(): void {
if (this.currentParentPage && this.customSectionType) {
// If in edit mode, update the selected section
if (this.editMode === 'section' && this.selectedSection) {
this.selectedSection.type = this.customSectionType;
this.selectedSection.content = this.customSectionContent || '';
} else {
// Otherwise, create a new section
const newSection: Section = {
type: this.customSectionType,
content: this.customSectionContent || ''
@ -662,9 +749,6 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
} else {
this.currentParentPage.sections.push(newSection);
}
if (!this.availableSectionTypes.includes(this.customSectionType)) {
this.availableSectionTypes.push(this.customSectionType);
}
this.hasUnsavedChanges = true;
@ -673,10 +757,10 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
}
// Edit section
// Edit section - modify this function
editSection(section: Section, event: Event): void {
event.stopPropagation();
this.selectedSection = section;
this.editMode = 'section';
this.openEditSectionOffcanvas(section, this.selectedPage, event);
}
// Cancel edit
@ -762,6 +846,8 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
);
}
// Force change detection
this.selectedPage = { ...this.selectedPage! };
this.hasUnsavedChanges = true;
}
@ -795,7 +881,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
if (!silent) {
// alert('Site structure saved successfully');
this.toastr.success("Site structure saved successfully");
this.toastr.success(`Site structure saved successfully`);
}
},
@ -804,9 +890,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
console.error('Error saving data:', err);
if (!silent) {
// alert('Failed to save site structure');
this.toastr.error("Failed to save site structure");
alert('Failed to save site structure');
}
}
});

View File

@ -0,0 +1,18 @@
export const Download_Css = `
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #fff;
color: #111;
}
`;

View File

@ -77,119 +77,287 @@
</div>
</div> -->
<!-- ✅ Render whole page in one block -->
<!-- <div class="canvas-section">
<!-- ✅ Render whole page in one block -->
<!-- <div class="canvas-section">
<div [innerHTML]="pageSections[pageName]['FullPage']"></div>
</div>
</div>
</div> -->
<!-- Controls and Actions Row -->
<!-- part 3 -->
<!-- 🔁 Regenerate + Save Buttons -->
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<div>
<button (click)="regenerateAllModifiedSections()" style="background: orange; color: white; border: none; padding: 8px 14px; border-radius: 4px; cursor: pointer;">
<!-- Editable HTML Area -->
<!-- <div class="editable-area" *ngIf="editMode">
<div class="editable-header">
<h3>Editing: {{ editingSection?.page }} > {{ editingSection?.section }}</h3>
<button class="close-btn" (click)="closeEditMode()">×</button>
</div>
<div class="editable-content">
<div
[innerHTML]="editingSectionHtml"
contenteditable="true"
class="editable-html"
(blur)="onSectionEdit($event)">
</div>
</div>
<div class="editable-footer">
<button class="action-btn save-edit-btn" (click)="saveSectionEdit()">
Save Changes
</button>
<button class="action-btn cancel-edit-btn" (click)="closeEditMode()">
Cancel
</button>
</div>
</div> -->
<div class="action-bar">
<div class="left-controls">
<button (click)="regenerateAllModifiedSections()" class="action-btn regenerate-btn">
♻️ Regenerate Modified
</button>
<button (click)="uploadHtmlFiles(37388)" style="background: green; color: white; border: none; padding: 8px 14px; border-radius: 4px; cursor: pointer;">
<!-- <button (click)="uploadHtmlFiles(11096)" class="action-btn save-btn"> -->
<button (click)="createHtmlFiles()" class="action-btn save-btn">
💾 Save All HTML Files
</button>
<button (click)="uploadHtmlFiles(11096)" class="action-btn save-btn">
💾 build wireframe
</button>
<button class="btn btn-primary" (click)="recreateWireframe()">🔄 Recreate Wireframe</button>
<div class="navbar-controls">
<select [(ngModel)]="selectedNavbarPage" (change)="selectedNavbarPage && selectNavbar(selectedNavbarPage)">
<option value="">Select Navbar Page</option>
<option *ngFor="let page of objectKeys(pageSections)" [value]="page">{{ page }}</option>
</select>
</div>
</div>
<!-- Navbar Editor Modal -->
<div class="modal" *ngIf="selectedNavbarPage">
<div class="modal-content">
<div class="modal-header">
<h3>Edit Navbar Links - {{ selectedNavbarPage }}</h3>
<button class="close-btn" (click)="selectedNavbarPage = ''">×</button>
</div>
<div class="modal-body">
<div class="link-editor" *ngFor="let link of navbarLinks[selectedNavbarPage]; let i = index">
<input type="text" [(ngModel)]="link.label" placeholder="Link Label">
<select [(ngModel)]="link.href">
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
</select>
<button class="remove-btn" (click)="removeLink(selectedNavbarPage, i)">×</button>
</div>
<button class="action-btn add-link-btn" (click)="addLink(selectedNavbarPage)">
Add New Link
</button>
</div>
<div>
<!-- Global Style Controls -->
<select [(ngModel)]="liveStyles.fontSize">
<div class="modal-footer">
<button class="action-btn" (click)="applyNavbarChanges(selectedNavbarPage)">
Save Changes
</button>
<button class="action-btn cancel-btn" (click)="selectedNavbarPage = ''">
Cancel
</button>
</div>
</div>
</div>
<div class="right-controls">
<select [(ngModel)]="liveStyles.fontSize" class="style-control">
<option value="">Font Size</option>
<option *ngFor="let size of ['12px', '14px', '16px', '18px', '20px', '24px', '28px']" [value]="size">{{ size }}</option>
<option *ngFor="let size of ['12px', '14px', '16px', '18px', '20px', '24px', '28px']" [value]="size">{{ size }}
</option>
</select>
<select [(ngModel)]="liveStyles.fontWeight">
<select [(ngModel)]="liveStyles.fontWeight" class="style-control">
<option value="">Font Weight</option>
<option *ngFor="let weight of ['normal', 'bold', 'lighter']" [value]="weight">{{ weight }}</option>
</select>
<select [(ngModel)]="liveStyles.textAlign">
<select [(ngModel)]="liveStyles.textAlign" class="style-control">
<option value="">Align</option>
<option value="left">Left</option>
<option value="center">Center</option>
<option value="right">Right</option>
</select>
<input type="color" [(ngModel)]="liveStyles.color" title="Text Color" />
<input type="color" [(ngModel)]="liveStyles.background" title="Background Color" />
<input type="color" [(ngModel)]="liveStyles.color" title="Text Color" class="color-picker" />
<input type="color" [(ngModel)]="liveStyles.background" title="Background Color" class="color-picker" />
<button (click)="applyStyleToSelection()" style="background: #555; color: white; padding: 6px 10px; border-radius: 4px;">🎨 Apply Style</button>
<button (click)="applyStyleToSelection()" class="action-btn style-btn">
🎨 Apply Style
</button>
<button (click)="verifyCssInSections()" class="action-btn">🔍 Verify CSS</button>
</div>
</div>
<!-- Navbar Link Manager -->
<!-- <div *ngIf="selectedNavbarPage" class="navbar-link-manager">
<h3>🔗 Navbar Link Manager ({{ selectedNavbarPage }})</h3>
<!-- Grid and Wireframe Canvas Area -->
<div class="visual-editor-container">
<!-- Zoom Controls -->
<div class="zoom-controls">
<button class="zoom-btn" (click)="zoomIn()">
<i class="fa fa-plus"></i>
</button>
<button class="zoom-btn" (click)="zoomOut()">
<i class="fa fa-minus"></i>
</button>
<button class="zoom-btn" (click)="resetZoom()">
<i class="fa fa-expand"></i>
</button>
</div>
<ul>
<li *ngFor="let link of navbarLinks[selectedNavbarPage]; let i = index">
<input [(ngModel)]="link.label" placeholder="Link Label" />
<select [(ngModel)]="link.href">
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
<!-- Canvas Area with Grid -->
<div class="canvas-wrapper" cdkScrollable>
<div class="canvas-boundary" [style.transform]="getTransform()" (mousedown)="startPan($event)"
(mousemove)="doPan($event)" (mouseup)="endPan()" (mouseleave)="endPan()">
<!-- Grid Background -->
<div class="grid-background"></div>
<div *ngIf="isLoading" class="loading-overlay">
<div class="spinner"></div>
</div>
<!-- Pages Container - Draggable Pages -->
<div *ngIf="!isLoading" class="pages-container" cdkDropList cdkDropListOrientation="vertical"
(cdkDropListDropped)="onPageDrop($event)">
<div *ngFor="let pageName of pageRenderOrder" class="page-card" cdkDrag
[class.page-hovered]="hoveredPage === pageName" (mouseenter)="showPageTools(pageName)"
(mouseleave)="hidePageTools(pageName)">
<!-- Page Header with Tools -->
<div class="page-header">
<h3 class="page-title">{{ pageName }}</h3>
<div class="page-tools" [class.visible]="hoveredPage === pageName">
<button class="tool-btn" (click)="regenerateWireframe(pageName)" title="Regenerate">♻️</button>
<button class="tool-btn" (click)="copyToClipboard(pageName)" title="Copy HTML">📋</button>
<button class="tool-btn" (click)="downloadHtml(pageName)" title="Download HTML">⬇️</button>
</div>
</div>
<!-- Sections Container - Draggable Sections -->
<div class="sections-container" cdkDropList [id]="'section-list-' + pageName"
[cdkDropListData]="getSectionsData(pageName)" (cdkDropListDropped)="onSectionDrop($event, pageName)">
<!-- Individual Draggable Sections -->
<div *ngFor="let sectionKey of getSectionKeys(pageName); let i = index" class="section-card"
[attr.data-section-id]="sectionKey" cdkDrag [cdkDragData]="sectionKey"
(mouseenter)="showSectionTools(pageName, sectionKey)" (mouseleave)="hideSectionTools()">
<!-- Section Content -->
<div class="section-content">
<!-- Section Header -->
<div class="section-header">
<span class="section-title">{{ sectionKey }}</span>
<!-- Section Actions -->
<div class="section-actions" [class.visible]="hoveredSection === sectionKey">
<button class="section-btn" title="Toggle Edit Mode"
(click)="toggleSectionEditing(pageName, sectionKey, $event)">
<i class="fa fa-pencil"></i>
</button>
<button class="section-btn" title="Remove Section" (click)="removeSection(pageName, sectionKey)">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
<!-- Section Preview with COMMON_CSS applied -->
<!-- Change all [innerHTML] bindings to use $any() to bypass strict checks -->
<div class="section-preview" [innerHTML]="$any(getSectionHtml(pageName, sectionKey))"
(dblclick)="toggleSectionEditing(pageName, sectionKey, $event)"
(blur)="handleDirectContentEdit($event, pageName, sectionKey)"
[attr.contenteditable]="isSectionEditable(pageName, sectionKey)">
</div>
</div>
<!-- Add Section Button (appears on hover) -->
<div class="add-section-btn" *ngIf="hoveredSection === sectionKey"
(click)="addNewSection(pageName, sectionKey)">
<i class="fa fa-plus"></i>
</div>
<!-- Drag Handle -->
<div class="drag-handle" cdkDragHandle>
<i class="fa fa-grip-lines"></i>
</div>
</div>
<!-- Empty State / Add First Section -->
<div *ngIf="getSectionKeys(pageName).length === 0" class="empty-sections">
<button class="add-first-section" (click)="addNewSection(pageName)">
<i class="fa fa-plus"></i> Add First Section
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal for Adding New Section -->
<div class="modal" *ngIf="showNewSectionModal">
<div class="modal-content">
<div class="modal-header">
<h3>Add New Section to {{ newSectionPage }}</h3>
<button class="close-btn" (click)="cancelAddSection()">×</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Section Name:</label>
<input type="text" [(ngModel)]="newSectionName" placeholder="Enter section name">
</div>
<div class="form-group">
<label>Section Type:</label>
<select [(ngModel)]="newSectionType">
<option value="">Select a type</option>
<option value="header">Header</option>
<option value="hero">Hero</option>
<option value="features">Features</option>
<option value="gallery">Gallery</option>
<option value="pricing">Pricing</option>
<option value="testimonials">Testimonials</option>
<option value="contact">Contact</option>
<option value="footer">Footer</option>
</select>
<button (click)="removeLink(selectedNavbarPage, i)"></button>
</li>
</ul>
</div>
<button (click)="addLink(selectedNavbarPage)"> Add Link</button>
<button (click)="applyNavbarChanges(selectedNavbarPage)">✅ Apply Changes</button>
</div> -->
<div class="form-group">
<label>Section Description:</label>
<textarea [(ngModel)]="newSectionDescription" rows="4"
placeholder="Describe the content for this section"></textarea>
</div>
</div>
<!-- 🔗 Navbar Link Editor Modal -->
<div *ngIf="selectedNavbarPage" class="navbar-popup-overlay">
<div class="navbar-popup-content">
<h3>🔗 Navbar Link Manager ({{ selectedNavbarPage }})</h3>
<ul>
<li *ngFor="let link of navbarLinks[selectedNavbarPage]; let i = index">
<input [(ngModel)]="link.label" placeholder="Link Label" />
<select [(ngModel)]="link.href">
<option *ngFor="let page of availablePages" [value]="page.href">{{ page.label }}</option>
</select>
<button (click)="removeLink(selectedNavbarPage, i)"></button>
</li>
</ul>
<div style="margin-top: 10px;">
<button (click)="addLink(selectedNavbarPage)"> Add Link</button>
<button (click)="applyNavbarChanges(selectedNavbarPage)">✅ Apply</button>
<button (click)="selectedNavbarPage = null">❌ Close</button>
</div>
</div>
</div>
<!-- ✅ Pages List -->
<div class="canvas-wrapper">
<div class="canvas-page" *ngFor="let pageName of pageRenderOrder">
<div class="canvas-header">
<button (click)="regenerateWireframe(pageName)">♻️</button>
<h3 (click)="selectNavbar(pageName)" style="cursor: pointer;">
{{ pageName }} <span title="Edit Navbar">🧭</span>
</h3>
<div class="canvas-actions">
<button (click)="copyToClipboard(pageName)">📋</button>
<button (click)="downloadHtml(pageName)">⬇️</button>
</div>
</div>
<!-- ✅ Render and Edit Section -->
<div class="canvas-section">
<div [innerHTML]="pageSections[pageName]?.FullPage"
contenteditable="true"
class="editable-html"
(blur)="onEdit(pageName, $event)">
</div>
<div class="modal-footer">
<button class="action-btn" [disabled]="!newSectionName || !newSectionType" (click)="confirmAddSection()">
Add Section
</button>
<button class="action-btn cancel-btn" (click)="cancelAddSection()">
Cancel
</button>
</div>
</div>
</div>

View File

@ -1,92 +1,668 @@
// .wireframe-container {
// font-family: Arial, sans-serif;
// background: #f8f8f8;
// padding: 1rem;
// }
/* Reset and base styles */
.visual-editor-container {
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
// .border-dashed {
// border-style: dashed;
// }
.canvas-wrapper {
display: flex;
flex-wrap: wrap; /* ✅ Allow wrapping on smaller screens */
gap: 20px;
padding: 20px;
justify-content: center;
background: #f0f0f0;
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #333;
line-height: 1.5;
overflow: hidden;
}
}
/* Overall layout fixes */
.visual-editor-container {
position: relative;
height: calc(100vh - 60px);
overflow: hidden;
background: #f0f2f5;
}
.canvas-page {
min-width: 300px;
min-height: 500px;
resize: both; /* 👈 Allows both vertical and horizontal resizing */
overflow: auto; /* 👈 So content doesn't get cut off */
border: 1px solid #ccc;
border-radius: 8px;
background: white;
padding: 20px;
box-shadow: 0 0 6px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
gap: 20px;
}
.canvas-header {
/* Action bar styling */
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
.canvas-header h3 {
margin: 0;
font-size: 18px;
}
.canvas-actions button {
margin-left: 5px;
background: #007bff;
border: none;
color: white;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
}
.canvas-section {
border: 1px dashed #ccc;
padding: 10px;
border-radius: 6px;
background: #fff;
padding: 8px 16px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 100;
height: 60px;
}
.section-preview {
.preview-mode {
min-height: 200px;
padding: 20px;
transform: scale(0.8);
transform-origin: top left;
width: 125%;
* {
pointer-events: none;
}
}
&[contenteditable="true"] {
.preview-mode {
transform: none;
width: 100%;
pointer-events: auto;
* {
pointer-events: auto;
}
}
}
}
.left-controls, .right-controls {
display: flex;
align-items: center;
gap: 10px;
}
.action-btn {
padding: 8px 12px;
border-radius: 4px;
border: none;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
background: #f5f5f5;
&:hover {
background: #e0e0e0;
}
&.regenerate-btn {
background: #e3f2fd;
color: #2196f3;
&:hover {
background: #bbdefb;
}
}
&.save-btn {
background: #e8f5e9;
color: #4caf50;
&:hover {
background: #c8e6c9;
}
}
}
.style-control {
padding: 6px 8px;
border-radius: 4px;
border: 1px solid #ddd;
min-width: 100px;
}
.color-picker {
width: 30px;
height: 30px;
padding: 0;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
}
/* Zoom controls */
.zoom-controls {
position: absolute;
top: 10px;
right: 10px;
display: flex;
gap: 5px;
z-index: 50;
background: rgba(255, 255, 255, 0.9);
padding: 5px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.zoom-btn {
width: 30px;
height: 30px;
border-radius: 50%;
border: none;
background: #f5f5f5;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background: #e0e0e0;
}
}
/* Canvas and grid styling */
.canvas-wrapper {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.canvas-boundary {
position: absolute;
width: 100%;
height: 100%;
cursor: move;
transform-origin: 0 0;
will-change: transform;
background-color: #f5f5f5;
}
.grid-background {
position: absolute;
width: 5000px;
height: 5000px;
background-size: 20px 20px;
background-image:
linear-gradient(to right, rgba(0,0,0,0.05) 1px, transparent 1px),
linear-gradient(to bottom, rgba(0,0,0,0.05) 1px, transparent 1px);
transform: translate(-2500px, -2500px);
}
/* Pages container */
.pages-container {
display: flex;
gap: 40px;
padding: 40px;
align-items: flex-start;
position: relative;
z-index: 10;
width: 2000px;
height: auto;
overflow: hidden;
}
/* Page card styling */
.page-card {
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
position: relative;
transition: box-shadow 0.3s;
width: 100%;
margin: 20px 0;
overflow: hidden;
&:hover {
box-shadow: 0 6px 14px rgba(0,0,0,0.15);
}
&.page-hovered {
border: 2px solid #2196f3;
}
}
/* Page header */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #eee;
background: #f5f7fa;
border-radius: 8px 8px 0 0;
}
/* Navbar Editor Styles */
.navbar-controls {
margin-left: 20px;
select {
padding: 8px 12px;
border-radius: 4px;
border: 1px solid #ccc;
}
}
.link-editor {
display: flex;
gap: 10px;
margin-bottom: 10px;
input, select {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.remove-btn {
background: #ff4444;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer;
}
}
.add-link-btn {
margin-top: 15px;
width: 100%;
}
.page-title {
font-size: 16px;
font-weight: 600;
margin: 0;
}
.page-tools {
display: flex;
gap: 8px;
opacity: 0;
transition: opacity 0.2s;
&.visible {
opacity: 1;
}
}
.tool-btn {
background: none;
border: none;
cursor: pointer;
font-size: 14px;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background: rgba(0,0,0,0.05);
}
}
/* In component's CSS */
.pages-container {
display: flex;
flex-direction: column;
gap: 2rem;
padding: 1rem;
}
.editable-html:focus {
outline: 2px solid #007bff;
background: #fcfcfc;
.page-card {
width: 100%;
margin-bottom: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
background: white;
}
.navbar-popup-overlay {
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Sections container */
.sections-container {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
min-height: 100px;
max-height: 600px;
overflow-y: auto;
scrollbar-width: thin;
// width: 1700px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(0,0,0,0.2);
border-radius: 3px;
}
}
/* Section card */
.section-card {
background: white;
border: 1px solid #eaeaea;
border-radius: 6px;
transition: all 0.2s;
position: relative;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
&:hover {
box-shadow: 0 2px 5px rgba(0,0,0,0.12);
border-color: #dadada;
}
}
/* Section header */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: #f8f9fa;
border-bottom: 1px solid #eee;
border-radius: 6px 6px 0 0;
}
.section-title {
font-size: 14px;
font-weight: 500;
color: #444;
}
.section-actions {
display: flex;
gap: 6px;
opacity: 0;
transition: opacity 0.2s;
&.visible {
opacity: 1;
}
}
.section-btn {
background: none;
border: none;
cursor: pointer;
width: 26px;
height: 26px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background: rgba(0,0,0,0.05);
}
}
/* Section preview area */
.section-preview {
padding: 12px;
min-height: 50px;
font-size: 14px;
word-break: break-word;
position: relative;
}
/* Add section button */
.add-section-btn {
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 24px;
border-radius: 50%;
background: #2196f3;
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 5;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
&:hover {
background: #1e88e5;
}
}
/* Drag handle */
.drag-handle {
position: absolute;
top: 50%;
left: -14px;
transform: translateY(-50%);
opacity: 0;
transition: opacity 0.2s;
width: 18px;
height: 36px;
background: #eee;
border-radius: 4px 0 0 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: grab;
.section-card:hover & {
opacity: 1;
}
&:active {
cursor: grabbing;
}
}
/* Empty sections state */
.empty-sections {
display: flex;
justify-content: center;
align-items: center;
min-height: 100px;
border: 2px dashed #ddd;
border-radius: 6px;
}
.add-first-section {
background: #f5f5f5;
border: 1px solid #ddd;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
&:hover {
background: #eee;
}
}
/* CDK drag styles */
.cdk-drag-preview {
box-shadow: 0 5px 15px rgba(0,0,0,0.3) !important;
opacity: 0.8;
border: 2px solid #2196f3;
}
.cdk-drag-placeholder {
opacity: 0.3;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.sections-container.cdk-drop-list-dragging .section-card:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
/* Responsive adjustments */
@media (max-width: 768px) {
.action-bar {
flex-direction: column;
height: auto;
padding: 10px;
gap: 10px;
}
.left-controls, .right-controls {
width: 100%;
justify-content: space-between;
}
.visual-editor-container {
height: calc(100vh - 110px);
}
}
/* Global modal styles (outside visual-editor scope) */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
z-index: 1000;
}
.navbar-popup-content {
.modal-content {
background: white;
padding: 20px;
width: 500px;
max-width: 90%;
border-radius: 8px;
width: 400px;
max-width: 90%;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.modal-header {
padding: 16px;
border-bottom: 1px solid #eee;
}
.modal-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.modal-body {
padding: 16px;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
margin-bottom: 6px;
font-weight: 500;
}
.form-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
.modal-footer {
padding: 16px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
}
/* Section editing styles */
.edit-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.edit-container {
background: white;
border-radius: 8px;
width: 80%;
max-width: 1000px;
height: 80%;
max-height: 800px;
display: flex;
flex-direction: column;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.edit-header {
padding: 16px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.edit-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.edit-content {
flex: 1;
padding: 16px;
overflow: auto;
}
.editable-html {
border: 1px solid #ddd;
border-radius: 4px;
padding: 16px;
min-height: 100%;
outline: none;
}
.edit-footer {
padding: 16px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
}

View File

@ -70,19 +70,33 @@ export class ApiRequestService {
//return Observable.throw(error.message);
}
get(url: string, urlParams?: HttpParams): Observable<any> {
// get(url: string, urlParams?: HttpParams): Observable<any> {
// let me = this;
// return this.http
// .get(this.appConfig.baseApiPath + url, {
// headers: this.getHeaders(),
// params: urlParams,
// }).pipe(
// catchError((error) => {
// return throwError(error || "Server error");
// })
// )
// }
get(url: string, urlParams?: HttpParams, responseType: 'json' | 'text' = 'json'): Observable<any> {
let me = this;
return this.http
.get(this.appConfig.baseApiPath + url, {
return this.http.get(this.appConfig.baseApiPath + url, {
headers: this.getHeaders(),
params: urlParams,
responseType: responseType as any, // 👈 force-cast because Angular types are strict
}).pipe(
catchError((error) => {
return throwError(error || "Server error");
})
)
);
}
loginAuthentication(url: string, body: Object): Observable<any> {
let me = this;
return this.http