-
{{ section.type }}
-
{{ getSectionContentPreview(section.content) }}
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ template.type }}
+
{{ template.description }}
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
{{ section.type }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0">
+
+
+
+
+
+
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts
index 0eea0aa..221e529 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/TreeNode/tree-node.component.ts
@@ -1,9 +1,9 @@
-import { Component, Input } from '@angular/core';
-import { OnInit } from '@angular/core';
-import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
-import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service';
+import { Component, ViewChild, ElementRef, OnInit, OnDestroy, AfterViewInit, HostListener } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
-
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
+import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service';
interface Section {
type: string;
@@ -11,13 +11,11 @@ interface Section {
}
interface Page {
+ id: string; // Unique identifier for each page
name: string;
sections: Section[];
children?: Page[];
-}
-
-interface SiteStructure {
- pages: Page[];
+ parent?: Page; // Reference to parent for easier navigation
}
@Component({
@@ -25,593 +23,798 @@ interface SiteStructure {
templateUrl: './tree-node.component.html',
styleUrls: ['./tree-node.component.css']
})
-export class TreeNodeComponent implements OnInit {
-
- @Input() node: any;
- showJsonInput = true;
- jsonInput = '';
- errorMessage = '';
- siteStructure: SiteStructure = { pages: [] };
+export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit {
+ // Canvas properties
+ @ViewChild('canvasContainer') canvasContainer!: ElementRef;
+ private destroy$ = new Subject
();
+
+ // Grid movement properties
+ private isDraggingCanvas = false;
+ private startX = 0;
+ private startY = 0;
+ private panning = false;
+
+ // Zoom properties
+ currentScale = 1;
+ private maxScale = 2;
+ private minScale = 0.5;
+ private scaleFactor = 0.1;
+
+ // Grid position
+ gridTranslateX = 0;
+ gridTranslateY = 0;
+
+ // Site structure data
rootPage: Page | null = null;
selectedPage: Page | null = null;
selectedSection: Section | null = null;
editMode: 'page' | 'section' | null = null;
- currentOutputJson = '';
-
- // Form controls
- newSectionType = '';
- newSectionContent = '';
+ siteId: number = 0;
+ loading = false;
+ hasUnsavedChanges = false;
+
+ // Section management
+ availableSectionTypes: string[] = [];
+ showOffcanvas = false;
+ currentParentPage: Page | null = null;
+ customSectionType = '';
+ customSectionContent = '';
+
+ // Adding child pages
+ showAddChildModal = false;
newPageName = '';
- showAddChildForm = false;
- id: number;
+ currentParentForChild: Page | null = null;
+
+ // For drag and drop
+ draggedPage: Page | null = null;
+ draggedSection: Section | null = null;
+ dropTargetPage: Page | null = null;
+ currentSectionIndex: number = -1;
+
+
+ // Section templates with better descriptions
+ sectionTemplates = [
+ {
+ type: 'Hero Header Section',
+ description: 'Main banner with headline and CTA',
+ content: 'Introduce the company and its mission, with a strong call-to-action to sign up or learn more.'
+ },
+ {
+ type: 'Feature Section',
+ description: 'Highlight product features with icons',
+ content: 'Highlight the core features of the platform, such as project creation, technology selection, and wireframing.'
+ },
+ {
+ type: 'Features List Section',
+ description: 'Bulleted list of key features',
+ content: 'Secure JWT-based login, Easy project management, and Technology choices for frontend and backend.'
+ },
+ {
+ type: 'Benefits Section',
+ description: 'Explain the benefits to users',
+ content: 'Detail the benefits of using our platform including time savings, quality improvement, and better outcomes.'
+ },
+ {
+ type: 'How It Works Section',
+ description: 'Step-by-step process explanation',
+ content: 'Explain the process of using our platform from signup to completion.'
+ },
+ {
+ type: 'Testimonial Section',
+ description: 'Customer quotes and reviews',
+ content: 'Showcase customer testimonials to build trust and credibility.'
+ },
+ {
+ type: 'CTA Section',
+ description: 'Call-to-action buttons and form',
+ content: 'Encourage visitors to sign up or contact for more information.'
+ },
+ {
+ type: 'Gallery Section',
+ description: 'Image gallery showcase',
+ content: 'Display portfolio items, project screenshots, or other visual content.'
+ },
+ {
+ type: 'Navbar',
+ description: 'Top navigation bar',
+ content: 'Site navigation with links to main sections.'
+ },
+ {
+ type: 'Footer',
+ description: 'Bottom site footer',
+ content: 'Contact information, links, and copyright details.'
+ }
+ ];
constructor(
private siteTreeService: SiteTreeservice,
- private route: ActivatedRoute,
-
- ) { }
-
+ private route: ActivatedRoute
+ ) {}
+
ngOnInit(): void {
- this.id = this.route.snapshot.params['id'];
-
- console.log(' id is : ', this.id)
-
- this.fetchTreeById(this.id);
+ this.siteId = this.route.snapshot.params['id'];
+ this.fetchSiteTree();
}
- // Will be populated dynamically from incoming JSON
- availableSectionTypes: string[] = [];
-
-
- getSections(data: any): { title: string, description: string }[] {
- const sections: { title: string, description: string }[] = [];
- for (const key in data) {
- if (key !== 'Children' && typeof data[key] === 'string') {
- sections.push({ title: key, description: data[key] });
- }
- if (key !== 'Children' && Array.isArray(data[key])) {
- sections.push({ title: key, description: data[key].join('\n') });
- }
+
+ ngAfterViewInit(): void {
+ this.initCanvasControls();
+
+ // Set up touch events for mobile
+ const canvas = this.canvasContainer.nativeElement;
+ canvas.addEventListener('touchstart', this.handleTouchStart.bind(this));
+ canvas.addEventListener('touchmove', this.handleTouchMove.bind(this));
+ canvas.addEventListener('touchend', this.handleTouchEnd.bind(this));
+
+ // Center grid after initial load
+ setTimeout(() => {
+ this.centerGrid();
+ }, 500);
+ }
+
+ ngOnDestroy(): void {
+ // Auto-save changes before component destruction
+ if (this.hasUnsavedChanges) {
+ this.saveTreeData(true);
}
- return sections;
+
+ this.destroy$.next();
+ this.destroy$.complete();
}
-
- getChildren(data: any): any[] {
- if (data.Children) {
- return Object.keys(data.Children).map((key) => ({
- title: key,
- data: data.Children[key]
- }));
- }
- return [];
- }
-
-
- // Code by harsh
-
-
-
-
- fetchTreeById(id: number) {
- this.siteTreeService.getById(id).subscribe({
- next: (res) => {
- console.log('📥 Fetched from getById:', res);
-
- // Check if model key exists and has some value
- if (res && res.model) {
- try {
- // Optional: if model is already a stringified JSON
- const maybeParsed = typeof res.model === 'string' ? JSON.parse(res.model) : res.model;
- this.jsonInput = JSON.stringify(maybeParsed, null, 2);
- this.parseJsonInput(); // convert it immediately
- } catch (err) {
- console.error('❌ Error parsing model JSON:', err);
- alert('Model data is not valid JSON format');
- }
- } else {
- console.warn('⚠️ Model key not found or empty, keeping inputJson as is.');
- }
- },
- error: (err) => {
- console.error('❌ Error in getById:', err);
- alert('Failed to fetch data from server');
+
+ // GRID MOVEMENT METHODS
+
+ initCanvasControls(): void {
+ const canvas = this.canvasContainer.nativeElement;
+
+ // Mouse down event for dragging
+ canvas.addEventListener('mousedown', (e: MouseEvent) => {
+ // Only initiate drag if not clicking on interactive elements
+ if ((e.target as HTMLElement).closest('.node-card, .node-header, button, input, select, textarea, .modal, .editor-form, .section-card, .add-button')) {
+ return;
}
+
+ this.panning = true;
+ this.startX = e.clientX - this.gridTranslateX;
+ this.startY = e.clientY - this.gridTranslateY;
+ canvas.style.cursor = 'grabbing';
+ e.preventDefault();
});
- }
-
-
- parseJsonInput(): void {
- try {
- const parsedData = JSON.parse(this.jsonInput);
-
- // Transform the nested JSON format to our page structure
- if (parsedData && typeof parsedData === 'object') {
- this.siteStructure = { pages: [] };
- this.rootPage = {
- name: 'Primary Sitemap',
- sections: [],
- children: []
- };
-
- // Collect all section types dynamically
- this.availableSectionTypes = [];
- this.availableSectionTypes.push('callToAction');
-
-
- // Process the root level objects (Home, etc.)
- Object.keys(parsedData).forEach(pageName => {
- const pageData = parsedData[pageName];
- const page: Page = {
- name: pageName,
- sections: this.extractSections(pageData),
- children: []
- };
-
- // Process children if they exist
- if (pageData.Children) {
- Object.keys(pageData.Children).forEach(childName => {
- const childData = pageData.Children[childName];
- const childPage: Page = {
- name: childName,
- sections: this.extractSections(childData),
- children: []
- };
-
- // Process grandchildren if they exist
- if (childData.Children) {
- Object.keys(childData.Children).forEach(grandchildName => {
- const grandchildData = childData.Children[grandchildName];
- const grandchildPage: Page = {
- name: grandchildName,
- sections: this.extractSections(grandchildData),
- };
- childPage.children?.push(grandchildPage);
- });
- }
-
- page.children?.push(childPage);
- });
- }
-
- this.rootPage?.children?.push(page);
- });
-
- this.siteStructure.pages.push(this.rootPage);
- this.showJsonInput = false;
- this.errorMessage = '';
-
- // Generate initial output JSON
- this.updateOutputJson();
- } else {
- this.errorMessage = 'Invalid JSON structure';
- }
- } catch (e) {
- this.errorMessage = 'Invalid JSON format: ' + (e as Error).message;
- }
- }
-
- // Extract sections from page data and collect section types
- extractSections(pageData: any): Section[] {
- const sections: Section[] = [];
-
- Object.keys(pageData).forEach(key => {
- // Skip the Children key as it's not a section
- if (key !== 'Children') {
- const value = pageData[key];
-
- // Add to available section types if not already included
- if (!this.availableSectionTypes.includes(key)) {
- this.availableSectionTypes.push(key);
- }
-
- // Handle array of sections
- if (Array.isArray(value)) {
- sections.push({
- type: key,
- content: value.join('\n')
- });
- }
- // Handle object sections with empty content
- else if (typeof value === 'object' && value !== null) {
- sections.push({
- type: key,
- content: JSON.stringify(value)
- });
- }
- // Handle string content
- else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
- sections.push({
- type: key,
- content: String(value)
- });
- }
- }
+
+ // Mouse move for dragging the grid
+ document.addEventListener('mousemove', (e: MouseEvent) => {
+ if (!this.panning) return;
+
+ // Calculate new position
+ const newX = e.clientX - this.startX;
+ const newY = e.clientY - this.startY;
+
+ // Update position
+ this.gridTranslateX = newX;
+ this.gridTranslateY = newY;
+ this.applyGridTransform();
});
-
- return sections;
+
+ // Mouse up to end dragging
+ document.addEventListener('mouseup', () => {
+ this.panning = false;
+ canvas.style.cursor = 'grab';
+ });
+
+ // Mouse wheel for zooming
+ canvas.addEventListener('wheel', (e: WheelEvent) => {
+ e.preventDefault();
+
+ // Calculate zoom based on wheel direction
+ const delta = e.deltaY < 0 ? this.scaleFactor : -this.scaleFactor;
+ const newScale = Math.min(this.maxScale, Math.max(this.minScale, this.currentScale + delta));
+
+ // Get mouse position relative to canvas
+ const rect = canvas.getBoundingClientRect();
+ const mouseX = e.clientX - rect.left;
+ const mouseY = e.clientY - rect.top;
+
+ // Calculate zoom around mouse position
+ this.zoomAtPoint(newScale, mouseX, mouseY);
+ }, { passive: false });
}
-
- resetEditor(): void {
- this.rootPage = null;
- this.siteStructure = { pages: [] };
- this.selectedPage = null;
- this.selectedSection = null;
- this.editMode = null;
- this.showJsonInput = true;
- this.jsonInput = '';
- this.errorMessage = '';
- this.availableSectionTypes = [];
- this.currentOutputJson = '';
+
+ // Touch event handlers for mobile devices
+ handleTouchStart(e: TouchEvent): void {
+ if (e.touches.length !== 1) return;
+
+ const touch = e.touches[0];
+ this.panning = true;
+ this.startX = touch.clientX - this.gridTranslateX;
+ this.startY = touch.clientY - this.gridTranslateY;
+ e.preventDefault();
}
-
- updateOutputJson(): void {
- if (this.rootPage) {
- const output = this.transformToNestedJson();
- this.currentOutputJson = JSON.stringify(output, null, 2);
-
- console.log('updtae json output ', this.currentOutputJson);
-
- // 🔁 Now call update API
- const updatePayload = {
- model: this.currentOutputJson // model format expected by backend
- };
-
- console.log('payload : ', updatePayload);
- this.siteTreeService.update(this.id, updatePayload).subscribe({
- next: (resp) => {
- console.log('✅ JSON updated on server:', resp);
- },
- error: (err) => {
- console.error('❌ Failed to update JSON on server:', err);
- alert('Update to server failed');
- }
- });
+
+ handleTouchMove(e: TouchEvent): void {
+ if (!this.panning || e.touches.length !== 1) return;
+
+ const touch = e.touches[0];
+ this.gridTranslateX = touch.clientX - this.startX;
+ this.gridTranslateY = touch.clientY - this.startY;
+ this.applyGridTransform();
+ e.preventDefault();
+ }
+
+ handleTouchEnd(): void {
+ this.panning = false;
+ }
+
+ // Apply transform to the grid
+ applyGridTransform(): void {
+ const grid = document.querySelector('.sitemap-grid') as HTMLElement;
+ if (!grid) return;
+
+ grid.style.transform = `translate(${this.gridTranslateX}px, ${this.gridTranslateY}px) scale(${this.currentScale})`;
+ }
+
+ // Public method to get total transform for template binding
+ getTotalTransform(): string {
+ return `translate(${this.gridTranslateX}px, ${this.gridTranslateY}px) scale(${this.currentScale})`;
+ }
+
+ // Zoom at a specific point
+ zoomAtPoint(newScale: number, x: number, y: number): void {
+ if (newScale === this.currentScale) return;
+
+ // Calculate offset change to zoom at mouse position
+ const scaleFactor = newScale / this.currentScale;
+ const dx = x - x * scaleFactor;
+ const dy = y - y * scaleFactor;
+
+ // Apply new position and scale
+ this.gridTranslateX = this.gridTranslateX * scaleFactor + dx;
+ this.gridTranslateY = this.gridTranslateY * scaleFactor + dy;
+ this.currentScale = newScale;
+
+ this.applyGridTransform();
+ }
+
+ // Center the grid in the viewport
+ centerGrid(): void {
+ const canvas = this.canvasContainer.nativeElement;
+ const grid = document.querySelector('.tree-grid') as HTMLElement;
+ const rootNode = document.querySelector('.home-node') as HTMLElement;
+
+ if (!canvas || !grid) {
+ setTimeout(() => this.centerGrid(), 100);
+ return;
+ }
+
+ const canvasRect = canvas.getBoundingClientRect();
+ const gridRect = grid.getBoundingClientRect();
+
+ // Calculate scale to fit content within the fixed box
+ const scaleX = canvasRect.width / gridRect.width * 0.2;
+ const scaleY = canvasRect.height / gridRect.height * 0.2;
+ const scale = Math.min(Math.max(this.minScale, Math.min(scaleX, scaleY)), 1);
+
+ // Position the grid - centered horizontally, but align top of tree with top of viewport
+ this.currentScale = scale;
+ this.gridTranslateX = -700; // Set left position to -900px
+
+ // Position vertically to place root node near top with some padding
+ const topPadding = 50; // Pixels from top of viewport
+ if (rootNode) {
+ const rootRect = rootNode.getBoundingClientRect();
+ const rootOffsetY = (rootRect.top - gridRect.top) * 0.9;
+ this.gridTranslateY = topPadding - rootOffsetY;
} else {
- this.currentOutputJson = '';
+ // Fallback if root node not found
+ this.gridTranslateY = topPadding;
}
+
+ this.applyGridTransform();
}
-
- downloadJson(): void {
- if (this.rootPage) {
- const output = this.transformToNestedJson();
- const dataStr = JSON.stringify(output, null, 2);
- const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
-
- const exportName = 'site-structure.json';
- const linkElement = document.createElement('a');
- linkElement.setAttribute('href', dataUri);
- linkElement.setAttribute('download', exportName);
- linkElement.click();
- }
+
+ // ZOOM CONTROL METHODS
+
+ zoomIn(): void {
+ const canvas = this.canvasContainer.nativeElement;
+ const rect = canvas.getBoundingClientRect();
+ const centerX = rect.width / 2;
+ const centerY = rect.height / 2;
+
+ const newScale = Math.min(this.maxScale, this.currentScale + this.scaleFactor);
+ this.zoomAtPoint(newScale, centerX, centerY);
}
+
+ zoomOut(): void {
+ const canvas = this.canvasContainer.nativeElement;
+ const rect = canvas.getBoundingClientRect();
+ const centerX = rect.width / 2;
+ const centerY = rect.height / 2;
+
+ const newScale = Math.max(this.minScale, this.currentScale - this.scaleFactor);
+ this.zoomAtPoint(newScale, centerX, centerY);
+ }
+
+ resetZoom(): void {
+ this.centerGrid();
+ }
+
+ // TREE DATA METHODS
+
+ fetchSiteTree(): void {
+ this.loading = true;
+ this.siteTreeService.getById(this.siteId).subscribe({
+ next: (res) => {
+ this.loading = false;
+ // Parse and unwrap
+ const raw = typeof res.model === 'string' ? JSON.parse(res.model) : res.model;
+ const rootTitle = raw.title || 'Site Structure';
+ const pagesObj = raw.data?.Children || raw.Children || raw.data || raw;
+ this.processSiteData( pagesObj,rootTitle);
+ },
+ error: () => {
+ this.loading = false;
+ this.createDefaultStructure();
+ }
+ });
+ }
+
+ // Create a default structure
+ createDefaultStructure(): void {
+ const defaultData = {
+ "Home": {
+ "Navbar": {},
+ "Hero Header Section": "Introduce the company and its mission, with a strong call-to-action to sign up or learn more.",
+ "Feature Section": "Highlight the core features of the platform.",
+ "CTA Section": "Encourage visitors to sign up or contact for more information.",
+ "Footer": {}
+ }
+ };
+ this.processSiteData(defaultData);
+ }
+
+ processSiteData(pagesObj: any, rootTitle?: string): void {
+ // Initialize root with dynamic title
+ this.rootPage = { id: 'root', name: rootTitle, sections: [], children: [] };
+ this.availableSectionTypes = [];
+ this.hasUnsavedChanges = false;
+ let idCounter = 0;
- // Transform our page structure back to the nested JSON format
- transformToNestedJson(): any {
- const result: any = {};
-
- // Process only the first level (Home)
- if (this.rootPage && this.rootPage.children && this.rootPage.children.length > 0) {
- this.rootPage.children.forEach(page => {
- result[page.name] = {};
-
- // Add sections
- page.sections.forEach(section => {
- // Try to parse content as JSON if it looks like JSON
- if (section.content.trim().startsWith('{') && section.content.trim().endsWith('}')) {
- try {
- result[page.name][section.type] = JSON.parse(section.content);
- } catch (e) {
- result[page.name][section.type] = section.content || {};
+ // Recursive builder
+ const buildPages = (obj: any, parent?: Page): Page[] => {
+ return Object.keys(obj || {}).map(key => {
+ const pageData = obj[key] || {};
+ const page: Page = {
+ id: `page-${++idCounter}`,
+ name: key,
+ sections: [],
+ children: [],
+ parent
+ };
+ // Sections: all props except Children
+ Object.keys(pageData).forEach(prop => {
+ if (prop !== 'Children') {
+ const val = pageData[prop];
+ page.sections.push({
+ type: prop,
+ content: Array.isArray(val)
+ ? val.join('\n')
+ : typeof val === 'object'
+ ? JSON.stringify(val, null, 2)
+ : String(val)
+ });
+ if (!this.availableSectionTypes.includes(prop)) {
+ this.availableSectionTypes.push(prop);
}
}
- // Try to parse as array if it looks like array
- else if (section.content.trim().startsWith('[') && section.content.trim().endsWith(']')) {
- try {
- result[page.name][section.type] = JSON.parse(section.content);
- } catch (e) {
- result[page.name][section.type] = section.content || [];
- }
- }
- // Handle numeric values
- else if (!isNaN(Number(section.content)) && section.content.trim() !== '') {
- result[page.name][section.type] = Number(section.content);
- }
- // Handle boolean values
- else if (section.content.trim() === 'true' || section.content.trim() === 'false') {
- result[page.name][section.type] = section.content.trim() === 'true';
- }
- // Default to string
- else {
- result[page.name][section.type] = section.content || "";
- }
});
+ // Recurse into Children
+ if (pageData.Children) {
+ page.children = buildPages(pageData.Children, page);
+ }
+ return page;
+ });
+ };
- // Add children
- if (page.children && page.children.length > 0) {
- result[page.name]['Children'] = {};
-
- page.children.forEach(childPage => {
- result[page.name]['Children'][childPage.name] = {};
-
- // Add child sections
- childPage.sections.forEach(section => {
- // Try to parse content as JSON if it looks like JSON
- if (section.content.trim().startsWith('{') && section.content.trim().endsWith('}')) {
- try {
- result[page.name]['Children'][childPage.name][section.type] = JSON.parse(section.content);
- } catch (e) {
- result[page.name]['Children'][childPage.name][section.type] = section.content || {};
- }
- }
- // Try to parse as array if it looks like array
- else if (section.content.trim().startsWith('[') && section.content.trim().endsWith(']')) {
- try {
- result[page.name]['Children'][childPage.name][section.type] = JSON.parse(section.content);
- } catch (e) {
- result[page.name]['Children'][childPage.name][section.type] = section.content || [];
- }
- }
- // Handle numeric values
- else if (!isNaN(Number(section.content)) && section.content.trim() !== '') {
- result[page.name]['Children'][childPage.name][section.type] = Number(section.content);
- }
- // Handle boolean values
- else if (section.content.trim() === 'true' || section.content.trim() === 'false') {
- result[page.name]['Children'][childPage.name][section.type] = section.content.trim() === 'true';
- }
- // Default to string
- else {
- result[page.name]['Children'][childPage.name][section.type] = section.content || "";
- }
+ this.rootPage.children = buildPages(pagesObj, this.rootPage);
+ setTimeout(() => this.centerGrid(), 100);
+ }
+
+ createPageFromData(pageName: string, pageData: any, idPrefix: string): Page {
+ const page: Page = {
+ id: idPrefix,
+ name: pageName,
+ sections: [],
+ children: []
+ };
+
+ // Extract sections
+ if (pageData) {
+ // Process properties as sections
+ Object.keys(pageData).forEach(key => {
+ if (key !== 'Children') {
+ const value = pageData[key];
+
+ // Add to available section types if not already included
+ if (!this.availableSectionTypes.includes(key)) {
+ this.availableSectionTypes.push(key);
+ }
+
+ // Handle different value types
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
+ // Object value
+ page.sections.push({
+ type: key,
+ content: Object.keys(value).length > 0 ? JSON.stringify(value, null, 2) : ''
});
-
- // Add grandchildren
- if (childPage.children && childPage.children.length > 0) {
- result[page.name]['Children'][childPage.name]['Children'] = {};
-
- childPage.children.forEach(grandchildPage => {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name] = {};
-
- // Add grandchild sections
- grandchildPage.sections.forEach(section => {
- // Try to parse content as JSON if it looks like JSON
- if (section.content.trim().startsWith('{') && section.content.trim().endsWith('}')) {
- try {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = JSON.parse(section.content);
- } catch (e) {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content || {};
- }
- }
- // Try to parse as array if it looks like array
- else if (section.content.trim().startsWith('[') && section.content.trim().endsWith(']')) {
- try {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = JSON.parse(section.content);
- } catch (e) {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content || [];
- }
- }
- // Handle numeric values
- else if (!isNaN(Number(section.content)) && section.content.trim() !== '') {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = Number(section.content);
- }
- // Handle boolean values
- else if (section.content.trim() === 'true' || section.content.trim() === 'false') {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content.trim() === 'true';
- }
- // Default to string
- else {
- result[page.name]['Children'][childPage.name]['Children'][grandchildPage.name][section.type] = section.content || "";
- }
- });
- });
- }
- });
+ } else if (Array.isArray(value)) {
+ // Array value
+ page.sections.push({
+ type: key,
+ content: value.join('\n')
+ });
+ } else {
+ // String or primitive value
+ page.sections.push({
+ type: key,
+ content: String(value)
+ });
+ }
}
});
+
+ // Process children recursively if the Children key exists
+ if (pageData.Children) {
+ let childId = 0;
+ Object.keys(pageData.Children).forEach(childName => {
+ const childData = pageData.Children[childName];
+ const childPage = this.createPageFromData(childName, childData, `${idPrefix}-child-${++childId}`);
+ childPage.parent = page;
+ page.children?.push(childPage);
+ });
+ }
}
-
- return result;
+
+ return page;
}
-
+
+ // Generate output JSON from tree structure
+ generateOutputJson(): any {
+ const output: any = {};
+
+ if (this.rootPage?.children) {
+ this.rootPage.children.forEach(page => {
+ output[page.name] = this.convertPageToJsonObject(page);
+ });
+ }
+
+ return output;
+ }
+
+ convertPageToJsonObject(page: Page): any {
+ const pageObj: any = {};
+
+ // Add sections
+ page.sections.forEach(section => {
+ try {
+ // Check if content is an array (line breaks)
+ if (section.content.includes('\n')) {
+ pageObj[section.type] = section.content.split('\n');
+ } else {
+ // Try to parse JSON content for objects
+ const parsedContent = JSON.parse(section.content);
+ pageObj[section.type] = parsedContent;
+ }
+ } catch (e) {
+ // If parsing fails, use the raw content
+ pageObj[section.type] = section.content;
+ }
+ });
+
+ // Add children
+ if (page.children && page.children.length > 0) {
+ pageObj.Children = {};
+
+ page.children.forEach(childPage => {
+ pageObj.Children[childPage.name] = this.convertPageToJsonObject(childPage);
+ });
+ }
+
+ return pageObj;
+ }
+
+ // PAGE AND SECTION MANAGEMENT
+
selectPage(page: Page): void {
this.selectedPage = page;
this.selectedSection = null;
- this.editMode = 'page';
}
-
+
selectSection(section: Section): void {
this.selectedSection = section;
this.editMode = 'section';
}
-
- addSectionToPage(page: Page): void {
- if (this.newSectionType && page) {
- // Create new section type if it doesn't exist
- if (!this.availableSectionTypes.includes(this.newSectionType)) {
- this.availableSectionTypes.push(this.newSectionType);
- }
-
- page.sections.push({
- type: this.newSectionType,
- content: this.newSectionContent || ""
- });
-
- this.newSectionType = '';
- this.newSectionContent = '';
-
- // Update JSON output
- this.updateOutputJson();
- }
- }
-
- updatePage(): void {
- // Already updated via ngModel binding
- this.updateOutputJson();
- }
-
- updateSection(): void {
- // Already updated via ngModel binding
- this.updateOutputJson();
- }
-
- addChildPage(parentPage: Page): void {
- if (!this.newPageName) return;
-
- if (!parentPage.children) {
- parentPage.children = [];
- }
-
- parentPage.children.push({
- name: this.newPageName,
- sections: []
- });
-
+
+ // Show modal to add a child page
+ showAddChildPageModal(parent: Page): void {
+ this.currentParentForChild = parent;
this.newPageName = '';
- this.updateOutputJson();
+ this.showAddChildModal = true;
}
-
- addSectionType(): void {
- if (this.newSectionType && !this.availableSectionTypes.includes(this.newSectionType)) {
- this.availableSectionTypes.push(this.newSectionType);
+
+ // Add a child page
+ addChildPage(): void {
+ if (this.currentParentForChild && this.newPageName) {
+ if (!this.currentParentForChild.children) {
+ this.currentParentForChild.children = [];
+ }
+
+ const newPage: Page = {
+ id: `page-${Date.now()}`,
+ name: this.newPageName,
+ sections: [],
+ children: [],
+ parent: this.currentParentForChild
+ };
+
+ this.currentParentForChild.children.push(newPage);
+ this.hasUnsavedChanges = true;
+ this.closeAddChildModal();
+
+ // Auto-select the new page
+ this.selectPage(newPage);
}
}
-
- deletePage(page: Page, parentPage: Page | null): void {
- if (parentPage && parentPage.children) {
- const index = parentPage.children.indexOf(page);
- if (index > -1) {
- parentPage.children.splice(index, 1);
-
- // Reset selection if deleting selected page
+
+ closeAddChildModal(): void {
+ this.showAddChildModal = false;
+ this.currentParentForChild = null;
+ this.newPageName = '';
+ }
+
+ // Delete a page
+ deletePage(page: Page, event: Event): void {
+ event.stopPropagation();
+
+ if (!page.parent || !page.parent.children) return;
+
+ if (confirm(`Are you sure you want to delete "${page.name}" and all its children?`)) {
+ const index = page.parent.children.indexOf(page);
+ if (index !== -1) {
+ page.parent.children.splice(index, 1);
+
if (this.selectedPage === page) {
this.selectedPage = null;
this.editMode = null;
}
-
- this.updateOutputJson();
+
+ this.hasUnsavedChanges = true;
}
}
}
-
- deleteSection(section: Section, page: Page): void {
- const index = page.sections.indexOf(section);
- if (index > -1) {
- page.sections.splice(index, 1);
-
- // Reset selection if deleting selected section
- if (this.selectedSection === section) {
- this.selectedSection = null;
- this.editMode = null;
+
+ // Format section content as HTML with colored text
+ getSectionContentHtml(content: string): string {
+ if (!content) return '';
+
+ // Replace line breaks with
for proper display
+ return content.replace(/\n/g, '
');
+ }
+
+ // Show add section offcanvas
+ openSectionOffcanvas(page: Page, sectionIndex: number = -1, event?: Event): void {
+ if (event) {
+ event.stopPropagation();
+ }
+ this.currentParentPage = page;
+ this.currentSectionIndex = sectionIndex;
+ this.customSectionType = '';
+ this.customSectionContent = '';
+ this.showOffcanvas = true;
+ }
+
+ // Close the offcanvas
+ closeOffcanvas(): void {
+ this.showOffcanvas = false;
+ this.currentParentPage = null;
+ this.currentSectionIndex = -1;
+ }
+
+ // Add section from a template
+ addSectionFromTemplate(template: any): void {
+ if (this.currentParentPage) {
+ const newSection: Section = {
+ type: template.type,
+ content: template.content
+ };
+
+ // Insert at specific position if provided, otherwise add to end
+ if (this.currentSectionIndex >= 0) {
+ this.currentParentPage.sections.splice(this.currentSectionIndex + 1, 0, newSection);
+ } else {
+ this.currentParentPage.sections.push(newSection);
}
-
- this.updateOutputJson();
+
+ if (!this.availableSectionTypes.includes(template.type)) {
+ this.availableSectionTypes.push(template.type);
+ }
+
+ this.hasUnsavedChanges = true;
+ this.closeOffcanvas();
}
}
-
- onPageDrop(event: CdkDragDrop, parentPage: Page): void {
- if (parentPage.children) {
- moveItemInArray(parentPage.children, event.previousIndex, event.currentIndex);
- this.updateOutputJson();
+
+ // Add custom section
+
+// Similar update for addCustomSection
+addCustomSection(): void {
+ if (this.currentParentPage && this.customSectionType) {
+ const newSection: Section = {
+ type: this.customSectionType,
+ content: this.customSectionContent || ''
+ };
+
+ // Insert at specific position if provided, otherwise add to end
+ if (this.currentSectionIndex >= 0) {
+ this.currentParentPage.sections.splice(this.currentSectionIndex + 1, 0, newSection);
+ } else {
+ this.currentParentPage.sections.push(newSection);
+ }
+
+ if (!this.availableSectionTypes.includes(this.customSectionType)) {
+ this.availableSectionTypes.push(this.customSectionType);
+ }
+
+ this.hasUnsavedChanges = true;
+ this.closeOffcanvas();
+ }
+}
+
+ // Edit section
+ editSection(section: Section, event: Event): void {
+ event.stopPropagation();
+ this.selectedSection = section;
+ this.editMode = 'section';
+ }
+
+ // Cancel edit
+ cancelEdit(): void {
+ this.editMode = null;
+ this.selectedSection = null;
+ }
+
+ // Save edit
+ saveEdit(): void {
+ this.editMode = null;
+ this.hasUnsavedChanges = true;
+ }
+
+ // Delete section
+ deleteSection(section: Section, page: Page, event: Event): void {
+ event.stopPropagation();
+
+ if (confirm(`Are you sure you want to delete this "${section.type}" section?`)) {
+ const index = page.sections.indexOf(section);
+ if (index !== -1) {
+ page.sections.splice(index, 1);
+
+ if (this.selectedSection === section) {
+ this.selectedSection = null;
+ this.editMode = null;
+ }
+
+ this.hasUnsavedChanges = true;
+ }
}
}
-
- onSectionDrop(event: CdkDragDrop, page: Page): void {
- moveItemInArray(page.sections, event.previousIndex, event.currentIndex);
- this.updateOutputJson();
+
+ // DRAG AND DROP METHODS
+
+ // Handle page drag start
+ onPageDragStart(page: Page): void {
+ this.draggedPage = page;
}
-
- getParentPage(childPage: Page | null): Page | null {
- if (!this.rootPage || !childPage) return null;
-
- // Check all levels recursively
- return this.findParentPageRecursive(this.rootPage, childPage);
+
+ // Handle page drag end
+ onPageDragEnd(): void {
+ this.draggedPage = null;
+ this.dropTargetPage = null;
}
-
- findParentPageRecursive(currentPage: Page, targetPage: Page): Page | null {
- if (!currentPage.children) return null;
-
- // Check direct children
- if (currentPage.children.includes(targetPage)) {
- return currentPage;
+
+ // Handle page drop
+ onPageDrop(targetPage: Page): void {
+ if (!this.draggedPage || this.draggedPage === targetPage) return;
+
+ // Remove from current parent
+ if (this.draggedPage.parent && this.draggedPage.parent.children) {
+ const sourceIndex = this.draggedPage.parent.children.indexOf(this.draggedPage);
+ if (sourceIndex > -1) {
+ this.draggedPage.parent.children.splice(sourceIndex, 1);
+ }
}
-
- // Check nested children
- for (const child of currentPage.children) {
- const result = this.findParentPageRecursive(child, targetPage);
- if (result) return result;
+
+ // Add to new parent
+ if (!targetPage.children) {
+ targetPage.children = [];
}
-
- return null;
+
+ // Update parent reference
+ this.draggedPage.parent = targetPage;
+ targetPage.children.push(this.draggedPage);
+
+ this.hasUnsavedChanges = true;
}
-
- canAddChildPage(page: Page | null): boolean {
- if (!page) return false;
-
- // Root can have unlimited children in dynamic mode
- if (page === this.rootPage) {
- return true;
+
+ // Handle section drag drop
+ onSectionDrop(event: CdkDragDrop): void {
+ if (event.previousContainer === event.container) {
+ // Reordering within the same page
+ moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
+ } else {
+ // Moving to a different page
+ transferArrayItem(
+ event.previousContainer.data,
+ event.container.data,
+ event.previousIndex,
+ event.currentIndex
+ );
}
-
- // First level children can have children
- const pageLevel = this.getPageLevel(page);
- if (pageLevel === 1) {
- return true;
+
+ this.hasUnsavedChanges = true;
+ }
+
+ // UI HELPER METHODS
+
+ getNodeIcon(page: Page): string {
+ if (page.name.toLowerCase() === 'home') {
+ return 'fas fa-home';
+ } else if (page.children && page.children.length > 0) {
+ return 'fas fa-folder-open';
+ } else {
+ return 'fas fa-file';
}
-
- // Second level children can't have children
- return pageLevel < 2;
}
-
- getPageLevel(page: Page): number {
- if (page === this.rootPage) return 0;
-
- let currentPage = page;
- let level = 0;
-
- while (currentPage !== this.rootPage) {
- const parent = this.getParentPage(currentPage);
- if (!parent) break;
-
- level++;
- currentPage = parent;
+
+ // Save tree data
+ saveTreeData(silent: boolean = false): void {
+ const outputData = this.generateOutputJson();
+ const jsonData = JSON.stringify(outputData, null, 2);
+
+ if (this.siteId) {
+ this.loading = true;
+
+ this.siteTreeService.update(this.siteId, {
+ id: this.siteId,
+ model: jsonData
+ }).subscribe({
+ next: () => {
+ this.loading = false;
+ this.hasUnsavedChanges = false;
+
+ if (!silent) {
+ alert('Site structure saved successfully');
+ }
+ },
+ error: (err) => {
+ this.loading = false;
+ console.error('Error saving data:', err);
+
+ if (!silent) {
+ alert('Failed to save site structure');
+ }
+ }
+ });
}
-
- return level;
}
-
- getHomePageFromRoot(): Page | null {
- return this.rootPage?.children?.[0] || null;
- }
-
- getChildrenPages(parentPageName: string): Page[] {
- const parentPage = this.findPageByName(parentPageName);
- return parentPage?.children || [];
- }
-
- findPageByName(name: string): Page | null {
- if (!this.rootPage) return null;
-
- return this.findPageByNameRecursive(this.rootPage, name);
- }
-
- findPageByNameRecursive(currentPage: Page, name: string): Page | null {
- if (currentPage.name === name) return currentPage;
-
- if (!currentPage.children) return null;
-
- for (const child of currentPage.children) {
- const result = this.findPageByNameRecursive(child, name);
- if (result) return result;
+
+ @HostListener('window:beforeunload', ['$event'])
+ beforeUnloadHandler(event: BeforeUnloadEvent): void {
+ if (this.hasUnsavedChanges) {
+ event.preventDefault();
+ event.returnValue = 'You have unsaved changes. Are you sure you want to leave?';
}
-
- return null;
}
-
- hasChildPages(page: Page | null): boolean {
- return !!page?.children && page.children.length > 0;
+
+ @HostListener('window:resize')
+ onWindowResize(): void {
+ this.centerGrid();
}
-
- getSectionContentPreview(content: string): string {
- return content?.length > 50 ? content.slice(0, 50) + '...' : content || '';
- }
-}
\ No newline at end of file
+}
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip
new file mode 100644
index 0000000..a039f77
Binary files /dev/null and b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi.zip differ
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts
index 916fefa..e2e91af 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/common-css.ts
@@ -326,10 +326,24 @@ button:hover {
}
.testimonial-card {
- border: 1px solid #ddd;
- border-radius: 6px;
+ background: white;
padding: 20px;
- background: #fff;
+ border-radius: 12px;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
+ max-width: 320px;
+}
+.stars {
+ color: #f39c12;
+ font-size: 1.2rem;
+}
+.author {
+ margin-top: 15px;
+ text-align: center;
+}
+.author .avatar {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
}
.testimonial-card .profile {
@@ -346,6 +360,8 @@ button:hover {
margin: 10px 0;
}
+
+
/* ======= FOOTER ======= */
.footer-1 {
background: #f8f8f8;
@@ -434,5 +450,2051 @@ button {
text-decoration: none;
}
+.hero-section {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 40px;
+ padding: 40px;
+ background-color: #ffffff;
+ font-family: sans-serif;
+ flex-wrap: wrap;
+}
+
+.hero-left {
+ flex: 1;
+ min-width: 300px;
+}
+
+.hero-right {
+ flex: 1;
+ min-width: 300px;
+ display: flex;
+ justify-content: center;
+ background-color: #e5e5e5;
+}
+
+.hero-right-img {
+ width: 100%;
+ height: auto;
+}
+
+.hero-buttons {
+ margin-top: 20px;
+ display: flex;
+ gap: 16px;
+}
+
+.btn {
+ padding: 10px 20px;
+ font-weight: 500;
+ text-decoration: none;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+}
+
+.btn-filled {
+ background-color: #000;
+ color: #fff;
+}
+
+.btn-outlined {
+ background-color: #fff;
+ color: #000;
+ border: 1px solid #000;
+}
+
+.hero-centered {
+ background: #ffffff;
+ font-family: sans-serif;
+ text-align: center;
+ padding: 60px 20px;
+}
+
+.hero-heading {
+ font-size: 32px;
+ font-weight: bold;
+ color: #000000;
+ margin-bottom: 20px;
+}
+
+.hero-description {
+ font-size: 16px;
+ color: #333333;
+ max-width: 600px;
+ margin: 0 auto 30px;
+}
+
+.btn-filled {
+ padding: 10px 20px;
+ background-color: #000000;
+ color: #ffffff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ margin-right: 10px;
+}
+
+.btn-outlined {
+ padding: 10px 20px;
+ background-color: #ffffff;
+ color: #000000;
+ border: 1px solid #000000;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.hero-image {
+ width: 100%;
+ height: auto;
+ display: block;
+ margin-top: 40px;
+ background-color: #e5e5e5;
+}
+
+
+/* ======= HERO SECTION (Two Column) ======= */
+.feature-section {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ padding: 60px 40px;
+ gap: 40px;
+}
+
+.feature-content {
+ flex: 1;
+ min-width: 300px;
+}
+
+.feature-content h2 {
+ font-size: 36px;
+ font-weight: bold;
+ margin-bottom: 15px;
+}
+
+.feature-content p {
+ font-size: 16px;
+ color: #444;
+ margin-bottom: 20px;
+}
+
+.cta-buttons {
+ display: flex;
+ gap: 15px;
+ margin-top: 20px;
+ flex-wrap: wrap;
+}
+
+.cta-buttons a {
+ padding: 10px 20px;
+ border-radius: 6px;
+ text-decoration: none;
+ font-weight: 500;
+ transition: 0.3s ease;
+}
+
+.cta-buttons a:hover {
+ opacity: 0.9;
+}
+
+.feature-section img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 8px;
+ display: block;
+}
+
+/* ======= PARTNER LOGOS ======= */
+.feature-content .partners-logos {
+ display: flex;
+ gap: 10px;
+ margin-top: 12px;
+}
+
+.feature-content .partners-logos img {
+ height: 24px;
+ object-fit: contain;
+}
+
+/* ======= DECORATIONS ======= */
+.decoration {
+ width: 50px;
+ height: 50px;
+ position: absolute;
+ z-index: 0;
+ opacity: 0.4;
+}
+
+.decoration.circle {
+ border-radius: 50%;
+}
+
+.decoration.abstract-green {
+ clip-path: polygon(0 0, 100% 20%, 80% 100%, 0 100%);
+}
+
+.decoration.abstract-orange {
+ clip-path: polygon(10% 0%, 90% 0%, 100% 100%, 0% 100%);
+}
+
+/* ======= SCROLL INDICATOR ======= */
+.scroll-indicator {
+ font-size: 20px;
+ position: absolute;
+ animation: bounce 2s infinite;
+ color: #666;
+}
+
+.scroll-indicator.bottom-left {
+ bottom: 20px;
+ left: 20px;
+}
+
+.scroll-indicator.bottom-right {
+ bottom: 20px;
+ right: 20px;
+}
+
+@keyframes bounce {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(8px); }
+}
+
+.hero-left-cards {
+ background: radial-gradient(circle, #1a1f1c, #2d3430);
+ color: white;
+ font-family: sans-serif;
+ padding: 60px 40px;
+}
+
+.hero-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 40px;
+ align-items: center;
+}
+
+.hero-left {
+ flex: 1;
+ min-width: 300px;
+}
+
+.hero-logo {
+ width: 48px;
+ margin-bottom: 10px;
+}
+
+.hero-label {
+ font-size: 14px;
+ text-transform: uppercase;
+ color: #aaa;
+}
+
+.hero-heading {
+ font-size: 36px;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+.hero-right {
+ flex: 1;
+ display: grid;
+ gap: 20px;
+ min-width: 300px;
+}
+
+.hero-card {
+ background: #1c1f1e;
+ padding: 20px;
+ border-radius: 8px;
+ text-align: center;
+}
+
+.hero-card img.card-img {
+ width: 100%;
+ border-radius: 4px;
+ margin-bottom: 10px;
+}
+
+.card-buttons {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.btn-filled {
+ background: #506c4f;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 4px;
+ text-decoration: none;
+}
+
+.btn-outlined {
+ border: 1px solid #fff;
+ background: transparent;
+ color: white;
+ padding: 10px 20px;
+ border-radius: 4px;
+ text-decoration: none;
+}
+
+.hero-centered-art {
+ text-align: center;
+ background: #fff;
+ padding: 80px 20px;
+ position: relative;
+}
+
+.hero-centered-art .hero-heading {
+ font-size: 36px;
+ color: #333;
+ font-family: sans-serif;
+ max-width: 700px;
+ margin: 0 auto 30px;
+ line-height: 1.3;
+}
+
+.hero-centered-art .btn-teal {
+ background-color: #00b3b3;
+ color: white;
+ padding: 12px 24px;
+ border-radius: 4px;
+ text-decoration: none;
+ font-weight: 500;
+ display: inline-block;
+}
+
+.hero-art-left {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100px;
+}
+
+.hero-art-right {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 100px;
+}
+
+.subsection-centered {
+ text-align: center;
+ padding: 60px 20px;
+ max-width: 700px;
+ margin: 0 auto;
+ font-family: sans-serif;
+}
+
+.subsection-centered h2 {
+ font-size: 28px;
+ margin-bottom: 15px;
+}
+
+.subsection-centered p {
+ font-size: 16px;
+ color: #444;
+}
+
+.testimonial-section.simple-testimonial {
+ padding: 60px 20px;
+ text-align: center;
+ background: #fff;
+ color: #222;
+}
+
+.testimonial-logo img {
+ height: 40px;
+ margin-bottom: 20px;
+}
+
+.testimonial-quote {
+ font-size: 20px;
+ font-style: italic;
+ margin-bottom: 30px;
+ color: #444;
+ max-width: 800px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.testimonial-author {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+}
+
+.testimonial-author .author-img {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ background: #ccc;
+ object-fit: cover;
+}
+
+.testimonial-author .author-details {
+ font-size: 14px;
+ color: #555;
+ line-height: 1.4;
+}
+.testimonial-section.dark {
+ background: #111;
+ color: #fff;
+ padding: 60px 20px;
+ text-align: center;
+}
+
+.testimonial-section.dark .testimonial-heading {
+ font-size: 32px;
+ margin-bottom: 30px;
+}
+
+.testimonial-section.dark .testimonial-grid {
+ display: flex;
+ gap: 30px;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.testimonial-section.dark .testimonial-card {
+ background: #1a1a1a;
+ padding: 30px;
+ border-radius: 12px;
+ max-width: 350px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+}
+
+.testimonial-section.dark .stars {
+ font-size: 18px;
+ color: gold;
+ margin-bottom: 10px;
+}
+
+.testimonial-section.dark .author {
+ margin-top: 20px;
+}
+
+.testimonial-section.dark .author .avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ margin-bottom: 10px;
+}
+.client-testimonial-section {
+ padding: 60px 20px;
+ background-color: #111;
+ color: #fff;
+ font-family: sans-serif;
+ position: relative;
+ text-align: center;
+}
+
+.testimonial-bg {
+ position: absolute;
+ inset: 0;
+ background-size: cover;
+ background-position: center;
+ opacity: 0.1;
+ z-index: -1;
+}
+
+.testimonial-logo {
+ width: 80px;
+ margin-bottom: 20px;
+}
+
+.testimonial-title {
+ font-size: 32px;
+ margin-bottom: 40px;
+}
+
+.testimonial-cards {
+ display: flex;
+ gap: 30px;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.testimonial-card {
+ background: #222;
+ border-radius: 8px;
+ padding: 20px;
+ max-width: 300px;
+}
+
+.testimonial-card .avatar {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ object-fit: cover;
+}
+
+.testimonial-card blockquote {
+ font-style: italic;
+ margin: 20px 0;
+}
+
+.testimonial-card .author {
+ font-size: 14px;
+ color: #aaa;
+}
+
+.testimonial-card .stars {
+ color: gold;
+ font-size: 18px;
+}
+
+.testimonial-cta .btn-filled {
+ margin-top: 30px;
+ background: teal;
+ color: white;
+ padding: 12px 24px;
+ text-decoration: none;
+ border-radius: 4px;
+}
+
+.testimonial-contact p {
+ margin-top: 10px;
+}
+
+.testimonial-landing {
+ padding: 60px 20px;
+ text-align: center;
+ font-family: sans-serif;
+}
+
+.testimonial-landing.orange {
+ background-color: #fff3e0;
+ color: #bf360c;
+}
+
+.landing-title {
+ font-size: 32px;
+ margin-bottom: 10px;
+}
+
+.landing-description {
+ font-size: 16px;
+ margin-bottom: 40px;
+ max-width: 600px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.testimonial-grid {
+ display: flex;
+ gap: 30px;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.testimonial-box {
+ background: #ffffff;
+ border: 1px solid #eee;
+ padding: 20px;
+ border-radius: 8px;
+ max-width: 300px;
+ box-shadow: 0 0 5px rgba(0,0,0,0.05);
+}
+
+.testimonial-box.highlight {
+ background: #ffe0b2;
+ border-color: #ff9800;
+}
+
+.testimonial-box blockquote {
+ font-style: italic;
+ margin-bottom: 16px;
+}
+
+.author-name {
+ font-weight: bold;
+ font-size: 14px;
+}
+
+/* Team secction */
+.team-slider-section {
+ text-align: center;
+ padding: 40px;
+ font-family: sans-serif;
+}
+
+.team-slider-section h2 {
+ font-size: 32px;
+ margin-bottom: 10px;
+}
+
+.team-slider-section p {
+ color: gray;
+}
+
+.team-slider {
+ margin-top: 40px;
+ display: flex;
+ justify-content: center;
+ gap: 20px;
+ overflow-x: auto;
+ padding-bottom: 10px;
+}
+
+.team-member {
+ min-width: 300px;
+ border: 1px solid #ddd;
+ border-radius: 10px;
+ padding: 20px;
+ background: #fff;
+ flex-shrink: 0;
+ text-align: center;
+}
+
+.team-member img {
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ border-radius: 50%;
+ margin-bottom: 15px;
+}
+
+.team-member h4 {
+ margin: 10px 0 5px;
+}
+
+.team-member .social-icons a {
+ margin: 0 5px;
+ color: #555;
+ text-decoration: none;
+ font-size: 16px;
+}
+
+.team-member .profile-link {
+ color: #007BFF;
+ text-decoration: underline;
+ display: inline-block;
+ margin-top: 10px;
+}
+.team-section {
+ padding: 60px 20px;
+ background: #fff;
+ text-align: center;
+}
+.team-member {
+ display: inline-block;
+ margin: 20px;
+}
+.team-member img {
+ width: 120px;
+ height: 120px;
+ border-radius: 50%;
+}
+.team-member .name {
+ margin-top: 10px;
+ font-weight: bold;
+}
+.team-member .role {
+ color: #777;
+}
+
+/* === Team Showcase === */
+#team-showcase {
+ padding: 60px 20px;
+ text-align: center;
+ background-color: #fff;
+}
+#team-showcase .heading {
+ font-size: 2.5rem;
+ margin-bottom: 10px;
+}
+#team-showcase .subtitle {
+ color: #666;
+ margin-bottom: 40px;
+}
+#team-showcase .group-list {
+ display: flex;
+ flex-direction: column;
+ gap: 50px;
+}
+#team-showcase .group {
+ padding: 0 10px;
+}
+#team-showcase .group .groupName {
+ font-size: 1.6rem;
+ font-weight: 600;
+ margin-bottom: 20px;
+}
+#team-showcase .group .team-member-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+ gap: 20px;
+ justify-items: center;
+}
+.team-member {
+ text-align: center;
+}
+.team-member img {
+ width: 140px;
+ height: 140px;
+ object-fit: cover;
+ border-radius: 50%;
+ margin-bottom: 10px;
+}
+.team-member .name {
+ font-weight: bold;
+}
+.team-member .role {
+ font-size: 0.95rem;
+ color: #777;
+}
+.team-showcase {
+ padding: 60px 20px;
+ background: #f9f9f9;
+ text-align: center;
+}
+
+.team-showcase .group {
+ margin-bottom: 40px;
+}
+
+.team-showcase .group h3 {
+ font-size: 1.8rem;
+ margin-bottom: 20px;
+}
+
+.team-showcase .member-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: 20px;
+ justify-items: center;
+}
+
+.team-showcase .member {
+ background: white;
+ padding: 15px;
+ border-radius: 8px;
+ box-shadow: 0 0 10px rgba(0,0,0,0.05);
+ max-width: 200px;
+}
+
+.team-showcase .member img {
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ border-radius: 50%;
+ margin-bottom: 10px;
+}
+
+.team-showcase .member .name {
+ font-weight: bold;
+ margin-bottom: 4px;
+}
+
+.team-showcase .member .role {
+ font-size: 0.9rem;
+ color: #777;
+}
+
+
+/* === Team Grid Section === */
+#team-grid {
+ padding: 60px 20px;
+ background-color: #f9f9f9;
+ text-align: center;
+}
+#team-grid .tag {
+ text-transform: uppercase;
+ font-weight: 600;
+ color: #ff6600;
+ margin-bottom: 10px;
+}
+#team-grid .heading {
+ font-size: 2.2rem;
+ margin-bottom: 10px;
+}
+#team-grid .description {
+ font-size: 1.1rem;
+ color: #666;
+ margin-bottom: 40px;
+}
+#team-member-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 30px;
+ justify-items: center;
+}
+.team-member {
+ background: white;
+ padding: 20px;
+ border-radius: 12px;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.05);
+ max-width: 250px;
+ text-align: center;
+}
+.team-member img {
+ width: 100%;
+ height: auto;
+ border-radius: 8px;
+}
+.team-member .name {
+ font-weight: bold;
+ margin-top: 12px;
+}
+.team-member .description {
+ font-size: 0.95rem;
+ color: #555;
+ margin-top: 6px;
+}
+
+
+/* === FAQ === */
+.faq-section {
+ padding: 60px 20px;
+}
+.faq-title {
+ font-size: 2rem;
+ margin-bottom: 20px;
+}
+.faq-subtitle {
+ font-size: 1rem;
+ color: #666;
+ margin-bottom: 30px;
+}
+.faq-question {
+ font-weight: bold;
+ margin-top: 15px;
+}
+.faq-answer {
+ margin-bottom: 20px;
+ color: #444;
+}
+
+/* === Logo Showcase === */
+.logo-showcase {
+ padding: 60px 20px;
+ text-align: center;
+}
+.logo-showcase img {
+ height: 40px;
+ margin: 10px 20px;
+ opacity: 0.8;
+}
+
+/* === HEADER === */
+header[ref="header-1"] {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px 40px;
+ background: #fff;
+ border-bottom: 1px solid #eee;
+}
+.logo-1 {
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+.nav-1 {
+ display: flex;
+ gap: 20px;
+ list-style: none;
+}
+.nav-1 li a {
+ text-decoration: none;
+ color: #333;
+ font-weight: 500;
+}
+.button-join, .button {
+ background: black;
+ color: white;
+ padding: 8px 16px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+/* === HERO SECTION === */
+.hero-section {
+ text-align: center;
+ padding: 80px 20px;
+ background: #fff;
+}
+.hero-heading {
+ font-size: 2.5rem;
+ font-weight: bold;
+ margin-bottom: 20px;
+}
+.hero-paragraph {
+ font-size: 1.2rem;
+ margin-bottom: 30px;
+ max-width: 700px;
+ margin-inline: auto;
+}
+.button-group {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+}
+.button-group .button:first-child {
+ background-color: black;
+ color: white;
+}
+.button-group .button:last-child {
+ background-color: transparent;
+ border: 1px solid #222;
+ color: #222;
+}
+
+/* === IMAGE GRID === */
+.image-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 20px;
+ padding: 40px;
+ background: #fafafa;
+}
+.image-tile img {
+ width: 100%;
+ border-radius: 8px;
+ display: block;
+ box-shadow: 0 2px 6px rgba(0,0,0,0.1);
+}
+/* Header */
+header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px 40px;
+ background: #fff;
+ border-bottom: 1px solid #eee;
+}
+.logo-1 {
+ font-size: 20px;
+ font-weight: bold;
+}
+.nav-1 {
+ display: flex;
+ list-style: none;
+ gap: 20px;
+}
+.nav-1 li a {
+ text-decoration: none;
+ color: #333;
+ font-weight: 500;
+}
+.join-btn {
+ background: black;
+ color: white;
+ padding: 8px 16px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+/* Hero */
+.hero-section {
+ text-align: center;
+ padding: 80px 20px;
+ background: #f9f9f9;
+}
+.hero-heading {
+ font-size: 32px;
+ margin-bottom: 20px;
+}
+.hero-paragraph {
+ max-width: 600px;
+ margin: auto;
+ color: #555;
+}
+.button-group {
+ margin-top: 20px;
+}
+.button-group .join-btn {
+ margin-right: 10px;
+}
+
+/* Image Grid */
+.image-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ gap: 20px;
+ padding: 40px;
+}
+.image-tile img {
+ width: 100%;
+ border-radius: 8px;
+}
+/* ======= HERO SECTION CENTERED ======= */
+.hero-centered {
+ text-align: center;
+ padding: 60px 20px;
+ background: #fdfdfd;
+ max-width: 100%;
+}
+
+.hero-label {
+ font-size: 14px;
+ font-weight: 600;
+ color: #007bff;
+ margin-bottom: 10px;
+ display: block;
+}
+
+.hero-heading {
+ font-size: 32px;
+ font-weight: bold;
+ margin-bottom: 20px;
+ color: #222;
+}
+
+.hero-description {
+ font-size: 16px;
+ color: #555;
+ max-width: 700px;
+ margin: 0 auto 30px auto;
+ line-height: 1.6;
+}
+
+.hero-centered .btn {
+ font-size: 15px;
+ padding: 10px 20px;
+ border-radius: 4px;
+ text-decoration: none;
+ display: inline-block;
+ margin: 0 8px;
+ cursor: pointer;
+}
+
+/* Button Filled */
+.btn-filled {
+ background-color: black;
+ color: white;
+ border: none;
+}
+
+.btn-filled:hover {
+ background-color: #333;
+}
+
+/* Button Outlined */
+.btn-outlined {
+ border: 1px solid black;
+ background: transparent;
+ color: black;
+}
+
+.btn-outlined:hover {
+ background-color: black;
+ color: white;
+}
+
+.text-dark {
+ color: #333333;
+}
+.text-blue {
+ color: #3b82f6;
+}
+.site-info {
+ margin-left: 20px;
+}
+
+.site-info h2 {
+ font-size: 18px;
+ margin-bottom: 5px;
+}
+
+.site-info p {
+ font-size: 14px;
+ color: #777;
+}
+.feature-list-section {
+ padding: 60px 20px;
+ background-color: #ffffff;
+ font-family: sans-serif;
+}
+
+.feature-heading {
+ font-size: 28px;
+ font-weight: bold;
+ color: #000;
+ text-align: center;
+ margin-bottom: 40px;
+}
+
+.feature-list-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 30px;
+}
+
+.feature-card {
+ background: white;
+ border-radius: 10px;
+ padding: 20px;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
+ text-align: center;
+}
+
+.feature-card img {
+ width: 48px;
+ height: 48px;
+ margin-bottom: 20px;
+}
+
+.feature-card h4 {
+ font-size: 18px;
+ margin-bottom: 10px;
+}
+
+.feature-card p {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 15px;
+}
+
+.text-button {
+ background: none;
+ color: #000;
+ font-weight: 500;
+ text-decoration: underline;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+}
+.subscription-section {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 60px 40px;
+ background-color: #ffffff;
+ flex-wrap: wrap;
+ gap: 40px;
+}
+
+.subscription-heading {
+ font-size: 24px;
+ font-weight: bold;
+ color: #000000;
+ margin-bottom: 10px;
+}
+
+.subscription-description {
+ font-size: 14px;
+ color: #555555;
+ margin-bottom: 20px;
+}
+
+.subscription-form {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ max-width: 400px;
+ width: 100%;
+}
+
+.form-input {
+ padding: 10px 15px;
+ font-size: 14px;
+ border: 1px solid #ccc;
+ border-radius: 6px;
+}
+
+.form-submit {
+ background-color: black;
+ color: white;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+.form-submit:hover {
+ background-color: #222;
+}
+
+.subscription-form .disclaimer {
+ font-size: 12px;
+ color: #777;
+}
+
+.subscription-form .link-text {
+ color: #2563eb;
+ text-decoration: underline;
+ margin-left: 4px;
+ font-weight: 500;
+}
+.cta-gradient-section {
+ background: linear-gradient(90deg, #fbb034, #ffdd00);
+ text-align: center;
+ padding: 40px 20px;
+ border-radius: 10px;
+ font-family: sans-serif;
+}
+.contact-signup-section {
+ display: flex;
+ justify-content: space-between;
+ background-color: #fafafa;
+ padding: 40px 20px;
+ border-radius: 12px;
+ font-family: sans-serif;
+ flex-wrap: wrap;
+ gap: 30px;
+}
+
+.contact-form {
+ display: flex;
+ align-items: stretch;
+ gap: 0;
+ max-width: 300px;
+}
+
+.contact-form input[type="email"] {
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 4px 0 0 4px;
+ flex: 1;
+}
+
+.btn-filled-orange {
+ background-color: #f5a623;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 0 4px 4px 0;
+ cursor: pointer;
+}
+.hero-banner {
+ background-color: #fcd34d;
+ text-align: center;
+ padding: 80px 20px;
+ position: relative;
+ font-family: sans-serif;
+}
+
+.hero-subheading {
+ font-size: 24px;
+ color: #333333;
+ font-weight: 500;
+ margin-top: 10px;
+}
+
+.hero-button {
+ background-color: #1f2937;
+ color: #ffffff;
+ padding: 10px 24px;
+ border-radius: 999px;
+ font-weight: bold;
+ display: inline-block;
+ margin-top: 20px;
+ cursor: pointer;
+ text-decoration: none;
+}
+
+.decoration {
+ position: absolute;
+ z-index: 0;
+ opacity: 0.5;
+}
+
+.decoration.top-left {
+ top: 0;
+ left: 0;
+}
+
+.decoration.top-right {
+ top: 0;
+ right: 0;
+}
+.pricing-section {
+ padding: 60px 20px;
+ text-align: center;
+ background: #fff;
+ font-family: sans-serif;
+}
+
+.pricing-section .hero-label {
+ font-size: 14px;
+ color: #888;
+ margin-bottom: 10px;
+}
+
+.pricing-section .hero-heading {
+ font-size: 32px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.pricing-section .hero-description {
+ font-size: 16px;
+ color: #555;
+ max-width: 600px;
+ margin: 0 auto 40px;
+}
+
+.pricing-section .pricing-cards {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 30px;
+}
+
+.pricing-card {
+ background: #f9f9f9;
+ padding: 30px;
+ border-radius: 8px;
+ width: 280px;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
+ text-align: left;
+}
+
+.pricing-card h3 {
+ font-size: 20px;
+ margin-bottom: 10px;
+}
+
+.pricing-card .price {
+ font-size: 24px;
+ font-weight: bold;
+ color: #000;
+ margin: 10px 0;
+}
+
+.pricing-card ul {
+ list-style: none;
+ padding: 0;
+ margin: 20px 0;
+}
+
+.pricing-card ul li {
+ margin-bottom: 10px;
+ color: #444;
+}
+
+.pricing-card .btn-filled {
+ display: inline-block;
+ padding: 10px 20px;
+ background: black;
+ color: white;
+ border-radius: 4px;
+ text-decoration: none;
+}
+.pricing-section-dark {
+ background: #111;
+ color: #fff;
+ padding: 60px 20px;
+ text-align: center;
+ font-family: sans-serif;
+}
+
+.pricing-section-dark .badge {
+ text-transform: uppercase;
+ color: #facc15;
+ font-weight: 600;
+ margin-bottom: 10px;
+}
+
+.pricing-section-dark .title {
+ font-size: 32px;
+ margin-bottom: 10px;
+}
+
+.pricing-section-dark .description {
+ color: #ccc;
+ margin-bottom: 40px;
+ max-width: 700px;
+ margin-inline: auto;
+}
+
+.pricing-section-dark .card-grid {
+ display: flex;
+ gap: 20px;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.pricing-card {
+ background: #1a1a1a;
+ padding: 30px;
+ border-radius: 8px;
+ width: 280px;
+ text-align: left;
+}
+
+.pricing-card.highlight {
+ border: 2px solid #facc15;
+}
+
+.pricing-card .badge {
+ color: #facc15;
+ font-weight: bold;
+ margin-bottom: 8px;
+}
+
+.pricing-card .price {
+ font-size: 24px;
+ font-weight: bold;
+ margin: 10px 0;
+ color: #fff;
+}
+
+.pricing-card ul {
+ list-style: none;
+ padding: 0;
+ color: #bbb;
+ margin-bottom: 20px;
+}
+
+.pricing-card ul li {
+ margin-bottom: 8px;
+}
+
+.pricing-card button {
+ padding: 10px 20px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+button.filled {
+ background: #facc15;
+ color: #000;
+ border: none;
+}
+
+button.outline {
+ background: transparent;
+ color: #facc15;
+ border: 1px solid #facc15;
+}
+
+button.light {
+ background: #fff;
+ color: #111;
+ border: none;
+}
+/* ======= PRICING SECTION ======= */
+.pricing-section {
+ padding: 60px 40px;
+ background: #fff;
+ text-align: center;
+}
+
+.pricing-header {
+ text-align: center;
+ margin-bottom: 40px;
+}
+
+.pricing-header h2 {
+ font-size: 28px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.pricing-header p {
+ color: #666;
+ font-size: 16px;
+}
+
+.pricing-tabs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+ justify-content: center;
+ list-style: none;
+ padding: 0;
+ margin: 20px 0 40px 0;
+}
+
+.pricing-tabs li {
+ background: #f2f2f2;
+ padding: 8px 16px;
+ border-radius: 20px;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.pricing-tabs li:hover {
+ background: #ddd;
+}
+
+.pricing-hub {
+ text-align: center;
+ margin-bottom: 40px;
+}
+
+.pricing-hub h3 {
+ font-size: 20px;
+ font-weight: bold;
+}
+
+.pricing-hub p {
+ color: #444;
+ font-size: 15px;
+ margin-bottom: 10px;
+}
+
+.hub-link {
+ color: #007bff;
+ font-weight: 500;
+ text-decoration: underline;
+}
+
+.pricing-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: 20px;
+}
+
+.pricing-card {
+ border: 1px solid #ddd;
+ border-radius: 6px;
+ padding: 20px;
+ background: #fdfdfd;
+ text-align: center;
+}
+
+.pricing-card h4 {
+ font-size: 20px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.pricing-card .price {
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.price-yearly {
+ display: block;
+ font-size: 14px;
+ color: #888;
+}
+
+.pricing-card .contacts {
+ font-size: 14px;
+ color: #444;
+ margin-bottom: 10px;
+}
+
+.pricing-card .notes {
+ font-size: 13px;
+ color: #777;
+ margin-bottom: 15px;
+}
+
+.buy-btn {
+ background: black;
+ color: white;
+ padding: 10px 16px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.buy-btn:hover {
+ background: #333;
+}
+
+
+
+.pricing-header {
+ margin-bottom: 30px;
+}
+
+.pricing-toggle {
+ display: inline-flex;
+ border: 1px solid #ccc;
+ border-radius: 6px;
+ overflow: hidden;
+ margin-bottom: 30px;
+}
+
+.pricing-toggle button {
+ padding: 10px 20px;
+ border: none;
+ background: #eee;
+ cursor: pointer;
+}
+
+.pricing-toggle button.active {
+ background: #000;
+ color: #fff;
+}
+
+.pricing-cards {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+ justify-content: center;
+}
+
+.pricing-card {
+ background: #fff;
+ border: 1px solid #ddd;
+ border-radius: 10px;
+ padding: 30px 20px;
+ width: 300px;
+ text-align: left;
+ box-shadow: 0 0 6px rgba(0,0,0,0.05);
+}
+
+.pricing-card.highlight {
+ border: 2px solid #000;
+}
+
+.pricing-card h3 {
+ font-size: 20px;
+ margin-bottom: 10px;
+}
+
+.pricing-card .price {
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.pricing-card .savings,
+.pricing-card .bonus {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 10px;
+}
+
+.pricing-card ul {
+ list-style: none;
+ padding: 0;
+ margin: 15px 0;
+}
+
+.pricing-card ul li {
+ margin-bottom: 8px;
+}
+
+.pricing-card button {
+ background: #000;
+ color: #fff;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+.pricing-single-plan {
+ padding: 60px 20px;
+ background-color: #fff;
+ text-align: center;
+ font-family: sans-serif;
+}
+
+.pricing-header .pricing-title {
+ font-size: 32px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.pricing-header .pricing-subtitle {
+ color: #555;
+ margin-bottom: 40px;
+ font-size: 16px;
+}
+
+.features-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ gap: 30px;
+ margin-bottom: 40px;
+}
+
+.feature-item {
+ background: #f9f9f9;
+ padding: 20px;
+ border-radius: 8px;
+ text-align: center;
+}
+
+.feature-icon {
+ font-size: 24px;
+ display: block;
+ margin-bottom: 10px;
+}
+
+.feature-title {
+ font-weight: bold;
+ font-size: 18px;
+ margin-bottom: 5px;
+}
+
+.feature-description {
+ font-size: 14px;
+ color: #666;
+}
+
+.plan-card {
+ background: #f0f4ff;
+ padding: 30px;
+ border-radius: 10px;
+ max-width: 400px;
+ margin: 0 auto;
+}
+
+.plan-title {
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.plan-price {
+ font-size: 20px;
+ color: #333;
+ margin-bottom: 10px;
+}
+
+.plan-description {
+ font-size: 14px;
+ color: #555;
+ margin-bottom: 20px;
+}
+.contact-form-section {
+ padding: 60px 20px;
+ background-color: #fff;
+ font-family: sans-serif;
+ text-align: center;
+}
+
+.contact-header .contact-title {
+ font-size: 28px;
+ font-weight: bold;
+ color: #000;
+ margin-bottom: 10px;
+}
+
+.contact-description {
+ color: #555;
+ font-size: 14px;
+ margin-bottom: 30px;
+}
+
+.contact-form {
+ max-width: 600px;
+ margin: 0 auto;
+ display: grid;
+ gap: 20px;
+}
+
+.contact-form input[type="text"],
+.contact-form input[type="email"],
+.contact-form textarea {
+ width: 100%;
+ padding: 12px;
+ border-radius: 4px;
+ border: 1px solid #ccc;
+ font-size: 14px;
+ font-family: inherit;
+}
+
+.contact-form textarea {
+ resize: vertical;
+ min-height: 120px;
+}
+
+.checkbox-label {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ font-size: 14px;
+ color: #444;
+ justify-content: start;
+ text-align: left;
+}
+
+.contact-form .btn-filled {
+ background-color: black;
+ color: white;
+ padding: 12px 24px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+ font-size: 16px;
+}
+.contact-info-section {
+ text-align: center;
+ padding: 60px 20px;
+ background-color: #fff;
+}
+
+.contact-profile-image {
+ width: 80px;
+ height: 80px;
+ object-fit: cover;
+ border-radius: 50%;
+ margin-bottom: 20px;
+}
+
+.contact-heading {
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.contact-description {
+ font-size: 14px;
+ color: #666;
+ max-width: 600px;
+ margin: 0 auto 40px;
+ white-space: pre-line;
+}
+
+.contact-columns {
+ display: flex;
+ justify-content: center;
+ gap: 30px;
+ flex-wrap: wrap;
+}
+
+.contact-column {
+ flex: 1;
+ min-width: 240px;
+ max-width: 300px;
+ text-align: left;
+}
+
+.contact-icon {
+ font-size: 24px;
+ display: block;
+ margin-bottom: 10px;
+}
+
+.contact-title {
+ font-weight: bold;
+ font-size: 16px;
+ margin-bottom: 6px;
+}
+
+.contact-text {
+ font-size: 14px;
+ color: #444;
+ white-space: pre-line;
+}
+.contact-split-section {
+ display: flex;
+ flex-wrap: wrap;
+ min-height: 100vh;
+}
+
+.contact-left-panel {
+ flex: 1;
+ background-size: cover;
+ background-position: center;
+ color: white;
+ padding: 40px;
+}
+
+.contact-right-panel {
+ flex: 1;
+ background-color: #ffffff;
+ padding: 60px 40px;
+}
+
+.contact-section {
+ margin-bottom: 30px;
+}
+
+.section-heading {
+ font-size: 18px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.section-content {
+ white-space: pre-line;
+ color: #eee;
+}
+
+.section-highlight {
+ color: #00ff99;
+ font-weight: 500;
+ white-space: pre-line;
+}
+
+.form-heading {
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 20px;
+}
+
+.contact-form input,
+.contact-form textarea {
+ display: block;
+ width: 100%;
+ padding: 12px;
+ margin-bottom: 16px;
+ border: 1px solid #ccc;
+ border-radius: 6px;
+ font-size: 14px;
+}
+
+.contact-form textarea {
+ height: 120px;
+ resize: vertical;
+}
+
+.contact-form .btn-filled {
+ background-color: #00c981;
+ color: white;
+ padding: 12px 24px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+}
+
+.contact-page {
+ font-family: sans-serif;
+}
+
+.hero-banner {
+ background-size: cover;
+ background-position: center;
+ padding: 80px 20px;
+ color: #fff;
+ text-align: center;
+}
+
+.contact-three-column {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 40px;
+ padding: 60px 20px;
+ justify-content: space-between;
+ background: #fff;
+}
+
+.contact-map {
+ flex: 1;
+ min-width: 280px;
+ border: none;
+ height: 300px;
+}
+
+.contact-info {
+ flex: 1;
+ min-width: 280px;
+}
+
+.contact-form {
+ flex: 1;
+ min-width: 280px;
+}
+
+.contact-form input,
+.contact-form textarea {
+ width: 100%;
+ padding: 12px;
+ margin-bottom: 16px;
+ font-size: 14px;
+ border: 1px solid #ccc;
+ border-radius: 6px;
+}
+
+.contact-form textarea {
+ resize: vertical;
+ height: 100px;
+}
+
+.contact-form button {
+ background-color: #007bff;
+ color: #fff;
+ padding: 12px 24px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+}
+.contact-getintouch {
+ background-color: #084c61;
+ color: #ffffff;
+ padding: 60px 20px;
+ text-align: center;
+}
+
+.contact-getintouch h2 {
+ font-size: 28px;
+ margin-bottom: 30px;
+}
+
+.contact-getintouch .columns {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 30px;
+ justify-content: center;
+}
+
+.contact-getintouch .column {
+ flex: 1 1 250px;
+ text-align: left;
+}
+
+.contact-getintouch .column h4 {
+ margin-top: 10px;
+ font-size: 18px;
+}
+
+.contact-getintouch .column p {
+ font-size: 14px;
+ margin: 4px 0;
+}
+
+.contact-messageus {
+ background-color: #ffffff;
+ padding: 60px 20px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 40px;
+ align-items: flex-start;
+}
+
+.contact-messageus .left-column {
+ flex: 1 1 300px;
+}
+
+.contact-messageus .left-column h2 {
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.contact-messageus .left-column p {
+ font-size: 14px;
+ color: #444;
+ margin-bottom: 15px;
+}
+
+.contact-messageus .form {
+ flex: 1 1 400px;
+}
+
+.contact-messageus form {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.contact-messageus form input,
+.contact-messageus form textarea {
+ padding: 10px;
+ border-radius: 4px;
+ border: 1px solid #ccc;
+}
+
+.contact-messageus form button {
+ background-color: #c3d500;
+ color: #000;
+ border: none;
+ padding: 12px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
`;
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts
index c904602..9cf5201 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/WireframesUi/wireframe-renderer.component.ts
@@ -20,6 +20,8 @@ export class WireframeRendererComponent implements OnInit {
jsonInput = '';
+ initialPrompt = '';
+
id: number;
@@ -45,7 +47,7 @@ export class WireframeRendererComponent implements OnInit {
return Object.keys(obj || {});
}
- // Step 1
+ // Step 1 get data from that id
fetchTreeById(id: number) {
this.siteTreeService.getById(id).subscribe({
next: (res) => {
@@ -54,6 +56,9 @@ export class WireframeRendererComponent implements OnInit {
// Check if model key exists and has some value
if (res && res.model) {
try {
+
+ this.initialPrompt = res.prompt;
+
// Optional: if model is already a stringified JSON
const maybeParsed = typeof res.model === 'string' ? JSON.parse(res.model) : res.model;
this.jsonInput = JSON.stringify(maybeParsed, null, 2);
@@ -114,11 +119,13 @@ export class WireframeRendererComponent implements OnInit {
if (sectionName === 'Children') continue; // already handled
- console.log('✅ Generating prompt for:', pageName, sectionName);
- console.log('sc name sectionDescription : ', sectionName, ' ', sectionDescription)
+ console.log('✅ Generating prompt for : ', pageName, 'sectionc name and sectionDescription : ', sectionName, ' ', sectionDescription);
const prompt = await this.generatePromptFromSection(sectionName, sectionDescription); // ✅ await
sectionPrompts[sectionName] = prompt;
+
+ // console.log( sectionName, ': ', prompt)
+
}
// 🧠 Store all prompts for that page
@@ -168,7 +175,7 @@ HTML Only. No CSS.
// Step 4
- generatePromptFromSection(
+ async generatePromptFromSection(
sectionName: string,
sectionDescription: string,
headerId: number = 35,
@@ -177,24 +184,48 @@ HTML Only. No CSS.
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
-
+ .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: (res) => {
+ next: async (res) => {
try {
const jsonBlock = res?.javacode;
+
+ // console.log( fieldType , ' json is : ', jsonBlock)
+
if (jsonBlock) {
const parsedJson = typeof jsonBlock === 'string' ? JSON.parse(jsonBlock) : jsonBlock;
// 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:
+
+- 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 {
- console.warn('⛔ No JSON found, skipping.');
resolve('{}');
}
} catch (err) {
@@ -235,7 +266,7 @@ HTML Only. No CSS.
pageSections: Record = {}; // final UI map
- // Step 5 - generate html code via llm through prompt
+ // Step 5 - generate html code via java Api through prompt
async processAllPagesLiveUpdate() {
for (const [pageName, sectionMap] of Object.entries(this.allPagePrompts)) {
const sectionHtmls: string[] = [];
@@ -436,6 +467,32 @@ HTML Only. No CSS.
}
+
+ // // Step 6
+ async calLlm(data): Promise {
+ return new Promise((resolve, reject) => {
+ console.log('call Llm Start ')
+ const payload = {
+ query: data,
+ conversationId: '677'
+ };
+ console.log(' query : ', payload.query)
+ this.siteTreeService.callLlm(payload).subscribe({
+ next: (res) => {
+ console.log('response ', res)
+ if (res && res.responseContent) {
+ resolve(res.responseContent);
+ } else {
+ resolve('⚠️ No response received.');
+ }
+ },
+ error: () => resolve('⚠️ LLaMA API failed.')
+
+ });
+ });
+ }
+
+
// // Step 4
// generatePromptFromSection(
// sectionName: string,
@@ -485,29 +542,7 @@ HTML Only. No CSS.
- // // Step 6
- // async generateJson(data): Promise {
- // return new Promise((resolve, reject) => {
- // console.log('genearte json start ')
- // const payload = {
- // query: data.message,
- // conversationId: '677'
- // };
- // console.log(' query : ', payload.query)
- // this.siteTreeService.generateJson(payload).subscribe({
- // next: (res) => {
- // console.log('response ', res)
- // if (res && res.responseContent) {
- // resolve(res.responseContent);
- // } else {
- // resolve('⚠️ No response received.');
- // }
- // },
- // error: () => resolve('⚠️ LLaMA API failed.')
- // });
- // });
- // }
// pageSections: Record> = {}; // { pageName: { sectionName: html } }
// async processAllPagesLiveUpdate() {
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html
index 464ad26..3d0276b 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.html
@@ -57,6 +57,8 @@
+
+
@@ -76,11 +78,18 @@
-
-
+
@@ -119,4 +130,4 @@
Generate
-
+
\ No newline at end of file
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts
index f7617d8..86fc161 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/tree-visualizer.component.ts
@@ -125,6 +125,13 @@ after click this icard inside we upload excel of student data, icard template, a
// }
// }
+ isWireframeLoaded = false;
+
+ generateWireframe() {
+ this.activeTab = 'wireframe';
+ this.isWireframeLoaded = true;
+ }
+
getSections(data: any): { title: string, description: string }[] {
const sections: { title: string, description: string }[] = [];
for (const key in data) {
@@ -180,7 +187,7 @@ after click this icard inside we upload excel of student data, icard template, a
}`;
- const suffix = `You are a JSON structure generator for website page hierarchies.
+ const suffix = ` You are a JSON structure generator for website page hierarchies.
You are a JSON structure generator for website UI and sitemap hierarchy.
@@ -250,13 +257,13 @@ Analyze the flow implied in the prompt:
const payload = {
- query: this.rawInputText + suffix,
+ query: this.rawInputText + ' ' + suffix,
conversationId: '677'
};
console.log(' query : ', payload.query)
- this.siteTreeService.generateJson(payload).subscribe({
+ this.siteTreeService.callLlm(payload).subscribe({
next: (res) => {
console.log('response ', res)
@@ -268,6 +275,7 @@ Analyze the flow implied in the prompt:
// 🔁 Now call update API
const updatePayload = {
+ prompt: this.rawInputText + ' ',
model: this.inputJson // model format expected by backend
};
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts
index da05f0a..742221d 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main-routing.module.ts
@@ -97,6 +97,7 @@ import { MappingruleeditComponent } from './datamanagement/mappingrule/mappingru
import { TreeVisualizerComponent } from './fnd/SiteTreeBuilder/tree-visualizer.component';
import { SiteTreeComponent } from './fnd/SiteTreeBuilder/SiteBuilderGrid/SiteTree.component';
+import { EditstepperComponent } from './BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component';
@@ -271,6 +272,9 @@ const routes: Routes = [
{ path: 'sitetree', component: SiteTreeComponent, },
+ { path: 'stepper', component: EditstepperComponent },
+
+
{ path: 'sitetree/SiteBuilder/:id', component: TreeVisualizerComponent },
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts
index 6453ca4..5de9635 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/main.module.ts
@@ -124,6 +124,7 @@ import { WireframeRendererComponent } from './fnd/SiteTreeBuilder/WireframesUi/w
import { HomepageEditorComponent } from './fnd/SiteTreeBuilder/HomePage/Templete1/homepage-editor.component';
import { SiteTreeComponent } from './fnd/SiteTreeBuilder/SiteBuilderGrid/SiteTree.component';
import { TreeNodeComponent } from './fnd/SiteTreeBuilder/TreeNode/tree-node.component';
+import { EditstepperComponent } from './BuilderComponents/vpspack/Visa_application/VisaOrderWorkflow/editstepper.component';
@NgModule({
@@ -145,6 +146,7 @@ import { TreeNodeComponent } from './fnd/SiteTreeBuilder/TreeNode/tree-node.comp
// buildercomponents
SiteTreeComponent,
+ EditstepperComponent,
HomepageEditorComponent,
WireframeRendererComponent,
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts
index 21ef221..2d66447 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.prod.ts
@@ -15,6 +15,7 @@ export const environment = {
backendUrl:'http://157.66.191.31:30101/back',
nodeUrl:'http://157.66.191.31:31170/llm',
+ builderUrl:'http://157.66.191.31:31170',
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts
index e276c77..34cb441 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/environments/environment.ts
@@ -11,10 +11,12 @@ export const environment = {
captchaSiteKey: '6LfrdSUpAAAAALkYDmnvdX3GLLCArgPWNHfXasjP',
// backport:'31701/visaproject36808/back',
- backendUrl:'http://localhost:9292/back',
- nodeUrl:'http://localhost:3000/llm',
+// backendUrl:'http://localhost:9292/back',
+ nodeUrl:'http://157.66.191.31:31170/llm',
+ builderUrl:'http://157.66.191.31:31170',
-// backendUrl:'http://157.66.191.31:30101/back',
+
+ backendUrl:'http://157.66.191.31:30101/back',
diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html
index 08d3e58..8e178a3 100644
--- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html
+++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/index.html
@@ -12,6 +12,7 @@
+