This commit is contained in:
string 2025-04-26 16:40:32 +05:30
parent 25270f4772
commit efed275353
13 changed files with 933 additions and 222 deletions

View File

@ -368,8 +368,27 @@
<div class="clr-col-12">
<h5>Customer Details:</h5>
<p><strong>Name:</strong> {{ rowSelected.name }}</p>
<p><strong>Phone:</strong> {{ rowSelected.phone }}</p>
<p><strong>Active:</strong> {{ rowSelected.active }}</p>
<p><strong>Visa Entry Type:</strong> {{ rowSelected.visa_entry_typename }}</p>
<p><strong>Visa Duration:</strong> {{ rowSelected.visa_durationname }}</p>
<p><strong>Visa Processing:</strong> {{ rowSelected.visa_processingname }}</p>
<p><strong>Travel Start Date:</strong> {{ rowSelected.travel_start_date }}</p>
<p><strong>Travel End Date:</strong> {{ rowSelected.travel_end_date }}</p>
<p><strong>Passport Number:</strong> {{ rowSelected.passport_number }}</p>
<p><strong>Passport Issue Date:</strong> {{ rowSelected.passport_issue_date }}</p>
<p><strong>Passport Expiry Date:</strong> {{ rowSelected.passport_expiry_date }}</p>
<p><strong>Email:</strong> {{ rowSelected.email }}</p>
<p><strong>Phone Number:</strong> {{ rowSelected.phone_number }}</p>
<p><strong>Birth Place:</strong> {{ rowSelected.birth_place }}</p>
<p><strong>Date of Birth:</strong> {{ rowSelected.date_of_birth }}</p>
<p><strong>Gender:</strong> {{ rowSelected.gendername }}</p>
<p><strong>Profession:</strong> {{ rowSelected.profession }}</p>
<p><strong>Visa Cost:</strong> {{ rowSelected.visa_cost }}</p>
<p><strong>Referrer:</strong> {{ rowSelected.referrername }}</p>
<p><strong>Nationality:</strong> {{ rowSelected.nationalityname }}</p>
<p><strong>Supplier:</strong> {{ rowSelected.suppliername }}</p>
<p><strong>Agent:</strong> {{ rowSelected.agentname }}</p>
</div>
</div>

View File

@ -112,8 +112,26 @@ export class EditstepperComponent implements OnInit {
supplier: [null],
agent: [null],
});
this.getallvisa_entry_type();
this.getallvisa_duration();
this.getallvisa_processing();
this.getallgender();
this.getallreferrer();
this.getallnationality();
this.getallsupplier();
this.getallagent();
}
// Change to Horizontal Layout
@ -207,6 +225,8 @@ export class EditstepperComponent implements OnInit {
this.toastr.error("Not Added");
}
});
this.rowSelected = this.entryForm.value;
setTimeout(() => {
this.ngOnInit();
}, 500);
@ -330,11 +350,51 @@ export class EditstepperComponent implements OnInit {
}
}
updategender(gender: string): void {
this.entryForm.get('gender').setValue(gender);
}
updategenderEdit(gender: string): void { this.rowSelected.gender = gender }
rsModaldescription = false;
goToReplaceStringdescription(row) {
this.rowSelected = row; this.rsModaldescription = true;
}
selectvisa_entry_type;
getallvisa_entry_type() {
this.mainService.getAllvisa_entry_type().subscribe(data => {
this.selectvisa_entry_type = data;
console.log(data);
}, (error) => { console.log(error); });
}
selectvisa_duration;
getallvisa_duration() {
this.mainService.getAllvisa_duration().subscribe(data => {
this.selectvisa_duration = data;
console.log(data);
}, (error) => { console.log(error); });
}
selectvisa_processing;
getallvisa_processing() {
this.mainService.getAllvisa_processing().subscribe(data => {
this.selectvisa_processing = data;
console.log('visa processing ', data);
}, (error) => { console.log(error); });
}
isValidemail(email: string): boolean {
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailPattern.test(email);
@ -349,13 +409,14 @@ export class EditstepperComponent implements OnInit {
updategender(gender: string): void {
this.entryForm.get('gender').setValue(gender);
selectgender;
getallgender() {
this.mainService.getAllgender().subscribe(data => {
this.selectgender = data;
console.log(data);
}, (error) => { console.log(error); });
}
updategenderEdit(gender: string): void { this.rowSelected.gender = gender }
//currency field start
@ -366,4 +427,37 @@ export class EditstepperComponent implements OnInit {
this.rowSelected.visa_cost = this.rowSelected.visa_cost?.replace(/,/g, '');
}
//currency field end
selectreferrer;
getallreferrer() {
this.mainService.getAllreferrer().subscribe(data => {
this.selectreferrer = data;
console.log(data);
}, (error) => { console.log(error); });
}
selectnationality;
getallnationality() {
this.mainService.getAllnationality().subscribe(data => {
this.selectnationality = data;
console.log(data);
}, (error) => { console.log(error); });
}
selectsupplier;
getallsupplier() {
this.mainService.getAllsupplier().subscribe(data => {
this.selectsupplier = data;
console.log(data);
}, (error) => { console.log(error); });
}
selectagent;
getallagent() {
this.mainService.getAllagent().subscribe(data => {
this.selectagent = data;
console.log(data);
}, (error) => { console.log(error); });
}
}

View File

@ -19,9 +19,6 @@
<clr-icon shape="plus"></clr-icon>ADD
</button>
<button id="add" class="btn btn-primary" (click)="gotositebuilder()">
<clr-icon shape="plus"></clr-icon>SiteBuilder
</button>
</div>
</div>
<ng-container *ngIf="!isCardview"> <!-- GET ALL --> <clr-datagrid [clrDgLoading]="loading"

View File

@ -197,49 +197,6 @@ export class Visa_applicationComponent implements OnInit {
onUpdate(id) {
this.modalEdit = false;
//console.log("in update");
console.log("id " + id);
console.log(this.rowSelected);
@ -255,48 +212,6 @@ export class Visa_applicationComponent implements OnInit {
}, 500);
}, (error) => {
console.log(error);
if (error.status >= 200 && error.status <= 299) {
@ -463,9 +378,6 @@ export class Visa_applicationComponent implements OnInit {
// updateaction
gotositebuilder() {
this.router.navigate(["/cns-portal/SiteBuilder"]);
}
}

View File

@ -8,7 +8,7 @@ import { environment } from 'src/environments/environment';
})
export class SiteTreeservice {
private baseURL = "SiteTree/SiteTree";
private dlfbaseURL = environment.builderUrl+"/entityBuilder";
private dlfbaseURL = environment.builderUrl + "/entityBuilder";
private nodeURL = environment.nodeUrl;
@ -52,7 +52,7 @@ export class SiteTreeservice {
}
createHtmlPages(data: any, projId: number): Observable<any> {
return this.apiRequest.post(`sureops/createFile?projId=` + projId, data);
return this.apiRequest.post(`sureops/deploy?projId=` + projId, data);
}

View File

@ -49,6 +49,8 @@
<!-- 🔁 Regenerate Button at top of wireframe page -->
<!-- part 2 code -->
<!--
<div style="text-align: right; margin: 10px 0;">
<button (click)="regenerateAllModifiedSections()"
style="background: orange; color: white; border: none; padding: 8px 14px; border-radius: 4px; cursor: pointer;">
@ -73,11 +75,121 @@
<button (click)="copyToClipboard(pageName)">📋</button>
<button (click)="downloadHtml(pageName)">⬇️</button>
</div>
</div>
</div> -->
<!-- ✅ Render whole page in one block -->
<div class="canvas-section">
<!-- <div class="canvas-section">
<div [innerHTML]="pageSections[pageName]['FullPage']"></div>
</div>
</div>
</div>
</div> -->
<!-- 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;">
♻️ Regenerate Modified
</button>
<button (click)="uploadHtmlFiles(37388)" style="background: green; color: white; border: none; padding: 8px 14px; border-radius: 4px; cursor: pointer;">
💾 Save All HTML Files
</button>
</div>
<div>
<!-- Global Style Controls -->
<select [(ngModel)]="liveStyles.fontSize">
<option value="">Font Size</option>
<option *ngFor="let size of ['12px', '14px', '16px', '18px', '20px', '24px', '28px']" [value]="size">{{ size }}</option>
</select>
<select [(ngModel)]="liveStyles.fontWeight">
<option value="">Font Weight</option>
<option *ngFor="let weight of ['normal', 'bold', 'lighter']" [value]="weight">{{ weight }}</option>
</select>
<select [(ngModel)]="liveStyles.textAlign">
<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" />
<button (click)="applyStyleToSelection()" style="background: #555; color: white; padding: 6px 10px; border-radius: 4px;">🎨 Apply Style</button>
</div>
</div>
<!-- Navbar Link Manager -->
<!-- <div *ngIf="selectedNavbarPage" class="navbar-link-manager">
<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>
<button (click)="addLink(selectedNavbarPage)"> Add Link</button>
<button (click)="applyNavbarChanges(selectedNavbarPage)">✅ Apply Changes</button>
</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>
</div>
</div>

View File

@ -64,3 +64,29 @@
border-radius: 6px;
background: #fff;
}
.editable-html:focus {
outline: 2px solid #007bff;
background: #fcfcfc;
}
.navbar-popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.navbar-popup-content {
background: white;
padding: 20px;
width: 500px;
max-width: 90%;
border-radius: 8px;
}

View File

@ -5,24 +5,31 @@ import { ActivatedRoute } from '@angular/router';
import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service';
import { COMMON_CSS } from './common-css';
import { ToastrService } from 'ngx-toastr';
import * as sha256 from 'crypto-js/sha256';
import SHA256 from 'crypto-js/sha256';
import { trigger, transition, style, animate, state } from '@angular/animations';
// Make sure path is correct
@Component({
selector: 'app-wireframe-renderer',
templateUrl: './wireframe-renderer.component.html',
styleUrls: ['./wireframe-renderer.component.scss']
styleUrls: ['./wireframe-renderer.component.scss'],
})
export class WireframeRendererComponent implements OnInit {
@Input() node: any;
@Input() title: string = '';
commonCss: string = COMMON_CSS;
jsonInput = '';
initialPrompt = '';
id: number;
allPagePrompts: Record<string, Record<string, string>> = {};
initialGeneratedPrompts: Record<string, Record<string, string>> = {};
promptHashCache: Record<string, string> = {};
pageSections: Record<string, { FullPage: SafeHtml }> = {};
constructor(
@ -30,9 +37,6 @@ export class WireframeRendererComponent implements OnInit {
private route: ActivatedRoute,
private sanitizer: DomSanitizer,
private toastr: ToastrService,
) { }
ngOnInit(): void {
@ -42,6 +46,7 @@ export class WireframeRendererComponent implements OnInit {
this.fetchTreeById(this.id);
}
// wireframe-renderer.component.ts (add this helper method)
getKeys(obj: any): string[] {
return Object.keys(obj || {});
@ -78,14 +83,9 @@ export class WireframeRendererComponent implements OnInit {
}
});
}
// all prompt in one map
allPagePrompts: Record<string, Record<string, string>> = {};
initialGeneratedPrompts: Record<string, Record<string, string>> = {};
// Recursively walk the tree and generate prompts for each section
// Step 2
async generatePromptsForAllSections(treeJsonString: string) {
try {
@ -98,14 +98,14 @@ export class WireframeRendererComponent implements OnInit {
}
// Step 3
async processPages(treeJson: any) {
pageRenderOrder: string[] = []; // 🚀 New array to hold correct page order
async processPages(treeJson: any) {
console.log('tree is : ', treeJson)
for (const [pageName, sections] of Object.entries(treeJson)) {
this.pageRenderOrder.push(pageName); // ✅ Track the order of pages
// child page prompt map
const sectionPrompts: Record<string, string> = {};
if (sections['Children']) {
// 👇 Recursively handle child pages
console.log(' child is : ', sections['Children'])
@ -113,7 +113,6 @@ export class WireframeRendererComponent implements OnInit {
await this.processPages(sections['Children']); // ✅ Fix: recursive + awaited
}
// Loop other sections
for (const [sectionName, sectionDescription] of Object.entries(sections)) {
@ -123,9 +122,6 @@ export class WireframeRendererComponent implements OnInit {
const prompt = await this.generatePromptFromSection(sectionName, sectionDescription); // ✅ await
sectionPrompts[sectionName] = prompt;
// console.log( sectionName, ': ', prompt)
}
// 🧠 Store all prompts for that page
@ -133,7 +129,6 @@ export class WireframeRendererComponent implements OnInit {
this.initialGeneratedPrompts[pageName] = JSON.parse(JSON.stringify(sectionPrompts)); // Deep clone
}
console.log('✅ FINAL PROMPT MAP:', this.allPagePrompts);
await this.processAllPagesLiveUpdate()
@ -174,124 +169,120 @@ HTML Only. No CSS.
Use placeholders for image/icon blocks where appropriate (e.g., <div style="width:100px;height:30px;background:#ccc;"></div>)`;
// Step 4
async generatePromptFromSection(
sectionName: string,
sectionDescription: string,
headerId: number = 35,
operationType: string = 'template 1',
operationType: string = 'template 1'
): Promise<string> {
return new Promise((resolve) => {
// const fieldType = sectionName.toLowerCase().replace(/\s+/g, '').replace(/section$/, '').trim();
const fieldType = sectionName
.toLowerCase()
.replace(/section$/i, '') // remove trailing 'section'
.replace(/\s{2,}/g, ' ') // multiple spaces to single space
.trim(); // trim leading/trailing spaces
const fieldType = sectionName.toLowerCase().replace(/section$/i, '').trim();
this.siteTreeService.getDlf(headerId, operationType, fieldType).subscribe({
next: async (res) => {
try {
const jsonBlock = res?.javacode;
if (!jsonBlock) return resolve('{}');
// console.log( fieldType , ' json is : ', jsonBlock)
const baseJson = typeof jsonBlock === 'string' ? JSON.parse(jsonBlock) : jsonBlock;
const baseJsonString = JSON.stringify(baseJson, null, 2);
if (jsonBlock) {
const parsedJson = typeof jsonBlock === 'string' ? JSON.parse(jsonBlock) : jsonBlock;
const enhancedPrompt = `
🎯 Enhance the content of this section using the following context.
🧠 Instructions:
- You are given an existing JSON structure for a UI section.
- Your job is to update ONLY the content-related fields like "text", "title", "label", "description", and "placeholder".
- You must NOT change the structure, hierarchy, tags, keys, class names, or styling values.
- You must NOT add or remove any keys or elements.
- You must return ONLY the updated JSON structure in valid format.
📄 Page Prompt:
"${this.initialPrompt}"
📦 Section Name:
"${sectionName}"
📘 Section Description:
"${sectionDescription}"
📌 Existing JSON (modify this only):
${baseJsonString}
🚫 Do NOT include:
- Any explanation, commentary, or markdown
- Any prefix like "Here is the updated JSON"
- Any new elements that were not already present
Output Requirement:
Only return the updated JSON structure nothing else.
`;
// const updatedJson = this.replaceContentRecursively(parsedJson, sectionDescription);
const finalJsonString = JSON.stringify(parsedJson, null, 2);
// 🧠 Build prompt to LLM with section info + json + full page prompt
const enhancedPrompt = `
📘 You are an intelligent layout enhancer.
Your task is to update and fill in the values of a given UI section JSON based on the following information:
const hash = sha256(enhancedPrompt).toString();
- Page-level prompt: "${this.initialPrompt || ''}"
- Section name: "${sectionName}"
- Section description: "${sectionDescription}"
You must only update relevant values like "title", "text", "description", "label", "placeholder", etc.
DO NOT change the structure. Only update content values.
JSON:
${finalJsonString}
Return only the updated JSON. Do not return any explanation.
`;
// console.log('enhanced query ', enhancedPrompt);
// const finalJson = await this.calLlm(enhancedPrompt);
resolve(finalJsonString);
} else {
resolve('{}');
if (this.promptHashCache[hash]) {
return resolve(this.promptHashCache[hash]);
}
// const enhanced = await this.callLlm(enhancedPrompt);
// this.promptHashCache[hash] = enhanced;
resolve(baseJsonString);
} catch (err) {
console.error('❌ JSON parse error:', err);
resolve('{}');
}
},
error: (err) => {
console.error('❌ API error:', err);
resolve('{}');
}
error: () => resolve('{}')
});
});
}
cachedPromptHashes: Record<string, Record<string, string>> = {};
sectionHtmls: Record<string, Record<string, string>> = {};
// Step 1 - Generate SHA-256 Hash
getPromptHash(prompt: string): string {
return SHA256(prompt).toString();
}
// Step 2 - Enhance per section with Hash Check + LLM call in Parallel
async processAllPagesLiveUpdate() {
const htmlGenerationTasks: Promise<void>[] = [];
for (const [pageName, sectionMap] of Object.entries(this.allPagePrompts)) {
htmlGenerationTasks.push(this.processPageSections(pageName, sectionMap));
}
await Promise.all(htmlGenerationTasks);
}
async processPageSections(pageName: string, sectionMap: Record<string, string>) {
const sectionHtmls: string[] = [];
const htmlTasks = Object.entries(sectionMap).map(async ([sectionName, jsonPrompt]) => {
const html = await this.generateJson({ sectionType: sectionName, jsonStructure: jsonPrompt });
sectionHtmls.push(html);
});
await Promise.all(htmlTasks);
const finalHtml = `<style>${COMMON_CSS}</style>\n${sectionHtmls.join('\n')}`;
const safeHtml = this.sanitizer.bypassSecurityTrustHtml(finalHtml);
this.pageSections[pageName] = { FullPage: safeHtml };
}
// replaceContentRecursively(obj: any, replacement: string): any {
// if (Array.isArray(obj)) {
// return obj.map(item => this.replaceContentRecursively(item, replacement));
// } else if (typeof obj === 'object' && obj !== null) {
// const updated = {};
// for (const [key, value] of Object.entries(obj)) {
// // Replace specific fields if they are strings
// if (['description', 'text', 'label', 'title', 'subtext'].includes(key.toLowerCase()) && typeof value === 'string') {
// updated[key] = replacement;
// } else {
// updated[key] = this.replaceContentRecursively(value, replacement);
// }
// }
// return updated;
// }
// return obj;
// }
pageHtmlMap: Record<string, string> = {};
generatedPages: { name: string, html: string }[] = [];
pageSections: Record<string, { FullPage: SafeHtml }> = {}; // final UI map
// Step 5 - generate html code via java Api through prompt
async processAllPagesLiveUpdate() {
for (const [pageName, sectionMap] of Object.entries(this.allPagePrompts)) {
const sectionHtmls: string[] = [];
for (const [sectionName, promptText] of Object.entries(sectionMap)) {
console.log(`🚀 Generating HTML for: ${pageName} > ${sectionName}`);
const response = await this.generateJson({ sectionType: sectionName, jsonStructure: promptText });
const html = response || '⚠️ No response received.';
sectionHtmls.push(html);
}
// ✅ Inject common CSS once per page
const htmlWithCss = `
<style>${COMMON_CSS}</style>
${sectionHtmls.join('\n')}`;
// ✅ Bypass Angular sanitizer
const safeHtml: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(htmlWithCss);
this.pageSections[pageName] = { FullPage: safeHtml };
}
}
// Step 6
async generateJson(data: { jsonStructure: any, sectionType: string }): Promise<string> {
return new Promise((resolve) => {
@ -310,7 +301,7 @@ ${finalJsonString}
this.siteTreeService.generateHtml(payload).subscribe({
next: (res) => {
console.log('✅ Response from HTML API:', res);
console.log('✅ Response from HTML API ', payload.sectionType, ' : ', res);
if (res && res.msg) {
resolve(res.msg);
} else {
@ -318,12 +309,12 @@ ${finalJsonString}
}
},
error: (err) => {
console.error('❌ HTML API Error:', err);
console.error(payload.sectionType, '❌ HTML API Error:', err);
resolve('⚠️ HTML API call failed.');
}
});
} catch (err) {
console.error('❌ JSON parsing error before sending to API:', err);
console.error(data.sectionType, '❌ JSON parsing error before sending to API:', err);
resolve('⚠️ Invalid JSON structure.');
}
});
@ -469,7 +460,7 @@ ${finalJsonString}
// // Step 6
async calLlm(data): Promise<string> {
async callLlm(data): Promise<string> {
return new Promise((resolve, reject) => {
console.log('call Llm Start ')
const payload = {
@ -492,6 +483,337 @@ ${finalJsonString}
});
}
activeEditTarget: string | null = null;
liveStyles = {
background: '',
color: '',
fontSize: '',
padding: '',
margin: '',
fontWeight: '',
textAlign: '',
};
selectEditable(pageName: string) {
this.activeEditTarget = pageName;
}
applyLiveStyles() {
const target = document.querySelector(`[data-target='${this.activeEditTarget}']`) as HTMLElement;
if (target) {
Object.keys(this.liveStyles).forEach((prop) => {
if (this.liveStyles[prop]) {
target.style[prop] = this.liveStyles[prop];
}
});
}
this.toastr.success(`🎨 Style updated for: ${this.activeEditTarget}`);
}
// 🔁 Track raw edits
rawEdits: Record<string, string> = {};
selectedElementSelector = '';
styleMap: Record<string, any> = {
'hero-header': { 'background': '#fdfdfd', 'text-align': 'center' },
'btn-filled': { 'background': '#000', 'color': '#fff' },
'hero-heading': { 'font-size': '36px', 'font-weight': 'bold' },
// Add all classes you want to allow editing
};
getAllCssSelectors(): string[] {
return Object.keys(this.styleMap);
}
applyCssFromMap() {
this.updateLiveStyles();
}
updateLiveStyles() {
const styleSheetId = 'dynamic-css-style';
let styleEl = document.getElementById(styleSheetId) as HTMLStyleElement;
if (!styleEl) {
styleEl = document.createElement('style');
styleEl.id = styleSheetId;
document.head.appendChild(styleEl);
}
let css = '';
for (const selector in this.styleMap) {
css += `.${selector} {`;
for (const prop in this.styleMap[selector]) {
css += `${prop}: ${this.styleMap[selector][prop]};`;
}
css += '}';
}
styleEl.innerHTML = css;
}
onEdit(pageName: string, event: any) {
const newHtml = event.target.innerHTML;
this.pageSections[pageName]['FullPage'] = this.sanitizer.bypassSecurityTrustHtml(newHtml);
}
applyStyleToSelection() {
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) return;
const range = selection.getRangeAt(0);
const selectedText = range.extractContents();
const span = document.createElement('span');
// Apply styles
if (this.liveStyles.color) span.style.color = this.liveStyles.color;
if (this.liveStyles.background) span.style.backgroundColor = this.liveStyles.background;
if (this.liveStyles.fontSize) span.style.fontSize = this.liveStyles.fontSize;
if (this.liveStyles.fontWeight) span.style.fontWeight = this.liveStyles.fontWeight;
if (this.liveStyles.textAlign) span.style.textAlign = this.liveStyles.textAlign;
span.appendChild(selectedText);
range.insertNode(span);
selection.removeAllRanges();
}
// new today 25 april
// New Variables
selectedNavbarPage: string = '';
navbarLinks: Record<string, { label: string, href: string }[]> = {};
availablePages: { label: string, href: string }[] = [];
// Select Navbar Section
selectNavbar(pageName: string) {
const html = this.pageSections[pageName]['FullPage']?.toString() || '';
if (html.includes('class="nav-1"') || html.includes('navbar')) {
this.selectedNavbarPage = pageName;
this.extractNavbarLinks(pageName, html);
} else {
alert('No Navbar found on this page.');
}
}
// Extract links from navbar HTML
extractNavbarLinks(pageName: string, html: string) {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
const navLinks = Array.from(tempDiv.querySelectorAll('.nav-1 li a'));
this.navbarLinks[pageName] = navLinks.map(link => ({
label: link.textContent?.trim() || '',
href: link.getAttribute('href') || '#'
}));
// Also prepare available pages list
this.availablePages = Object.keys(this.pageSections).map(p => ({
label: p,
href: `${p.replace(/\s+/g, '-').toLowerCase()}.html`
}));
}
// Add new link
addLink(pageName: string) {
this.navbarLinks[pageName].push({ label: '', href: '#' });
}
// Remove a link
removeLink(pageName: string, index: number) {
this.navbarLinks[pageName].splice(index, 1);
}
// Apply Changes to navbar
applyNavbarChanges(pageName: string) {
const links = this.navbarLinks[pageName] || [];
const html = this.pageSections[pageName]?.FullPage?.toString() || '';
if (!html) return;
// Build new <ul class="nav-1"> from links
const ulHtml = `<ul class="nav-1">` + links.map(link => {
return `<li><a href="${link.href}">${link.label}</a></li>`;
}).join('') + `</ul>`;
// Replace all instances of <ul class="nav-1">...</ul>
const updatedHtml = html.replace(/<ul class="nav-1"([\s\S]*?)<\/ul>/g, ulHtml);
console.log(' new html ', updatedHtml);
const safe = this.sanitizer.bypassSecurityTrustHtml(updatedHtml);
this.pageSections[pageName] = { FullPage: safe };
this.toastr.success('✅ Navbar updated for all nav-1 instances!');
}
// applyNavbarChanges(pageName: string) {
// const links = this.navbarLinks[pageName] || [];
// const htmlContent = this.pageSections[pageName]?.FullPage?.toString() || '';
// if (!htmlContent) return;
// // Create a temporary container to parse and manipulate the HTML
// const wrapper = document.createElement('div');
// wrapper.innerHTML = htmlContent;
// // Find all navbar UL elements
// const navMenus = wrapper.querySelectorAll('ul.nav-1');
// if (navMenus.length === 0) {
// console.warn('No navigation menus found with class "nav-1"');
// return;
// }
// // Update each navigation menu found
// navMenus.forEach(ul => {
// // step 1 clear the old vals in html
// ul.innerHTML = '';
// // creating a new one and appending
// links.forEach(link => {
// const listItem = document.createElement('li');
// const anchor = document.createElement('a');
// // Set attributes safely using setAttribute
// anchor.setAttribute('href', link.href);
// anchor.textContent = link.label;
// listItem.appendChild(anchor);
// ul.appendChild(listItem);
// });
// });
// // Get the updated HTML content
// const updatedHtml = wrapper.innerHTML;
// // Sanitize and update the page content
// const safeHtml = this.sanitizer.bypassSecurityTrustHtml(updatedHtml);
// this.pageSections[pageName] = { FullPage: safeHtml };
// this.toastr.success('✅ Navbar updated for all nav-1 instances!');
// }
// // Step 4
// async generatePromptFromSection(
// sectionName: string,
// sectionDescription: string,
// headerId: number = 35,
// operationType: string = 'template 1',
// ): Promise<string> {
// return new Promise((resolve) => {
// // const fieldType = sectionName.toLowerCase().replace(/\s+/g, '').replace(/section$/, '').trim();
// const fieldType = sectionName
// .toLowerCase()
// .replace(/section$/i, '') // remove trailing 'section'
// .replace(/\s{2,}/g, ' ') // multiple spaces to single space
// .trim(); // trim leading/trailing spaces
// this.siteTreeService.getDlf(headerId, operationType, fieldType).subscribe({
// next: async (res) => {
// try {
// const jsonBlock = res?.javacode;
// if (!jsonBlock) {
// resolve('{}');
// return;
// }
// // console.log( fieldType , ' json is : ', jsonBlock)
// const parsedJson = typeof jsonBlock === 'string' ? JSON.parse(jsonBlock) : jsonBlock;
// // const updatedJson = this.replaceContentRecursively(parsedJson, sectionDescription);
// const formattedJson = JSON.stringify(parsedJson, null, 2);
// // 🧠 Build prompt to LLM with section info + json + full page prompt
// // // 🧠 LLM enhancement: Content update without changing structure
// const smartPrompt = `
// 🔁 Enhance this JSON content while keeping the structure exactly same.
// Section Name: ${sectionName}
// Section Description: ${sectionDescription}
// Full Page Prompt: ${this.initialPrompt || ''}
// ✅ Replace or fill only content fields like: "title", "description", "text", "label", "placeholder", "heading", etc.
// ❌ Do NOT change or restructure keys, nesting, or CSS class names.
// ❌ Do NOT remove or add new blocks unless it's strictly a content array (like testimonials or FAQs).
// JSON Input:
// ${formattedJson}
// ⬇️ Return only the updated JSON with better content.
// `;
// // console.log('enhanced query ', smartPrompt);
// // // const finalJson = await this.calLlm(enhancedPrompt);
// // resolve(formattedJson);
// const enhancedJson = await this.calLlm(smartPrompt);
// // Return directly the enhanced JSON
// resolve(enhancedJson || formattedJson);
// } catch (err) {
// console.error('❌ JSON parse error:', err);
// resolve('{}');
// }
// },
// error: (err) => {
// console.error('❌ API error:', err);
// resolve('{}');
// }
// });
// });
// }
// // Step 5 - generate html code via java Api through prompt
// async processAllPagesLiveUpdate() {
// for (const [pageName, sectionMap] of Object.entries(this.allPagePrompts)) {
// const sectionHtmls: string[] = [];
// for (const [sectionName, promptText] of Object.entries(sectionMap)) {
// console.log(`🚀 Generating HTML for: ${pageName} > ${sectionName}`);
// const response = await this.generateJson({ sectionType: sectionName, jsonStructure: promptText });
// const html = response || '⚠️ No response received.';
// sectionHtmls.push(html);
// }
// // ✅ Inject common CSS once per page
// const htmlWithCss = `
// <style>${COMMON_CSS}</style>
// ${sectionHtmls.join('\n')}`;
// // ✅ Bypass Angular sanitizer
// const safeHtml: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(htmlWithCss);
// this.pageSections[pageName] = { FullPage: safeHtml };
// }
// }
// replaceContentRecursively(obj: any, replacement: string): any {
// if (Array.isArray(obj)) {
// return obj.map(item => this.replaceContentRecursively(item, replacement));
// } else if (typeof obj === 'object' && obj !== null) {
// const updated = {};
// for (const [key, value] of Object.entries(obj)) {
// // Replace specific fields if they are strings
// if (['description', 'text', 'label', 'title', 'subtext'].includes(key.toLowerCase()) && typeof value === 'string') {
// updated[key] = replacement;
// } else {
// updated[key] = this.replaceContentRecursively(value, replacement);
// }
// }
// return updated;
// }
// return obj;
// }
// // Step 4
// generatePromptFromSection(

View File

@ -110,11 +110,10 @@
<!-- Popup Dialog -->
<div *ngIf="showPopup"
<!-- <div *ngIf="showPopup"
style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 1000;">
<div style="background: white; padding: 20px; width: 500px; border-radius: 8px; position: relative;">
<!-- CLOSE BUTTON -->
<button (click)="showPopup = false"
style="position: absolute; top: 10px; right: 10px; background: #ff4d4f; border: none; color: white; border-radius: 50%; width: 30px; height: 30px; font-size: 18px; cursor: pointer;">
×
@ -130,4 +129,39 @@
Generate
</button>
</div>
</div> -->
<!-- Popup Dialog with improved z-index and animations -->
<div *ngIf="showPopup" [@fadeIn] class="modal-overlay"
style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.75); display: flex; align-items: center; justify-content: center; z-index: 1000;">
<div class="chart-box" [@slideIn]
style="width: 600px; max-height: none; padding: 24px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); border-radius: 8px; border: none; transform-origin: center; background-color: snow">
<!-- Close button -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<h3 style="margin: 0; color: #333; font-weight: 500;">Generate Site Structure</h3>
<button (click)="showPopup = false" [@pulseOnHover]
style="background: transparent; border: none; color: #666; font-size: 24px; cursor: pointer; padding: 4px; height: 36px; width: 36px; border-radius: 50%; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; color: #f5f5f5;">
×
</button>
</div>
<p style="margin-bottom: 16px; color: #666;">Describe your website structure in plain language:</p>
<textarea [(ngModel)]="rawInputText" rows="8" [@focusBorder]
class="tag-input"
style="width: 100%; margin-bottom: 20px; font-family: inherit; resize: vertical; transition: border 0.3s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.05);"></textarea>
<div style="display: flex; justify-content: flex-end; gap: 12px;">
<button (click)="showPopup = false" [@buttonHover]
style="padding: 8px 16px; background: #f5f5f5; color: #333; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; transition: all 0.2s ease;">
Cancel
</button>
<button (click)="generateJson()" [@buttonHover]
style="padding: 8px 18px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">
Generate Structure
</button>
</div>
</div>
</div>

View File

@ -1,3 +1,146 @@
// .controls {
// margin-bottom: 20px;
// }
// .controls textarea {
// width: 100%;
// height: 200px;
// padding: 10px;
// font-size: 14px;
// font-family: monospace;
// border: 1px solid #ccc;
// border-radius: 4px;
// resize: vertical;
// box-sizing: border-box;
// margin-bottom: 10px;
// }
// .controls button {
// padding: 10px 20px;
// background-color: #1976d2;
// color: white;
// border: none;
// border-radius: 4px;
// cursor: pointer;
// }
// .controls button:hover {
// background-color: #1565c0;
// }
// .toolbar {
// display: flex;
// gap: 10px;
// align-items: flex-start;
// padding: 1rem;
// background-color: #f7f7f7;
// }
// textarea {
// width: 300px;
// height: 120px;
// font-family: monospace;
// padding: 10px;
// border-radius: 8px;
// border: 1px solid #ccc;
// }
// button {
// padding: 10px 20px;
// background-color: #3f51b5;
// color: white;
// border: none;
// border-radius: 6px;
// cursor: pointer;
// transition: background-color 0.3s ease;
// }
// button:hover {
// background-color: #303f9f;
// }
// mat-tab-group {
// margin: 1rem;
// }
// .tab-content {
// padding: 1rem;
// background-color: #fafafa;
// border: 1px solid #ddd;
// border-radius: 10px;
// min-height: 400px;
// }
// .active-tab {
// border-bottom: 2px solid #007bff;
// color: #007bff;
// }
// .tab-button {
// padding: 8px 16px;
// margin-right: 8px;
// background: #f1f1f1;
// border: none;
// border-bottom: 2px solid transparent;
// cursor: pointer;
// color: black; /* 👈 Text color black */
// font-weight: bold;
// }
// .tab-button.active {
// border-bottom: 2px solid #000;
// background: #e0e0e0;
// color: black; /* 👈 Ensure active tab also stays black */
// }
// .editor-container {
// position: relative; /* 🔑 Important to contain absolute button */
// max-width: 1200px;
// margin: 0 auto;
// font-family: 'Segoe UI', sans-serif;
// padding: 20px;
// background-color: #ffffff;
// }
// .style-panel {
// position: fixed;
// top: 100px;
// right: 20px;
// width: 220px;
// padding: 15px;
// background: white;
// border: 1px solid #ccc;
// border-radius: 6px;
// box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
// z-index: 9999;
// h4 {
// margin-bottom: 10px;
// }
// label {
// display: block;
// margin-bottom: 10px;
// font-size: 13px;
// }
// input {
// width: 100%;
// padding: 4px;
// margin-top: 2px;
// }
// button {
// margin-right: 8px;
// padding: 6px 12px;
// }
// }
.controls {
margin-bottom: 20px;
}
@ -104,4 +247,20 @@ mat-tab-group {
padding: 20px;
background-color: #ffffff;
}
.modal-overlay *{
z-index: 1000;
}
:host {
--zoom-level: 1;
--primary-color: #2196f3;
--spacing-unit: 8px;
}
:root {
--level: 0;
--primary-color: #2196f3;
--border-color: #e2e8f0;
}

View File

@ -3,17 +3,55 @@ import { treeVisualizerService } from './tree-visualizer.service';
import { SiteTreeservice } from './SiteBuilderGrid/SiteTree.service';
import { ActivatedRoute } from '@angular/router';
import { ViewChild } from '@angular/core';
import { trigger, transition, style, animate, state } from '@angular/animations';
// Make sure path is correct
@Component({
selector: 'app-tree-visualizer',
templateUrl: './tree-visualizer.component.html',
styleUrls: ['./tree-visualizer.component.scss']
styleUrls: ['./tree-visualizer.component.scss'],
animations: [
trigger('fadeIn', [
transition(':enter', [
style({ opacity: 0 }),
animate('200ms ease-out', style({ opacity: 1 }))
]),
transition(':leave', [
animate('200ms ease-in', style({ opacity: 0 }))
])
]),
trigger('slideIn', [
transition(':enter', [
style({ transform: 'scale(0.9)', opacity: 0 }),
animate('300ms ease-out', style({ transform: 'scale(1)', opacity: 1 }))
]),
transition(':leave', [
animate('200ms ease-in', style({ transform: 'scale(0.9)', opacity: 0 }))
])
]),
trigger('buttonHover', [
state('void', style({ transform: 'translateY(0)' })),
state('hover', style({ transform: 'translateY(-2px)', boxShadow: '0 4px 8px rgba(0,0,0,0.2)' }))
]),
trigger('focusBorder', [
state('void', style({ borderColor: '#ccc' })),
state('focus', style({ borderColor: '#1976d2', boxShadow: '0 0 0 2px rgba(25,118,210,0.2)' }))
]),
trigger('pulseOnHover', [
state('void', style({ backgroundColor: 'transparent' })),
state('hover', style({ backgroundColor: 'rgba(0,0,0,0.05)' }))
])
]
})
export class TreeVisualizerComponent {
activeTab: 'sitemap' | 'wireframe' = 'sitemap';
showPopup: boolean = false;
openPopup() {
this.showPopup = true;
}
inputJson: string = `{
"Login": {
"Navbar": {},
@ -75,9 +113,7 @@ after click this icard inside we upload excel of student data, icard template, a
this.fetchTreeById(this.id);
}
openPopup() {
this.showPopup = true;
}
convertJson() {
try {