From 81565d294b59cc5a8874bd91f073e03e4793eac9 Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Sat, 7 Feb 2026 14:57:21 +0530 Subject: [PATCH] changes made to fix the VAPT testing --- index.html | 78 ++----- .../common/FormattedDescription.tsx | 12 +- src/components/ui/rich-text-editor.tsx | 211 +++++++++--------- src/main.tsx | 1 + .../sections/CriticalAlertsSection.tsx | 6 +- .../sections/PriorityDistributionReport.tsx | 10 +- .../sections/RecentActivitySection.tsx | 6 +- .../MyRequests/components/RequestCard.tsx | 44 ++-- src/pages/Requests/components/RequestCard.tsx | 28 ++- src/styles/base-layout.css | 42 ++++ vite.config.ts | 12 +- 11 files changed, 229 insertions(+), 221 deletions(-) create mode 100644 src/styles/base-layout.css diff --git a/index.html b/index.html index 5846836..8100c49 100644 --- a/index.html +++ b/index.html @@ -1,61 +1,23 @@ - - - - - - - - - Royal Enfield | Approval Portal - - - - - - - - - -
- - - + + + + + + + Royal Enfield | Approval Portal + + + + + + + +
+ + + + \ No newline at end of file diff --git a/src/components/common/FormattedDescription.tsx b/src/components/common/FormattedDescription.tsx index d384ba7..da81583 100644 --- a/src/components/common/FormattedDescription.tsx +++ b/src/components/common/FormattedDescription.tsx @@ -15,24 +15,24 @@ interface FormattedDescriptionProps { export function FormattedDescription({ content, className }: FormattedDescriptionProps) { const processedContent = React.useMemo(() => { if (!content) return ''; - + // Wrap tables that aren't already wrapped in a scrollable container using regex // Match tags that aren't already inside a .table-wrapper let processed = content; - + // Pattern to match table tags that aren't already wrapped const tablePattern = /]*>[\s\S]*?<\/table>/gi; - + processed = processed.replace(tablePattern, (match) => { // Check if this table is already wrapped if (match.includes('table-wrapper')) { return match; } - + // Wrap the table in a scrollable container - return `
${match}
`; + return `
${match}
`; }); - + return processed; }, [content]); diff --git a/src/components/ui/rich-text-editor.tsx b/src/components/ui/rich-text-editor.tsx index db1a11c..f53b199 100644 --- a/src/components/ui/rich-text-editor.tsx +++ b/src/components/ui/rich-text-editor.tsx @@ -68,55 +68,55 @@ export function RichTextEditor({ const cleanWordHTML = React.useCallback((html: string): string => { // Remove HTML comments (like Word style definitions) html = html.replace(//g, ''); - + // Remove style tags (Word CSS) html = html.replace(/]*>[\s\S]*?<\/style>/gi, ''); - + // Remove script tags html = html.replace(/]*>[\s\S]*?<\/script>/gi, ''); - + // Remove meta tags html = html.replace(/]*>/gi, ''); - + // Remove Word-specific classes and attributes html = html.replace(/\s*class="Mso[^"]*"/gi, ''); html = html.replace(/\s*class="mso[^"]*"/gi, ''); html = html.replace(/\s*style="[^"]*mso-[^"]*"/gi, ''); html = html.replace(/\s*style="[^"]*font-family:[^"]*"/gi, ''); - + // Remove xmlns attributes html = html.replace(/\s*xmlns[^=]*="[^"]*"/gi, ''); - + // Remove o:p tags (Word paragraph markers) html = html.replace(/<\/?o:p[^>]*>/gi, ''); - + // Remove v:shapes and other Word-specific elements html = html.replace(/]*>[\s\S]*?<\/v:[^>]*>/gi, ''); html = html.replace(/]*\/>/gi, ''); - + // Clean up empty paragraphs html = html.replace(/]*>\s*<\/p>/gi, ''); html = html.replace(/]*>\s*<\/div>/gi, ''); - + // Remove excessive whitespace html = html.replace(/\s+/g, ' '); html = html.trim(); - + return html; }, []); // Handle paste event to preserve formatting const handlePaste = React.useCallback((e: React.ClipboardEvent) => { e.preventDefault(); - + const clipboardData = e.clipboardData; let pastedData = clipboardData.getData('text/html') || clipboardData.getData('text/plain'); - + // Clean Word/Office metadata if HTML if (pastedData.includes('/g, ''); + .replace(//g, ''); p.innerHTML = cleaned; p.removeAttribute('style'); p.removeAttribute('class'); @@ -227,7 +224,7 @@ export function RichTextEditor({ } range.insertNode(fragment); - + // Move cursor to end of inserted content range.collapse(false); selection.removeAllRanges(); @@ -242,21 +239,21 @@ export function RichTextEditor({ // Check active formats (bold, italic, etc.) const checkActiveFormats = React.useCallback(() => { if (!editorRef.current || !isFocused) return; - + const formats = new Set(); const selection = window.getSelection(); - + if (selection && selection.rangeCount > 0) { const range = selection.getRangeAt(0); const commonAncestor = range.commonAncestorContainer; let element: HTMLElement | null = null; - + if (commonAncestor.nodeType === Node.TEXT_NODE) { element = commonAncestor.parentElement; } else { element = commonAncestor as HTMLElement; } - + while (element && element !== editorRef.current) { const tagName = element.tagName.toLowerCase(); if (tagName === 'strong' || tagName === 'b') formats.add('bold'); @@ -267,40 +264,40 @@ export function RichTextEditor({ if (tagName === 'h3') formats.add('h3'); if (tagName === 'ul') formats.add('ul'); if (tagName === 'ol') formats.add('ol'); - + const style = window.getComputedStyle(element); if (style.textAlign === 'center') formats.add('center'); if (style.textAlign === 'right') formats.add('right'); if (style.textAlign === 'left') formats.add('left'); - - // Convert RGB/RGBA to hex for comparison - const colorToHex = (color: string): string | null => { - // If already hex format - if (color.startsWith('#')) { - return color.toUpperCase(); - } - // If RGB/RGBA format - const result = color.match(/\d+/g); - if (!result || result.length < 3) return null; - const r = result[0]; - const g = result[1]; - const b = result[2]; - if (!r || !g || !b) return null; - const rHex = parseInt(r).toString(16).padStart(2, '0'); - const gHex = parseInt(g).toString(16).padStart(2, '0'); - const bHex = parseInt(b).toString(16).padStart(2, '0'); - return `#${rHex}${gHex}${bHex}`.toUpperCase(); - }; - + + // Convert RGB/RGBA to hex for comparison + const colorToHex = (color: string): string | null => { + // If already hex format + if (color.startsWith('#')) { + return color.toUpperCase(); + } + // If RGB/RGBA format + const result = color.match(/\d+/g); + if (!result || result.length < 3) return null; + const r = result[0]; + const g = result[1]; + const b = result[2]; + if (!r || !g || !b) return null; + const rHex = parseInt(r).toString(16).padStart(2, '0'); + const gHex = parseInt(g).toString(16).padStart(2, '0'); + const bHex = parseInt(b).toString(16).padStart(2, '0'); + return `#${rHex}${gHex}${bHex}`.toUpperCase(); + }; + // Check for background color (highlight) const bgColor = style.backgroundColor; // Check if background color is set and not transparent/default - if (bgColor && - bgColor !== 'rgba(0, 0, 0, 0)' && - bgColor !== 'transparent' && - bgColor !== 'rgb(255, 255, 255)' && - bgColor !== '#ffffff' && - bgColor !== '#FFFFFF') { + if (bgColor && + bgColor !== 'rgba(0, 0, 0, 0)' && + bgColor !== 'transparent' && + bgColor !== 'rgb(255, 255, 255)' && + bgColor !== '#ffffff' && + bgColor !== '#FFFFFF') { formats.add('highlight'); const hexColor = colorToHex(bgColor); if (hexColor) { @@ -321,15 +318,15 @@ export function RichTextEditor({ // Only reset if we haven't found a highlight yet setCurrentHighlightColor(null); } - + // Check for text color const textColor = style.color; // Convert to hex for comparison const hexTextColor = colorToHex(textColor); // Check if text color is set and not default black - if (textColor && hexTextColor && - textColor !== 'rgba(0, 0, 0, 0)' && - hexTextColor !== '#000000') { + if (textColor && hexTextColor && + textColor !== 'rgba(0, 0, 0, 0)' && + hexTextColor !== '#000000') { formats.add('textColor'); // Find matching color from our palette const matchedColor = HIGHLIGHT_COLORS.find(c => { @@ -350,23 +347,23 @@ export function RichTextEditor({ setCurrentTextColor(null); } } - + element = element.parentElement; } } - + setActiveFormats(formats); }, [isFocused]); // Apply formatting command const applyFormat = React.useCallback((command: string, value?: string) => { if (!editorRef.current) return; - + // Restore focus if needed if (!isFocused) { editorRef.current.focus(); } - + // Save current selection const selection = window.getSelection(); if (!selection || selection.rangeCount === 0) { @@ -374,15 +371,15 @@ export function RichTextEditor({ editorRef.current.focus(); return; } - + // Execute formatting command document.execCommand(command, false, value); - + // Update content if (editorRef.current) { onChange(editorRef.current.innerHTML); } - + // Check active formats after a short delay setTimeout(checkActiveFormats, 10); }, [isFocused, onChange, checkActiveFormats]); @@ -390,12 +387,12 @@ export function RichTextEditor({ // Apply highlight color const applyHighlight = React.useCallback((color: string) => { if (!editorRef.current) return; - + // Restore focus if needed if (!isFocused) { editorRef.current.focus(); } - + // Save current selection const selection = window.getSelection(); if (!selection || selection.rangeCount === 0) { @@ -403,26 +400,26 @@ export function RichTextEditor({ editorRef.current.focus(); return; } - + // Check if this color is already applied by checking the selection's style let isAlreadyApplied = false; if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); const commonAncestor = range.commonAncestorContainer; let element: HTMLElement | null = null; - + if (commonAncestor.nodeType === Node.TEXT_NODE) { element = commonAncestor.parentElement; } else { element = commonAncestor as HTMLElement; } - + // Check if the selected element has the same background color while (element && element !== editorRef.current) { const style = window.getComputedStyle(element); const bgColor = style.backgroundColor; - if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent' && - bgColor !== 'rgb(255, 255, 255)' && bgColor !== '#ffffff' && bgColor !== '#FFFFFF') { + if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent' && + bgColor !== 'rgb(255, 255, 255)' && bgColor !== '#ffffff' && bgColor !== '#FFFFFF') { // Convert to hex and compare const colorToHex = (c: string): string | null => { if (c.startsWith('#')) return c.toUpperCase(); @@ -446,7 +443,7 @@ export function RichTextEditor({ element = element.parentElement; } } - + // Use backColor command for highlight (background color) if (color === 'transparent' || isAlreadyApplied) { // Remove highlight - use a more aggressive approach to fully remove @@ -454,10 +451,10 @@ export function RichTextEditor({ if (!range.collapsed) { // Store the range before manipulation const contents = range.extractContents(); - + // Create a new text node or span without background color const fragment = document.createDocumentFragment(); - + // Process extracted contents to remove background colors const processNode = (node: Node) => { if (node.nodeType === Node.TEXT_NODE) { @@ -465,14 +462,14 @@ export function RichTextEditor({ } else if (node.nodeType === Node.ELEMENT_NODE) { const el = node as HTMLElement; const newEl = document.createElement(el.tagName.toLowerCase()); - + // Copy all attributes except style-related ones Array.from(el.attributes).forEach(attr => { if (attr.name !== 'style' && attr.name !== 'class') { newEl.setAttribute(attr.name, attr.value); } }); - + // Process children and copy without background color Array.from(el.childNodes).forEach(child => { const processed = processNode(child); @@ -480,27 +477,27 @@ export function RichTextEditor({ newEl.appendChild(processed); } }); - + // Remove background color if present if (el.style.backgroundColor) { newEl.style.backgroundColor = ''; } - + return newEl; } return null; }; - + Array.from(contents.childNodes).forEach(child => { const processed = processNode(child); if (processed) { fragment.appendChild(processed); } }); - + // Insert the cleaned fragment range.insertNode(fragment); - + // Also use execCommand to ensure removal document.execCommand('removeFormat', false); } else { @@ -523,21 +520,21 @@ export function RichTextEditor({ return; } } - + // Clear selection immediately after applying to prevent "sticky" highlight mode const sel = window.getSelection(); if (sel) { sel.removeAllRanges(); } - + // Update content if (editorRef.current) { onChange(editorRef.current.innerHTML); } - + // Close popover setHighlightColorOpen(false); - + // Refocus editor after a short delay and check formats setTimeout(() => { if (editorRef.current) { @@ -550,12 +547,12 @@ export function RichTextEditor({ // Apply text color const applyTextColor = React.useCallback((color: string) => { if (!editorRef.current) return; - + // Restore focus if needed if (!isFocused) { editorRef.current.focus(); } - + // Save current selection const selection = window.getSelection(); if (!selection || selection.rangeCount === 0) { @@ -563,20 +560,20 @@ export function RichTextEditor({ editorRef.current.focus(); return; } - + // Check if this color is already applied by checking the selection's style let isAlreadyApplied = false; if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); const commonAncestor = range.commonAncestorContainer; let element: HTMLElement | null = null; - + if (commonAncestor.nodeType === Node.TEXT_NODE) { element = commonAncestor.parentElement; } else { element = commonAncestor as HTMLElement; } - + // Check if the selected element has the same text color while (element && element !== editorRef.current) { const style = window.getComputedStyle(element); @@ -612,7 +609,7 @@ export function RichTextEditor({ element = element.parentElement; } } - + // Use foreColor command for text color if (color === 'transparent' || color === 'default' || isAlreadyApplied) { // Remove text color by removing format or setting to default @@ -633,15 +630,15 @@ export function RichTextEditor({ setCustomTextColor(color); } } - + // Update content if (editorRef.current) { onChange(editorRef.current.innerHTML); } - + // Close popover setTextColorOpen(false); - + // Check active formats after a short delay setTimeout(checkActiveFormats, 10); }, [isFocused, onChange, checkActiveFormats]); @@ -692,11 +689,11 @@ export function RichTextEditor({ // Handle selection change to update active formats React.useEffect(() => { if (!isFocused) return; - + const handleSelectionChange = () => { checkActiveFormats(); }; - + document.addEventListener('selectionchange', handleSelectionChange); return () => { document.removeEventListener('selectionchange', handleSelectionChange); @@ -748,7 +745,7 @@ export function RichTextEditor({ > - + {/* Highlight Color Picker */} @@ -765,8 +762,8 @@ export function RichTextEditor({ - { // Prevent closing when clicking inside popover @@ -791,7 +788,7 @@ export function RichTextEditor({ > - +
Highlight Color
{HIGHLIGHT_COLORS.map((color) => { @@ -833,7 +830,7 @@ export function RichTextEditor({ ); })}
- + {/* Remove Highlight Button - Standard pattern */} {currentHighlightColor && currentHighlightColor !== 'transparent' && (
@@ -852,7 +849,7 @@ export function RichTextEditor({
)} - + {/* Custom Color Picker */}
Custom Color
@@ -899,7 +896,7 @@ export function RichTextEditor({ // Get pasted text from clipboard const pastedText = e.clipboardData.getData('text').trim(); e.preventDefault(); - + // Process after paste event completes setTimeout(() => { // Check if it's a valid hex color with # @@ -980,7 +977,7 @@ export function RichTextEditor({
- + {/* Text Color Picker */} @@ -997,8 +994,8 @@ export function RichTextEditor({ - { const target = e.target as HTMLElement; @@ -1022,7 +1019,7 @@ export function RichTextEditor({ > - +
Text Color
{/* Default/Black Color Option - First position (standard) */} @@ -1085,7 +1082,7 @@ export function RichTextEditor({ ); })}
- + {/* Remove Text Color Button - Standard pattern */} {currentTextColor && currentTextColor !== '#000000' && (
@@ -1104,7 +1101,7 @@ export function RichTextEditor({
)} - + {/* Custom Text Color Picker */}
Custom Color
diff --git a/src/main.tsx b/src/main.tsx index 9ed172d..5993aca 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,6 +5,7 @@ import { AuthProvider } from './contexts/AuthContext'; import { AuthenticatedApp } from './pages/Auth'; import { store } from './redux/store'; import './styles/globals.css'; +import './styles/base-layout.css'; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/pages/Dashboard/components/sections/CriticalAlertsSection.tsx b/src/pages/Dashboard/components/sections/CriticalAlertsSection.tsx index 2497e32..0b0828d 100644 --- a/src/pages/Dashboard/components/sections/CriticalAlertsSection.tsx +++ b/src/pages/Dashboard/components/sections/CriticalAlertsSection.tsx @@ -33,8 +33,7 @@ export function CriticalAlertsSection({ }: CriticalAlertsSectionProps) { return ( @@ -60,8 +59,7 @@ export function CriticalAlertsSection({
1 ? 'calc(100% - 140px)' : 'calc(100% - 80px)' }} + className={`overflow-y-auto flex-1 p-4 ${pagination.totalPages > 1 ? 'max-h-[calc(100%-140px)]' : 'max-h-[calc(100%-80px)]'}`} >
{breachedRequests.length === 0 ? ( diff --git a/src/pages/Dashboard/components/sections/PriorityDistributionReport.tsx b/src/pages/Dashboard/components/sections/PriorityDistributionReport.tsx index 44afc61..4e79401 100644 --- a/src/pages/Dashboard/components/sections/PriorityDistributionReport.tsx +++ b/src/pages/Dashboard/components/sections/PriorityDistributionReport.tsx @@ -84,11 +84,7 @@ export function PriorityDistributionReport({ fill="#1f2937" textAnchor={x > cx ? 'start' : 'end'} dominantBaseline="central" - style={{ - fontSize: '14px', - fontWeight: '600', - pointerEvents: 'none', - }} + className="text-sm font-semibold pointer-events-none" > {`${name}: ${percentage}%`} @@ -102,13 +98,13 @@ export function PriorityDistributionReport({ onNavigate(`requests?priority=${data.priority}`); } }} - style={{ cursor: 'pointer' }} + className="cursor-pointer" > {priorityDistribution.map((priority, index) => ( ))} diff --git a/src/pages/Dashboard/components/sections/RecentActivitySection.tsx b/src/pages/Dashboard/components/sections/RecentActivitySection.tsx index 3532884..2fce9d6 100644 --- a/src/pages/Dashboard/components/sections/RecentActivitySection.tsx +++ b/src/pages/Dashboard/components/sections/RecentActivitySection.tsx @@ -40,8 +40,7 @@ export function RecentActivitySection({ }: RecentActivitySectionProps) { return ( @@ -73,8 +72,7 @@ export function RecentActivitySection({
1 ? 'calc(100% - 140px)' : 'calc(100% - 80px)' }} + className={`overflow-y-auto flex-1 p-4 ${pagination.totalPages > 1 ? 'max-h-[calc(100%-140px)]' : 'max-h-[calc(100%-80px)]'}`} >
{recentActivity.length === 0 ? ( diff --git a/src/pages/MyRequests/components/RequestCard.tsx b/src/pages/MyRequests/components/RequestCard.tsx index 9e36fd0..ba8d4fa 100644 --- a/src/pages/MyRequests/components/RequestCard.tsx +++ b/src/pages/MyRequests/components/RequestCard.tsx @@ -16,23 +16,29 @@ import { formatDateDDMMYYYY } from '@/utils/dateFormatter'; */ const stripHtmlTags = (html: string): string => { if (!html) return ''; - - // Check if we're in a browser environment - if (typeof document === 'undefined') { - // Fallback for SSR: use regex to strip HTML tags - return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim(); - } - - // Create a temporary div to parse HTML - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = html; - - // Get text content (automatically strips HTML tags) - let text = tempDiv.textContent || tempDiv.innerText || ''; - - // Clean up extra whitespace + + // 1. Replace block-level tags with a space to avoid merging words (e.g.
-> " ") + // This preserves readability for the card preview + let text = html.replace(/<(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hr|li|main|nav|noscript|ol|p|pre|section|table|tfoot|ul|video)[^>]*>/gi, ' '); + + // 2. Replace
with space + text = text.replace(//gi, ' '); + + // 3. Strip all other tags + text = text.replace(/<[^>]*>/g, ''); + + // 4. Clean up extra whitespace text = text.replace(/\s+/g, ' ').trim(); - + + // 5. Basic HTML entity decoding for common characters + text = text + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, "'"); + return text; }; @@ -101,18 +107,18 @@ export function RequestCard({ request, index, onViewRequest }: RequestCardProps) {(() => { const templateType = request?.templateType || (request as any)?.template_type || ''; const templateTypeUpper = templateType?.toUpperCase() || ''; - + // Direct mapping from templateType let templateLabel = 'Non-Templatized'; let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200'; - + if (templateTypeUpper === 'DEALER CLAIM') { templateLabel = 'Dealer Claim'; templateColor = 'bg-blue-100 !text-blue-700 border-blue-200'; } else if (templateTypeUpper === 'TEMPLATE') { templateLabel = 'Template'; } - + return ( { if (!html) return ''; - // Check if we're in a browser environment - if (typeof document === 'undefined') { - // Fallback for SSR: use regex to strip HTML tags - return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim(); - } + // 1. Replace block-level tags with a space to avoid merging words (e.g.
-> " ") + // This preserves readability for the card preview + let text = html.replace(/<(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hr|li|main|nav|noscript|ol|p|pre|section|table|tfoot|ul|video)[^>]*>/gi, ' '); - // Create a temporary div to parse HTML - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = html; + // 2. Replace
with space + text = text.replace(//gi, ' '); - // Get text content (automatically strips HTML tags) - let text = tempDiv.textContent || tempDiv.innerText || ''; + // 3. Strip all other tags + text = text.replace(/<[^>]*>/g, ''); - // Clean up extra whitespace + // 4. Clean up extra whitespace text = text.replace(/\s+/g, ' ').trim(); + // 5. Basic HTML entity decoding for common characters + text = text + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, "'"); + return text; }; diff --git a/src/styles/base-layout.css b/src/styles/base-layout.css new file mode 100644 index 0000000..a74dcfc --- /dev/null +++ b/src/styles/base-layout.css @@ -0,0 +1,42 @@ +/* Ensure Lucide icons render properly */ +svg { + display: inline-block; + vertical-align: middle; +} + +/* Fix for icon alignment in buttons */ +button svg { + flex-shrink: 0; +} + +/* Ensure proper text rendering */ +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +/* Fix for mobile viewport and sidebar */ +@media (max-width: 768px) { + html { + overflow-x: hidden; + } +} + +/* Ensure proper sidebar toggle behavior */ +.sidebar-toggle { + transition: all 0.3s ease-in-out; +} + +/* Fix for icon button hover states */ +button:hover svg { + transform: scale(1.05); + transition: transform 0.2s ease; +} + +/* Table wrapper for CSP-compliant horizontal scrolling */ +.table-wrapper { + overflow-x: auto; + max-width: 100%; + margin: 8px 0; +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 8e1ec1e..85e509e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -43,7 +43,7 @@ const ensureChunkOrder = () => { const reactChunk = Object.keys(bundle).find( (key) => bundle[key].type === 'chunk' && bundle[key].name === 'react-vendor' ); - + if (reactChunk) { // Ensure Radix vendor chunk depends on React vendor chunk Object.keys(bundle).forEach((key) => { @@ -75,8 +75,6 @@ export default defineConfig({ server: { port: 3000, open: true, - host: true, - allowedHosts: ['9b89f4bfd360.ngrok-free.app','c6ba819712b5.ngrok-free.app'], }, build: { outDir: 'dist', @@ -126,10 +124,10 @@ export default defineConfig({ // Radix UI components try to access React before it's initialized // Option 1: Don't split React - keep it in main bundle (most reliable) // Option 2: Keep React in separate chunk but ensure it loads first - + // For now, let's keep React in main bundle to avoid initialization issues // Only split other vendors - + // Radix UI - CRITICAL: ALL Radix packages MUST stay together in ONE chunk // This chunk will import React from the main bundle, avoiding initialization issues if (id.includes('node_modules/@radix-ui')) { @@ -173,6 +171,10 @@ export default defineConfig({ }, chunkSizeWarningLimit: 1500, // Increased limit since we have manual chunks }, + esbuild: { + // CRITICAL: Strip all legal comments to prevent "Suspicious Comments" alerts (e.g. from Redux docs) + legalComments: 'none', + }, optimizeDeps: { include: [ 'react',