diff --git a/api/dropbox-proxy.js b/api/dropbox-proxy.js new file mode 100644 index 0000000..79e06dd --- /dev/null +++ b/api/dropbox-proxy.js @@ -0,0 +1,23 @@ +// Vercel serverless function — proxies the Dropbox file server-side (no CORS) +export default async function handler(req, res) { + const fileUrl = + 'https://dl.dropboxusercontent.com/scl/fi/nkyabkq2jdwfudof2ovrl/Data-Matrix.xlsx' + + '?rlkey=1cz5fuabwipqqivihii7sybw0&st=io0g4wvb&dl=1'; + + try { + const upstream = await fetch(fileUrl, { + headers: { 'User-Agent': 'Mozilla/5.0' }, + }); + + if (!upstream.ok) { + return res.status(upstream.status).send(`Dropbox error: ${upstream.status}`); + } + + const buffer = await upstream.arrayBuffer(); + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Cache-Control', 'no-store'); + res.send(Buffer.from(buffer)); + } catch (err) { + res.status(500).send('Proxy error: ' + err.message); + } +} diff --git a/src/App.tsx b/src/App.tsx index f23bf24..f2e1957 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,8 +24,11 @@ export default function App() { useEffect(() => { const loadDefaultData = async () => { - // Use Vite's dev proxy to avoid CORS issues - const fileUrl = '/dropbox-file/scl/fi/nkyabkq2jdwfudof2ovrl/Data-Matrix.xlsx?rlkey=1cz5fuabwipqqivihii7sybw0&st=io0g4wvb&dl=1'; + // In dev: Vite proxy handles /dropbox-file (see vite.config.ts) + // In prod: Vercel serverless function at /api/dropbox-proxy handles it + const fileUrl = import.meta.env.DEV + ? '/dropbox-file/scl/fi/nkyabkq2jdwfudof2ovrl/Data-Matrix.xlsx?rlkey=1cz5fuabwipqqivihii7sybw0&st=io0g4wvb&dl=1' + : '/api/dropbox-proxy'; setIsLoadingDefault(true); setDefaultLoadError(null); diff --git a/src/components/ProductDescriptions.tsx b/src/components/ProductDescriptions.tsx index dc7a1ac..04a157a 100644 --- a/src/components/ProductDescriptions.tsx +++ b/src/components/ProductDescriptions.tsx @@ -76,6 +76,14 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) }; const getRowColor = (row: ExcelRow) => { + // EOL Rule: OOC Classification and 0 stock (Item Available) + const classification = String(row[COLUMNS.CLASSIFICATION] || '').toUpperCase(); + const stock = Number(row[COLUMNS.ITEM_AVAILABLE] || 0); + + if (classification === 'OOC' && stock === 0) { + return 'bg-yellow-500/10 hover:bg-yellow-500/20'; // EOL Highlight + } + const fields = [row[COLUMNS.LONG_DE], row[COLUMNS.LONG_EN], row[COLUMNS.SHORT_DE], row[COLUMNS.SHORT_EN]]; const filled = fields.filter(Boolean).length; if (filled === 4) return 'bg-green-900/10 hover:bg-green-900/20'; @@ -83,8 +91,17 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) return 'bg-yellow-900/10 hover:bg-yellow-900/20'; }; - const Badge = ({ content }: { content: any }) => { + const Badge = ({ content, row }: { content: any, row: ExcelRow }) => { if (content) return ✓; + + // EOL Exception + const classification = String(row[COLUMNS.CLASSIFICATION] || '').toUpperCase(); + const stock = Number(row[COLUMNS.ITEM_AVAILABLE] || 0); + + if (classification === 'OOC' && stock === 0) { + return EOL not neccessary; + } + return ✗ Missing; }; @@ -184,10 +201,10 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)