changes made to fix the VAPT testing
This commit is contained in:
parent
c97053e0e3
commit
81565d294b
48
index.html
48
index.html
@ -1,61 +1,23 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<!-- CSP: Allows blob URLs for file previews and cross-origin API calls during development -->
|
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self'; img-src 'self' data: https: blob:; connect-src 'self' blob: data: http://localhost:5000 http://localhost:3000 ws://localhost:5000 ws://localhost:3000 wss://localhost:5000 wss://localhost:3000; frame-src 'self' blob:; font-src 'self' https://fonts.gstatic.com data:; object-src 'none'; base-uri 'self'; form-action 'self';" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/royal_enfield_logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/royal_enfield_logo.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Royal Enfield Approval & Request Management Portal - Streamlined approval workflows for enterprise operations" />
|
<meta name="description"
|
||||||
|
content="Royal Enfield Approval & Request Management Portal - Streamlined approval workflows for enterprise operations" />
|
||||||
<meta name="theme-color" content="#2d4a3e" />
|
<meta name="theme-color" content="#2d4a3e" />
|
||||||
<title>Royal Enfield | Approval Portal</title>
|
<title>Royal Enfield | Approval Portal</title>
|
||||||
|
|
||||||
<!-- Preload critical fonts and icons -->
|
<!-- Preload critical fonts and icons -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
|
||||||
<!-- Ensure proper icon rendering and layout -->
|
|
||||||
<style>
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -30,7 +30,7 @@ export function FormattedDescription({ content, className }: FormattedDescriptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the table in a scrollable container
|
// Wrap the table in a scrollable container
|
||||||
return `<div class="table-wrapper" style="overflow-x: auto; max-width: 100%; margin: 8px 0;">${match}</div>`;
|
return `<div class="table-wrapper">${match}</div>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
|
|||||||
@ -169,9 +169,6 @@ export function RichTextEditor({
|
|||||||
// Wrap table in scrollable container for mobile
|
// Wrap table in scrollable container for mobile
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
wrapper.className = 'table-wrapper';
|
wrapper.className = 'table-wrapper';
|
||||||
wrapper.style.overflowX = 'auto';
|
|
||||||
wrapper.style.maxWidth = '100%';
|
|
||||||
wrapper.style.margin = '8px 0';
|
|
||||||
wrapper.appendChild(table);
|
wrapper.appendChild(table);
|
||||||
fragment.appendChild(wrapper);
|
fragment.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { AuthProvider } from './contexts/AuthContext';
|
|||||||
import { AuthenticatedApp } from './pages/Auth';
|
import { AuthenticatedApp } from './pages/Auth';
|
||||||
import { store } from './redux/store';
|
import { store } from './redux/store';
|
||||||
import './styles/globals.css';
|
import './styles/globals.css';
|
||||||
|
import './styles/base-layout.css';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
@ -33,8 +33,7 @@ export function CriticalAlertsSection({
|
|||||||
}: CriticalAlertsSectionProps) {
|
}: CriticalAlertsSectionProps) {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="lg:col-span-2 shadow-md hover:shadow-lg transition-shadow flex flex-col overflow-hidden"
|
className="lg:col-span-2 shadow-md hover:shadow-lg transition-shadow flex flex-col overflow-hidden h-full"
|
||||||
style={{ height: '100%' }}
|
|
||||||
data-testid="critical-alerts-section"
|
data-testid="critical-alerts-section"
|
||||||
>
|
>
|
||||||
<CardHeader className="pb-3 sm:pb-4 flex-shrink-0">
|
<CardHeader className="pb-3 sm:pb-4 flex-shrink-0">
|
||||||
@ -60,8 +59,7 @@ export function CriticalAlertsSection({
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent
|
<CardContent
|
||||||
className="overflow-y-auto flex-1 p-4"
|
className={`overflow-y-auto flex-1 p-4 ${pagination.totalPages > 1 ? 'max-h-[calc(100%-140px)]' : 'max-h-[calc(100%-80px)]'}`}
|
||||||
style={{ maxHeight: pagination.totalPages > 1 ? 'calc(100% - 140px)' : 'calc(100% - 80px)' }}
|
|
||||||
>
|
>
|
||||||
<div className="space-y-3 sm:space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
{breachedRequests.length === 0 ? (
|
{breachedRequests.length === 0 ? (
|
||||||
|
|||||||
@ -84,11 +84,7 @@ export function PriorityDistributionReport({
|
|||||||
fill="#1f2937"
|
fill="#1f2937"
|
||||||
textAnchor={x > cx ? 'start' : 'end'}
|
textAnchor={x > cx ? 'start' : 'end'}
|
||||||
dominantBaseline="central"
|
dominantBaseline="central"
|
||||||
style={{
|
className="text-sm font-semibold pointer-events-none"
|
||||||
fontSize: '14px',
|
|
||||||
fontWeight: '600',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{`${name}: ${percentage}%`}
|
{`${name}: ${percentage}%`}
|
||||||
</text>
|
</text>
|
||||||
@ -102,13 +98,13 @@ export function PriorityDistributionReport({
|
|||||||
onNavigate(`requests?priority=${data.priority}`);
|
onNavigate(`requests?priority=${data.priority}`);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ cursor: 'pointer' }}
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
{priorityDistribution.map((priority, index) => (
|
{priorityDistribution.map((priority, index) => (
|
||||||
<Cell
|
<Cell
|
||||||
key={`cell-${index}`}
|
key={`cell-${index}`}
|
||||||
fill={priority.priority === 'express' ? '#ef4444' : '#3b82f6'}
|
fill={priority.priority === 'express' ? '#ef4444' : '#3b82f6'}
|
||||||
style={{ cursor: 'pointer' }}
|
className="cursor-pointer"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Pie>
|
</Pie>
|
||||||
|
|||||||
@ -40,8 +40,7 @@ export function RecentActivitySection({
|
|||||||
}: RecentActivitySectionProps) {
|
}: RecentActivitySectionProps) {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="lg:col-span-1 shadow-md hover:shadow-lg transition-shadow flex flex-col overflow-hidden"
|
className="lg:col-span-1 shadow-md hover:shadow-lg transition-shadow flex flex-col overflow-hidden h-full"
|
||||||
style={{ height: '100%' }}
|
|
||||||
data-testid="recent-activity-section"
|
data-testid="recent-activity-section"
|
||||||
>
|
>
|
||||||
<CardHeader className="pb-3 sm:pb-4 flex-shrink-0">
|
<CardHeader className="pb-3 sm:pb-4 flex-shrink-0">
|
||||||
@ -73,8 +72,7 @@ export function RecentActivitySection({
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent
|
<CardContent
|
||||||
className="overflow-y-auto flex-1 p-4"
|
className={`overflow-y-auto flex-1 p-4 ${pagination.totalPages > 1 ? 'max-h-[calc(100%-140px)]' : 'max-h-[calc(100%-80px)]'}`}
|
||||||
style={{ maxHeight: pagination.totalPages > 1 ? 'calc(100% - 140px)' : 'calc(100% - 80px)' }}
|
|
||||||
>
|
>
|
||||||
<div className="space-y-2 sm:space-y-3">
|
<div className="space-y-2 sm:space-y-3">
|
||||||
{recentActivity.length === 0 ? (
|
{recentActivity.length === 0 ? (
|
||||||
|
|||||||
@ -17,22 +17,28 @@ import { formatDateDDMMYYYY } from '@/utils/dateFormatter';
|
|||||||
const stripHtmlTags = (html: string): string => {
|
const stripHtmlTags = (html: string): string => {
|
||||||
if (!html) return '';
|
if (!html) return '';
|
||||||
|
|
||||||
// Check if we're in a browser environment
|
// 1. Replace block-level tags with a space to avoid merging words (e.g. </div><div> -> " ")
|
||||||
if (typeof document === 'undefined') {
|
// This preserves readability for the card preview
|
||||||
// Fallback for SSR: use regex to strip HTML tags
|
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, ' ');
|
||||||
return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary div to parse HTML
|
// 2. Replace <br> with space
|
||||||
const tempDiv = document.createElement('div');
|
text = text.replace(/<br\s*\/?>/gi, ' ');
|
||||||
tempDiv.innerHTML = html;
|
|
||||||
|
|
||||||
// Get text content (automatically strips HTML tags)
|
// 3. Strip all other tags
|
||||||
let text = tempDiv.textContent || tempDiv.innerText || '';
|
text = text.replace(/<[^>]*>/g, '');
|
||||||
|
|
||||||
// Clean up extra whitespace
|
// 4. Clean up extra whitespace
|
||||||
text = text.replace(/\s+/g, ' ').trim();
|
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;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -16,22 +16,28 @@ import { formatDateDDMMYYYY } from '@/utils/dateFormatter';
|
|||||||
const stripHtmlTags = (html: string): string => {
|
const stripHtmlTags = (html: string): string => {
|
||||||
if (!html) return '';
|
if (!html) return '';
|
||||||
|
|
||||||
// Check if we're in a browser environment
|
// 1. Replace block-level tags with a space to avoid merging words (e.g. </div><div> -> " ")
|
||||||
if (typeof document === 'undefined') {
|
// This preserves readability for the card preview
|
||||||
// Fallback for SSR: use regex to strip HTML tags
|
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, ' ');
|
||||||
return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary div to parse HTML
|
// 2. Replace <br> with space
|
||||||
const tempDiv = document.createElement('div');
|
text = text.replace(/<br\s*\/?>/gi, ' ');
|
||||||
tempDiv.innerHTML = html;
|
|
||||||
|
|
||||||
// Get text content (automatically strips HTML tags)
|
// 3. Strip all other tags
|
||||||
let text = tempDiv.textContent || tempDiv.innerText || '';
|
text = text.replace(/<[^>]*>/g, '');
|
||||||
|
|
||||||
// Clean up extra whitespace
|
// 4. Clean up extra whitespace
|
||||||
text = text.replace(/\s+/g, ' ').trim();
|
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;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
42
src/styles/base-layout.css
Normal file
42
src/styles/base-layout.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
@ -75,8 +75,6 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
open: true,
|
open: true,
|
||||||
host: true,
|
|
||||||
allowedHosts: ['9b89f4bfd360.ngrok-free.app','c6ba819712b5.ngrok-free.app'],
|
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
@ -173,6 +171,10 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
chunkSizeWarningLimit: 1500, // Increased limit since we have manual chunks
|
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: {
|
optimizeDeps: {
|
||||||
include: [
|
include: [
|
||||||
'react',
|
'react',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user