From 7bb707f3594918a2c9f291fb3f9084ce0ae4e88b Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Date: Thu, 1 May 2025 10:26:41 +0530 Subject: [PATCH] site builder --- .../Design_lbrary.component.html | 2 +- .../TreeNode/tree-node.component.html | 76 +- .../TreeNode/tree-node.component.ts | 308 ++++---- .../WireframesUi/common-css.ts | 704 +++++++++++++++++- .../wireframe-renderer.component.ts | 5 +- .../src/assets/images/micrologo.png | Bin 0 -> 3603 bytes .../images/{placeholder.svg => place.svg} | 0 7 files changed, 881 insertions(+), 214 deletions(-) create mode 100644 visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/micrologo.png rename visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/{placeholder.svg => place.svg} (100%) diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/Design_lbrary/Design_lbrary.component.html b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/Design_lbrary/Design_lbrary.component.html index 687e4ce..b5fd255 100644 --- a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/Design_lbrary/Design_lbrary.component.html +++ b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/app/modules/main/fnd/SiteTreeBuilder/Design_lbrary/Design_lbrary.component.html @@ -4,7 +4,7 @@
-

DesignLbrary

+

Design Library

+ @@ -40,9 +43,7 @@ - +
@@ -51,9 +52,7 @@
Edit Section: {{ selectedSection.type }}
- +
@@ -67,7 +66,8 @@
-
+
{{ template.type }}
{{ template.description }}
@@ -79,7 +79,8 @@
- +
@@ -113,47 +115,41 @@
Processing...
-
+
-
+
{{ page.name }}
- - -
-
{{ section.type }}
-
-
- - -
- -
- -
-
+ + +
+
{{ section.type }}
+
+
+ + +
+ +
+ +
+
-
-
+
\ No newline at end of file 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 221e529..870fb78 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 @@ -4,6 +4,7 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { SiteTreeservice } from '../SiteBuilderGrid/SiteTree.service'; +import { ToastrService } from 'ngx-toastr'; interface Section { type: string; @@ -27,23 +28,23 @@ 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; @@ -52,19 +53,19 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { 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 = ''; currentParentForChild: Page | null = null; - + // For drag and drop draggedPage: Page | null = null; draggedSection: Section | null = null; @@ -72,7 +73,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { currentSectionIndex: number = -1; - + // Section templates with better descriptions sectionTemplates = [ { @@ -129,174 +130,177 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { constructor( private siteTreeService: SiteTreeservice, - private route: ActivatedRoute - ) {} - + private route: ActivatedRoute, + private toastr: ToastrService + + + ) { } + ngOnInit(): void { this.siteId = this.route.snapshot.params['id']; this.fetchSiteTree(); } - + 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); } - + this.destroy$.next(); this.destroy$.complete(); } - + // 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(); }); - + // 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(); }); - + // 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 }); } - + // 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(); } - + 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) { @@ -307,38 +311,38 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { // Fallback if root node not found this.gridTranslateY = topPadding; } - + this.applyGridTransform(); } - + // 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({ @@ -348,7 +352,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { 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); + this.processSiteData(pagesObj, rootTitle); }, error: () => { this.loading = false; @@ -356,7 +360,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { } }); } - + // Create a default structure createDefaultStructure(): void { const defaultData = { @@ -370,7 +374,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { }; this.processSiteData(defaultData); } - + processSiteData(pagesObj: any, rootTitle?: string): void { // Initialize root with dynamic title this.rootPage = { id: 'root', name: rootTitle, sections: [], children: [] }; @@ -398,8 +402,8 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { content: Array.isArray(val) ? val.join('\n') : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) + ? JSON.stringify(val, null, 2) + : String(val) }); if (!this.availableSectionTypes.includes(prop)) { this.availableSectionTypes.push(prop); @@ -417,7 +421,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { this.rootPage.children = buildPages(pagesObj, this.rootPage); setTimeout(() => this.centerGrid(), 100); } - + createPageFromData(pageName: string, pageData: any, idPrefix: string): Page { const page: Page = { id: idPrefix, @@ -425,19 +429,19 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { 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 @@ -460,7 +464,7 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { } } }); - + // Process children recursively if the Children key exists if (pageData.Children) { let childId = 0; @@ -472,26 +476,26 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { }); } } - + 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 { @@ -508,45 +512,45 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { 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; } - + selectSection(section: Section): void { this.selectedSection = section; this.editMode = 'section'; } - + // Show modal to add a child page showAddChildPageModal(parent: Page): void { this.currentParentForChild = parent; this.newPageName = ''; this.showAddChildModal = true; } - + // 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, @@ -554,51 +558,51 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { children: [], parent: this.currentParentForChild }; - + this.currentParentForChild.children.push(newPage); this.hasUnsavedChanges = true; this.closeAddChildModal(); - + // Auto-select the new page this.selectPage(newPage); } } - + 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.hasUnsavedChanges = true; } } } - + // 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) { @@ -610,14 +614,14 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { 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) { @@ -625,104 +629,104 @@ export class TreeNodeComponent implements OnInit, OnDestroy, AfterViewInit { 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); } - + if (!this.availableSectionTypes.includes(template.type)) { this.availableSectionTypes.push(template.type); } - + this.hasUnsavedChanges = true; this.closeOffcanvas(); } } - + // 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); + + // 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(); } - - 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; } } } - + // DRAG AND DROP METHODS - + // Handle page drag start onPageDragStart(page: Page): void { this.draggedPage = page; } - + // Handle page drag end onPageDragEnd(): void { this.draggedPage = null; this.dropTargetPage = null; } - + // 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); @@ -730,19 +734,19 @@ addCustomSection(): void { this.draggedPage.parent.children.splice(sourceIndex, 1); } } - + // Add to new parent if (!targetPage.children) { targetPage.children = []; } - + // Update parent reference this.draggedPage.parent = targetPage; targetPage.children.push(this.draggedPage); - + this.hasUnsavedChanges = true; } - + // Handle section drag drop onSectionDrop(event: CdkDragDrop): void { if (event.previousContainer === event.container) { @@ -757,12 +761,12 @@ addCustomSection(): void { event.currentIndex ); } - + this.hasUnsavedChanges = true; } - + // UI HELPER METHODS - + getNodeIcon(page: Page): string { if (page.name.toLowerCase() === 'home') { return 'fas fa-home'; @@ -772,39 +776,43 @@ addCustomSection(): void { return 'fas fa-file'; } } - + // 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 + + 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'); + // alert('Site structure saved successfully'); + this.toastr.success("Site structure saved successfully"); + } }, error: (err) => { this.loading = false; console.error('Error saving data:', err); - + if (!silent) { - alert('Failed to save site structure'); + // alert('Failed to save site structure'); + this.toastr.error("Failed to save site structure"); + } } }); } } - + @HostListener('window:beforeunload', ['$event']) beforeUnloadHandler(event: BeforeUnloadEvent): void { if (this.hasUnsavedChanges) { @@ -812,7 +820,7 @@ addCustomSection(): void { event.returnValue = 'You have unsaved changes. Are you sure you want to leave?'; } } - + @HostListener('window:resize') onWindowResize(): void { this.centerGrid(); 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 1507044..30cbbf7 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 @@ -303,50 +303,106 @@ button:hover { } /* ======= TESTIMONIAL SECTION ======= */ +/* ๐Ÿ”ฒ Main Section */ .testimonial-section { - padding: 60px 20px; + padding: 80px 40px; text-align: center; + background-size: cover; + background-position: center; + position: relative; + color: #fff; } +/* ๐Ÿ”ณ Optional Background Overlay */ +.testimonial-section::before { + content: ""; + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 0; +} + +.testimonial-section > * { + position: relative; + z-index: 1; +} + +/* ๐Ÿท๏ธ Heading */ .testimonial-heading { - font-size: 28px; + font-size: 2rem; font-weight: bold; margin-bottom: 10px; + letter-spacing: 1px; } .testimonial-subtitle { - color: #666; + color: #ddd; margin-bottom: 40px; + font-size: 1rem; } +/* ๐Ÿง‘โ€๐Ÿ’ผ Grid Layout */ .testimonial-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); - gap: 20px; + gap: 30px; + justify-content: center; } +/* ๐Ÿ“ฆ Testimonial Card */ .testimonial-card { - background: white; - padding: 20px; + background: #fff; + color: #222; + padding: 25px; border-radius: 12px; - box-shadow: 0 2px 10px rgba(0,0,0,0.05); + box-shadow: 0 4px 20px rgba(0,0,0,0.08); max-width: 320px; + margin: 0 auto; + text-align: center; + transition: transform 0.3s ease; } + +.testimonial-card:hover { + transform: translateY(-5px); +} + +/* ๐Ÿ–ผ๏ธ Avatar Wrapper with fallback */ +.testimonial-avatar { + width: 60px; + height: 60px; + border-radius: 50%; + background-color: #ccc; + overflow: hidden; + margin: 0 auto 15px; + display: flex; + align-items: center; + justify-content: center; +} + +.testimonial-avatar img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* ๐Ÿ—ฃ๏ธ Quote Text */ +.testimonial-card blockquote { + font-style: italic; + color: #444; + margin: 15px 0; + font-size: 0.95rem; + line-height: 1.6; +} + +/* โญ Star Ratings */ .stars { color: #f39c12; font-size: 1.2rem; + margin-bottom: 8px; } -.author { - margin-top: 15px; - text-align: center; -} -.author .avatar { - width: 50px; - height: 50px; - border-radius: 50%; -} - -.testimonial-card .profile { + /* ๐Ÿ”ฒ Placeholder block */ +.profile { width: 40px; height: 40px; background: #ccc; @@ -354,10 +410,74 @@ button:hover { margin: 0 auto 10px; } -.testimonial-card blockquote { - font-style: italic; - color: #444; - margin: 10px 0; +/* ๐Ÿง‘ Avatar image */ +.avatar { + width: 50px; + height: 50px; + border-radius: 50%; + object-fit: cover; + display: block; + margin: 0 auto; +} + +/* ๐Ÿงพ Author Info */ +.author { + margin-top: 15px; + font-weight: 600; + font-size: 0.95rem; + color: #333; +} + +/* ๐ŸŸก CTA Button */ +.testimonial-cta { + display: inline-block; + margin-top: 40px; + background: #ffc107; + color: #000; + padding: 12px 24px; + border-radius: 6px; + font-weight: 600; + text-decoration: none; + transition: 0.3s ease; +} + +.testimonial-cta:hover { + background: #e6ac00; +} + +/* ๐Ÿ“ž Contact Info */ +.testimonial-contact { + margin-top: 30px; + font-size: 14px; + color: #ccc; +} + +.testimonial-contact p { + margin: 5px 0; +} + +/* ๐Ÿ“ฑ Responsive Adjustments */ +@media (max-width: 768px) { + .testimonial-heading { + font-size: 1.5rem; + } + + .testimonial-subtitle { + font-size: 0.95rem; + } + + .testimonial-card { + max-width: 100%; + } + + .testimonial-grid { + gap: 20px; + } + + .testimonial-cta { + width: 100%; + max-width: 300px; + } } @@ -1250,25 +1370,105 @@ button { /* === FAQ === */ +/* ๐Ÿ”ฒ Base Layout */ .faq-section { padding: 60px 20px; + max-width: 1200px; + margin: auto; } + +/* Title and Subtitle */ .faq-title { font-size: 2rem; margin-bottom: 20px; + text-align: center; } + .faq-subtitle { font-size: 1rem; color: #666; margin-bottom: 30px; + text-align: center; } + +/* ๐Ÿ” Search Bar */ +.faq-search { + padding: 12px 20px; + width: 100%; + max-width: 400px; + font-size: 16px; + border-radius: 6px; + border: 1px solid #ccc; + margin: 0 auto 40px; + display: block; +} + +/* ๐Ÿ“ฆ Illustration + List Wrapper */ +.faq-content { + display: flex; + flex-wrap: wrap; + gap: 40px; + align-items: flex-start; + justify-content: center; +} + +/* ๐Ÿ–ผ๏ธ Left Illustration */ +.faq-illustration { + flex: 1 1 40%; + max-width: 400px; +} + +.faq-image { + width: 100%; + height: auto; + object-fit: contain; +} + +/* โ“ Q&A List */ +.faq-list { + flex: 1 1 55%; + display: flex; + flex-direction: column; + gap: 25px; +} + +.faq-item { + border-bottom: 1px solid #eee; + padding-bottom: 15px; +} + +/* ๐Ÿ—‚๏ธ Q & A */ .faq-question { font-weight: bold; margin-top: 15px; + font-size: 1.1rem; } + .faq-answer { margin-bottom: 20px; color: #444; + font-size: 0.95rem; +} + +/* ๐Ÿ“ฑ Responsive */ +@media (max-width: 768px) { + .faq-content { + flex-direction: column; + align-items: center; + } + + .faq-illustration, + .faq-list { + max-width: 100%; + } + + .faq-title { + font-size: 1.5rem; + } + + .faq-question { + font-size: 1rem; + } } /* === Logo Showcase === */ @@ -2612,6 +2812,95 @@ button.light { } } +.blog-template { + padding: 60px 30px; + max-width: 1200px; + margin: auto; + font-family: sans-serif; +} + +.blog-header { + text-align: center; + margin-bottom: 30px; +} + +.blog-icon { + font-size: 2rem; + display: block; + margin-bottom: 10px; +} + +.blog-title { + font-size: 2rem; + font-weight: bold; +} + +.blog-nav ul { + display: flex; + gap: 20px; + justify-content: center; + padding: 0; + list-style: none; + margin-bottom: 40px; +} + +.blog-nav li { + font-weight: 500; + cursor: pointer; + color: #007bff; +} + +.blog-layout { + display: flex; + gap: 40px; + flex-wrap: wrap; +} + +.blog-main { + flex: 1 1 65%; +} + +.content-block { + margin-bottom: 30px; +} + +.content-heading { + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 10px; +} + +.content-text { + font-size: 1rem; + color: #444; + line-height: 1.6; +} + +.blog-sidebar { + flex: 1 1 30%; + background: #f9f9f9; + padding: 20px; + border-radius: 8px; +} + +.sidebar-widget { + margin-bottom: 20px; + font-weight: 500; +} + +/* Responsive */ +@media (max-width: 768px) { + .blog-layout { + flex-direction: column; + } + .blog-nav ul { + flex-direction: column; + align-items: center; + gap: 10px; + } +} + + /* โœ… Clean and responsive logo section */ .logo-cloud { padding: 60px 20px; @@ -2685,6 +2974,377 @@ button.light { background: #555; } + /* feature section */ +/* ๐Ÿ”ฒ Feature Section Base */ +.feature-section { + display: flex; + align-items: center; + justify-content: center; + padding: 60px 40px; + gap: 40px; + flex-wrap: wrap; +} +/* ๐Ÿ–ผ๏ธ Image Block */ + .feature-image { + flex: 1 1 40%; + width: 100%; + max-width: 500px; + height: auto; + object-fit: contain; + display: block; + align-self: center; +} + +/* ๐Ÿ“„ Text Content */ +.feature-content { + flex: 1 1 50%; + max-width: 600px; +} + +.feature-title { + font-size: 28px; + font-weight: 700; + margin-bottom: 20px; + line-height: 1.3; +} + +.feature-description { + font-size: 16px; + color: #555; + margin-bottom: 20px; +} + +/* ๐Ÿ“‹ Feature List */ +.feature-list { + list-style: none; + padding-left: 0; + margin: 0; +} + +.feature-list li { + position: relative; + padding-left: 30px; + margin-bottom: 10px; + font-size: 15px; + color: #444; +} + +.feature-list li::before { + content: "โœ”"; + position: absolute; + left: 0; + color: #008060; + font-weight: bold; +} + +/* ๐Ÿ” Responsive Layout */ +@media (max-width: 768px) { + .feature-section { + flex-direction: column; + text-align: center; + gap: 30px; + } + + .feature-content { + max-width: 100%; + } + + .feature-title { + font-size: 22px; + } + + .feature-description { + font-size: 15px; + } + + .feature-image { + max-width: 100%; + } +} +/* ๐Ÿ”ฒ SECTION WRAPPER */ +.brand-guideline { + padding: 60px 30px; + max-width: 1000px; + margin: auto; + font-family: sans-serif; +} + +/* ๐Ÿงพ PROJECT INFO */ +.project-info { + margin-bottom: 40px; + text-align: center; +} +.brand-title { + font-size: 2rem; + font-weight: bold; + margin-bottom: 10px; +} +.brand-meta { + font-size: 0.95rem; + color: #555; +} + +/* ๐Ÿ–ผ๏ธ LOGO SECTION */ +.logo-section { + margin-bottom: 40px; +} +.section-heading { + font-size: 1.3rem; + font-weight: bold; + margin-bottom: 20px; + text-align: center; +} +.logo-label { + font-weight: 600; + margin-top: 20px; + margin-bottom: 10px; +} +.logo-img, +.logo-mono-group, +.logo-social-group { + display: flex; + gap: 20px; + flex-wrap: wrap; + justify-content: center; + margin-bottom: 20px; +} + +/* ๐Ÿ”ณ INDIVIDUAL LOGOS */ +.default-img { + max-height: 60px; + object-fit: contain; + background-color: #f0f0f0; + padding: 6px; + border-radius: 6px; + display: block; +} +.logo-primary { + height: 60px; + width: auto; +} + +.logo-icon { + height: 40px; + width: 40px; +} + +.logo-mono { + height: 40px; + width: auto; +} + +.logo-social-square, +.logo-social-round { + height: 40px; + width: 40px; +} + + +/* ๐ŸŽจ BRAND COLORS */ +.brand-colors { + margin-bottom: 40px; +} +.color-group { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: center; +} +.color-block { + width: 160px; + height: 100px; + border-radius: 8px; + padding: 10px; + color: #000; + box-shadow: 0 2px 6px rgba(0,0,0,0.05); + display: flex; + flex-direction: column; + justify-content: flex-end; + font-size: 0.85rem; + text-align: center; +} +.color-name { + font-weight: bold; + margin-bottom: 4px; +} +.color-usage { + font-style: italic; + font-size: 0.8rem; + color: #444; +} + +/* ๐ŸŽจ COLOR BACKGROUNDS */ +.color-block.primary-blue { + background-color: #1E9FFF; +} +.color-block.light-grey { + background-color: #C6CBD6; +} +.color-block.charcoal-black { + background-color: #2B2B2B; + color: #fff; +} +.color-block.white { + background-color: #FFFFFF; + border: 1px solid #ddd; +} + +/* โœ๏ธ TYPOGRAPHY */ +.typography-section { + margin-top: 40px; + text-align: center; +} +.font-block { + margin-bottom: 20px; +} +.font-role { + font-weight: 600; + font-size: 1rem; +} +.font-family { + font-family: monospace; + font-size: 0.95rem; + color: #333; +} + +/* ๐Ÿ“ฑ RESPONSIVE */ +@media (max-width: 768px) { + .logo-img, + .logo-mono-group, + .logo-social-group, + .color-group { + flex-direction: column; + align-items: center; + } + + .color-block { + width: 100%; + max-width: 300px; + } + + .logo-primary, + .logo-icon, + .logo-mono, + .logo-social-square, + .logo-social-round { + max-width: 100%; + height: auto; + } +} + +.logo-grid-section { + padding: 60px 30px; + max-width: 1200px; + margin: auto; +} + +.logo-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 30px; +} + +.logo-card { + background: #fff; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0,0,0,0.05); + padding: 20px; + text-align: center; +} + +.logo-img.placeholder { + width: 100%; + height: 140px; + background-color: #eee; + border-radius: 6px; + margin-bottom: 15px; + background-image: url('/assets/images/placeholder.png'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +.logo-title { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 6px; +} + +.logo-meta { + font-size: 0.9rem; + color: #666; +} + + +.logo-guideline { + padding: 60px 30px; + max-width: 1000px; + margin: auto; + font-family: sans-serif; +} +.guideline-header { + text-align: center; + margin-bottom: 40px; +} +.guideline-title { + font-size: 2rem; + font-weight: bold; +} +.guideline-meta { + color: #666; + font-size: 0.95rem; +} +.guideline-section { + margin-bottom: 50px; +} +.section-title { + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 10px; +} +.section-description { + font-size: 1rem; + color: #444; + margin-bottom: 15px; +} +.cover-version { + font-weight: 600; + font-size: 1rem; + color: #888; +} +.cover-title { + font-size: 1.6rem; + font-weight: bold; + margin: 8px 0; +} +.cover-subtitle { + color: #666; + font-size: 0.95rem; +} +.font-preview { + background: #f9f9f9; + padding: 15px; + border-radius: 6px; + text-align: center; +} +.font-name { + font-size: 1.2rem; + font-weight: bold; +} +.font-usage { + font-size: 0.95rem; + color: #555; +} +.placeholder { + background: #eee; + border-radius: 6px; + height: 180px; + background-image: url('/assets/images/placeholder.png'); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + margin-top: 10px; +} `; 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 07836d6..6ff3f16 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 @@ -324,7 +324,10 @@ HTML Only. No CSS. copyToClipboard(pageName: string) { const fullHtml = Object.values(this.pageSections[pageName]).join('\n'); navigator.clipboard.writeText(fullHtml).then(() => { - alert(`๐Ÿ“‹ HTML copied for page: ${pageName}`); + // alert(`๐Ÿ“‹ HTML copied for page: ${pageName}`); + this.toastr.success(`๐Ÿ“‹ HTML copied for page: ${pageName}`); + + }); } diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/micrologo.png b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/micrologo.png new file mode 100644 index 0000000000000000000000000000000000000000..7334d565f3971a6579336d4a8e6a7665dd313c7b GIT binary patch literal 3603 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 Z{2xx`c?e0U&@%u4002ovPDHLkV1mc7XS)CZ literal 0 HcmV?d00001 diff --git a/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/placeholder.svg b/visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/place.svg similarity index 100% rename from visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/placeholder.svg rename to visaproject-front-f/authsec_angular/frontend/angular-clarity-master/src/assets/images/place.svg