fix: remove rowStatuses prop from ProductDescriptions to fix app crash

The rowStatuses prop was merged from feature branch but is incompatible
with main. Removed it and restored original tbody structure while keeping
the Present/Missing filter fix.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Christian Vidal Wolf
2026-04-08 19:32:28 +02:00
co-authored by Qwen-Coder
parent 0d47f5be33
commit 71a4897f28
2 changed files with 60 additions and 71 deletions
+29 -30
View File
@@ -81,22 +81,22 @@ export default function App() {
const articleNo = String(row[articleNoIdx]); const articleNo = String(row[articleNoIdx]);
const synced = syncedData[articleNo]; const synced = syncedData[articleNo];
const finalRow = synced ? synced.data : row; const finalRow = synced ? synced.data : row;
// Sync status_check // Sync status_check
if (synced && synced.status === 'pending') { if (synced && synced.status === 'pending') {
setRowStatuses(prev => ({ ...prev, [articleNo]: 'pending' })); setRowStatuses(prev => ({ ...prev, [articleNo]: 'pending' }));
} }
// Format numeric/price fields to 2 decimal places // Format numeric/price fields to 2 decimal places
return finalRow.map((val, idx) => { return finalRow.map((val, idx) => {
if (val === undefined || val === null || val === '') return val; if (val === undefined || val === null || val === '') return val;
const header = (rawHeaders[idx] || '').toLowerCase(); const header = (rawHeaders[idx] || '').toLowerCase();
// Skip Article No, Barcodes, and other code-like fields // Skip Article No, Barcodes, and other code-like fields
// But allow if it's a weight/measure column (e.g. Article NW (kg)) // But allow if it's a weight/measure column (e.g. Article NW (kg))
if ((header.includes('id') || header.includes('no') || header.includes('code') || if ((header.includes('id') || header.includes('no') || header.includes('code') ||
header.includes('art.') || header.includes('barcode') || header.includes('article')) && header.includes('art.') || header.includes('barcode') || header.includes('article')) &&
!(header.includes('nw') || header.includes('gw') || header.includes('weight') || header.includes('kg'))) { !(header.includes('nw') || header.includes('gw') || header.includes('weight') || header.includes('kg'))) {
return val; return val;
} }
@@ -106,7 +106,7 @@ export default function App() {
if (typeof val === 'number') { if (typeof val === 'number') {
return Number(val.toFixed(2)); return Number(val.toFixed(2));
} }
if (typeof val === 'string') { if (typeof val === 'string') {
const normalized = val.trim().replace(',', '.'); const normalized = val.trim().replace(',', '.');
const num = parseFloat(normalized); const num = parseFloat(normalized);
@@ -156,18 +156,18 @@ export default function App() {
const wsname = wb.SheetNames[0]; const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname]; const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_json<any[]>(ws, { header: 1 }); const data = XLSX.utils.sheet_to_json<any[]>(ws, { header: 1 });
if (data.length > 0) { if (data.length > 0) {
const rawHeaders = data[0]; const rawHeaders = data[0];
const rawRows = data.slice(1); const rawRows = data.slice(1);
const processedRows = rawRows.map(row => { const processedRows = rawRows.map(row => {
return row.map((val, idx) => { return row.map((val, idx) => {
if (val === undefined || val === null || val === '') return val; if (val === undefined || val === null || val === '') return val;
const header = (rawHeaders[idx] || '').toLowerCase(); const header = (rawHeaders[idx] || '').toLowerCase();
if (header.includes('id') || header.includes('no') || header.includes('code') || if (header.includes('id') || header.includes('no') || header.includes('code') ||
header.includes('art.') || header.includes('barcode') || header.includes('article')) { header.includes('art.') || header.includes('barcode') || header.includes('article')) {
// But allow if it's a weight/measure column (e.g. Article NW (kg)) // But allow if it's a weight/measure column (e.g. Article NW (kg))
if (!(header.includes('nw') || header.includes('gw') || header.includes('weight') || header.includes('kg'))) { if (!(header.includes('nw') || header.includes('gw') || header.includes('weight') || header.includes('kg'))) {
return val; return val;
@@ -190,7 +190,7 @@ export default function App() {
if (typeof val === 'number') { if (typeof val === 'number') {
return Number(val.toFixed(2)); return Number(val.toFixed(2));
} }
if (typeof val === 'string') { if (typeof val === 'string') {
const normalized = val.trim().replace(',', '.'); const normalized = val.trim().replace(',', '.');
const num = parseFloat(normalized); const num = parseFloat(normalized);
@@ -217,15 +217,15 @@ export default function App() {
const handleExport = () => { const handleExport = () => {
if (appState.data.length === 0) return; if (appState.data.length === 0) return;
const wsData = [appState.headers, ...appState.data]; const wsData = [appState.headers, ...appState.data];
const ws = XLSX.utils.aoa_to_sheet(wsData); const ws = XLSX.utils.aoa_to_sheet(wsData);
const wb = XLSX.utils.book_new(); const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Products'); XLSX.utils.book_append_sheet(wb, ws, 'Products');
const dateStr = new Date().toISOString().split('T')[0]; const dateStr = new Date().toISOString().split('T')[0];
XLSX.writeFile(wb, `CRAZE_Products_Updated_${dateStr}.xlsx`); XLSX.writeFile(wb, `CRAZE_Products_Updated_${dateStr}.xlsx`);
// 3. Post-export: Reset pending statuses in Supabase // 3. Post-export: Reset pending statuses in Supabase
console.log('Resetting pending statuses in Supabase...'); console.log('Resetting pending statuses in Supabase...');
resetAllPendingRows().then(success => { resetAllPendingRows().then(success => {
@@ -254,12 +254,12 @@ export default function App() {
// 2. Persist to Supabase // 2. Persist to Supabase
const articleNo = String(updatedRow[COLUMNS.ARTICLE_NO]); const articleNo = String(updatedRow[COLUMNS.ARTICLE_NO]);
console.log(`Saving article ${articleNo} to Supabase...`); console.log(`Saving article ${articleNo} to Supabase...`);
// Update local status to pending // Update local status to pending
setRowStatuses(prev => ({ ...prev, [articleNo]: 'pending' })); setRowStatuses(prev => ({ ...prev, [articleNo]: 'pending' }));
const success = await saveRowToSupabase(articleNo, updatedRow); const success = await saveRowToSupabase(articleNo, updatedRow);
if (success) { if (success) {
console.log(`Successfully saved ${articleNo}`); console.log(`Successfully saved ${articleNo}`);
setAppState(prev => ({ ...prev, hasUnsavedChanges: false })); setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
@@ -283,7 +283,7 @@ export default function App() {
const handleUndo = () => { const handleUndo = () => {
if (undoHistory.length === 0) return; if (undoHistory.length === 0) return;
const [lastAction, ...remainingHistory] = undoHistory; const [lastAction, ...remainingHistory] = undoHistory;
setAppState(prev => ({ setAppState(prev => ({
...prev, ...prev,
@@ -366,10 +366,9 @@ export default function App() {
) : ( ) : (
<> <>
{activeModule === 'descriptions' && ( {activeModule === 'descriptions' && (
<ProductDescriptions <ProductDescriptions
data={appState.data} data={appState.data}
onEdit={(index) => setEditingRowIndex(index)} onEdit={(index) => setEditingRowIndex(index)}
rowStatuses={rowStatuses}
/> />
)} )}
{activeModule === 'matrix' && ( {activeModule === 'matrix' && (
@@ -409,15 +408,15 @@ export default function App() {
</main> </main>
</div> </div>
<UndoToast <UndoToast
undoState={undoHistory[0] || null} undoState={undoHistory[0] || null}
onUndo={handleUndo} onUndo={handleUndo}
onClose={() => setUndoHistory([])} onClose={() => setUndoHistory([])}
/> />
{editingRowIndex !== null && ( {editingRowIndex !== null && (
<EditPanel <EditPanel
row={appState.data[editingRowIndex]} row={appState.data[editingRowIndex]}
rowIndex={editingRowIndex} rowIndex={editingRowIndex}
onSave={handleSaveRow} onSave={handleSaveRow}
onClose={() => setEditingRowIndex(null)} onClose={() => setEditingRowIndex(null)}
+31 -41
View File
@@ -304,47 +304,37 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-slate-700/50"> <tbody className="divide-y divide-slate-700/50">
{paginatedData.map(({ row, index }) => { {paginatedData.map(({ row, index }) => (
const isPending = rowStatuses[String(row[COLUMNS.ARTICLE_NO])] === 'pending'; <tr key={index} className={cn("transition-colors", getRowColor(row))}>
return ( <td className="px-4 py-3 font-mono text-slate-300 text-xs">{row[COLUMNS.ARTICLE_NO]}</td>
<tr <td className="px-4 py-3 font-medium text-white max-w-[200px] truncate" title={row[COLUMNS.ARTICLE_NAME]}>{row[COLUMNS.ARTICLE_NAME]}</td>
key={index} <td className="px-4 py-3 text-slate-300 text-xs">{row[COLUMNS.LINE]}</td>
className={cn( <td className="px-4 py-3 text-slate-300 text-xs truncate max-w-[120px]" title={row[COLUMNS.LICENSE]}>{row[COLUMNS.LICENSE] || '—'}</td>
"transition-colors", <td className="px-4 py-3">
getRowColor(row), <span className={cn(
isPending ? "bg-yellow-400/20 border-l-4 border-l-yellow-400" : "" "px-2 py-0.5 rounded text-[10px] font-bold border",
)} String(row[COLUMNS.CLASSIFICATION]).includes('OOC')
> ? "bg-amber-500/10 text-amber-500 border-amber-500/20"
<td className="px-4 py-3 font-mono text-slate-300 text-xs">{row[COLUMNS.ARTICLE_NO]}</td> : "bg-slate-700/50 text-slate-400 border-slate-600/50"
<td className="px-4 py-3 font-medium text-white max-w-[200px] truncate" title={row[COLUMNS.ARTICLE_NAME]}>{row[COLUMNS.ARTICLE_NAME]}</td> )}>
<td className="px-4 py-3 text-slate-300 text-xs">{row[COLUMNS.LINE]}</td> {row[COLUMNS.CLASSIFICATION] || '—'}
<td className="px-4 py-3 text-slate-300 text-xs truncate max-w-[120px]" title={row[COLUMNS.LICENSE]}>{row[COLUMNS.LICENSE] || '—'}</td> </span>
<td className="px-4 py-3"> </td>
<span className={cn( <td className="px-4 py-3"><Badge content={row[COLUMNS.LONG_DE]} row={row} /></td>
"px-2 py-0.5 rounded text-[10px] font-bold border", <td className="px-4 py-3"><Badge content={row[COLUMNS.LONG_EN]} row={row} /></td>
String(row[COLUMNS.CLASSIFICATION]).includes('OOC') <td className="px-4 py-3"><Badge content={row[COLUMNS.SHORT_DE]} row={row} /></td>
? "bg-amber-500/10 text-amber-500 border-amber-500/20" <td className="px-4 py-3"><Badge content={row[COLUMNS.SHORT_EN]} row={row} /></td>
: "bg-slate-700/50 text-slate-400 border-slate-600/50" <td className="px-4 py-3 text-right">
)}> <button
{row[COLUMNS.CLASSIFICATION] || '—'} onClick={() => onEdit(index)}
</span> className="inline-flex items-center gap-2 px-3 py-1.5 bg-blue-600/10 text-blue-400 hover:bg-blue-600 hover:text-white rounded-md transition-colors font-medium"
</td> >
<td className="px-4 py-3"><Badge content={row[COLUMNS.LONG_DE]} row={row} /></td> <Edit2 className="w-4 h-4" />
<td className="px-4 py-3"><Badge content={row[COLUMNS.LONG_EN]} row={row} /></td> Edit
<td className="px-4 py-3"><Badge content={row[COLUMNS.SHORT_DE]} row={row} /></td> </button>
<td className="px-4 py-3"><Badge content={row[COLUMNS.SHORT_EN]} row={row} /></td> </td>
<td className="px-4 py-3 text-right"> </tr>
<button ))}
onClick={() => onEdit(index)}
className="inline-flex items-center gap-2 px-3 py-1.5 bg-blue-600/10 text-blue-400 hover:bg-blue-600 hover:text-white rounded-md transition-colors font-medium"
>
<Edit2 className="w-4 h-4" />
Edit
</button>
</td>
</tr>
);
})}
{paginatedData.length === 0 && ( {paginatedData.length === 0 && (
<tr> <tr>
<td colSpan={9} className="px-4 py-8 text-center text-slate-500"> <td colSpan={9} className="px-4 py-8 text-center text-slate-500">