diff --git a/src/App.tsx b/src/App.tsx index ce39602..0d73595 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -74,7 +74,35 @@ export default function App() { const processedRows = rawRows.map(row => { const articleNo = String(row[articleNoIdx]); - return syncedData[articleNo] || row; + const finalRow = syncedData[articleNo] || row; + + // Format numeric/price fields to 2 decimal places + return finalRow.map((val, idx) => { + if (val === undefined || val === null || val === '') return val; + const header = (rawHeaders[idx] || '').toLowerCase(); + + // Skip Article No, Barcodes, and other code-like fields + if (header.includes('id') || header.includes('no') || header.includes('code') || + header.includes('art.') || header.includes('barcode') || header.includes('article')) { + return val; + } + + const priceKeywords = ['price', 'eur', 'cost', 'msrp', 'net', 'gross', 'netto', 'brutto', 'pp', 'pph', 'uvp', 'vpe', 'stk']; + const isPriceCol = priceKeywords.some(kw => header.includes(kw)); + + if (typeof val === 'number') { + return Number(val.toFixed(2)); + } + + if (typeof val === 'string') { + const normalized = val.trim().replace(',', '.'); + const num = parseFloat(normalized); + if (!isNaN(num) && (isPriceCol || val.includes('.') || val.includes(','))) { + return num.toFixed(2); + } + } + return val; + }); }); setAppState({ @@ -117,9 +145,40 @@ export default function App() { const data = XLSX.utils.sheet_to_json(ws, { header: 1 }); if (data.length > 0) { + const rawHeaders = data[0]; + const rawRows = data.slice(1); + + const processedRows = rawRows.map(row => { + return row.map((val, idx) => { + if (val === undefined || val === null || val === '') return val; + const header = (rawHeaders[idx] || '').toLowerCase(); + + if (header.includes('id') || header.includes('no') || header.includes('code') || + header.includes('art.') || header.includes('barcode') || header.includes('article')) { + return val; + } + + const priceKeywords = ['price', 'eur', 'cost', 'msrp', 'net', 'gross', 'netto', 'brutto', 'pp', 'pph', 'uvp', 'vpe', 'stk']; + const isPriceCol = priceKeywords.some(kw => header.includes(kw)); + + if (typeof val === 'number') { + return Number(val.toFixed(2)); + } + + if (typeof val === 'string') { + const normalized = val.trim().replace(',', '.'); + const num = parseFloat(normalized); + if (!isNaN(num) && (isPriceCol || val.includes('.') || val.includes(','))) { + return num.toFixed(2); + } + } + return val; + }); + }); + setAppState({ - headers: data[0], - data: data.slice(1), + headers: rawHeaders, + data: processedRows, fileName: file.name, fileDate: new Date(), hasUnsavedChanges: false diff --git a/src/components/MatrixView.tsx b/src/components/MatrixView.tsx index eae4d5d..cc59dc1 100644 --- a/src/components/MatrixView.tsx +++ b/src/components/MatrixView.tsx @@ -15,13 +15,50 @@ export function MatrixView({ data, headers }: MatrixViewProps) { return data.slice(start, start + pageSize); }, [data, page, pageSize]); + const formatCellValue = (val: any, header: string = '') => { + if (val === undefined || val === null || val === '') return ''; + + // Convert header to lowercase for checks + const h = header.toLowerCase(); + + // Do NOT format columns that are clearly IDs, barcodes, or codes + if (h.includes('id') || h.includes('no') || h.includes('code') || h.includes('art.') || h.includes('barcode') || h.includes('article')) { + return val; + } + + // List of keywords that typically indicate a price or numeric value to format + const priceKeywords = ['price', 'eur', 'cost', 'msrp', 'net', 'gross', 'netto', 'brutto', 'pp', 'pph', 'uvp', 'vpe', 'stk']; + const isPriceCol = priceKeywords.some(kw => h.includes(kw)); + + // Handle numbers + if (typeof val === 'number') { + return val.toFixed(2); + } + + // Handle strings that represent numbers + if (typeof val === 'string') { + // Normalize decimals (handle comma as decimal separator) + const normalized = val.trim().replace(',', '.'); + const num = parseFloat(normalized); + + if (!isNaN(num)) { + // Format if it's a price column OR if it already has a decimal separator (.) + if (isPriceCol || val.includes('.') || val.includes(',')) { + return num.toFixed(2); + } + } + } + + return val; + }; + const totalPages = Math.ceil(data.length / pageSize); return (

Matrix View

-

All data fields in their original order.

+

All data fields formatted to 2 decimal places for numbers/prices.

@@ -38,11 +75,18 @@ export function MatrixView({ data, headers }: MatrixViewProps) { {paginatedData.map((row, rowIndex) => ( - {headers.map((_, colIndex) => ( - - {row[colIndex]} - - ))} + {headers.map((header, colIndex) => { + const formattedValue = formatCellValue(row[colIndex], header); + return ( + + {formattedValue} + + ); + })} ))} {paginatedData.length === 0 && (