mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 11:45:24 +02:00
fix(pricing): edit units inline
This commit is contained in:
@@ -858,11 +858,16 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
return isNaN(n) ? String(val) : n.toFixed(2);
|
return isNaN(n) ? String(val) : n.toFixed(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatUnits = (val: any) => {
|
const formatPlainValue = (val: any) => {
|
||||||
if (val === undefined || val === null || val === '') return '—';
|
if (val === undefined || val === null || val === '') return '—';
|
||||||
return String(val);
|
return String(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getEditableCellValue = (val: any) => {
|
||||||
|
if (val === undefined || val === null) return '';
|
||||||
|
return String(val);
|
||||||
|
};
|
||||||
|
|
||||||
const unitBadge = (val: any, label: string) => {
|
const unitBadge = (val: any, label: string) => {
|
||||||
const n = Number(val);
|
const n = Number(val);
|
||||||
if (!val || n === 0) return (
|
if (!val || n === 0) return (
|
||||||
@@ -2188,31 +2193,239 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
|
|
||||||
{/* Units columns */}
|
{/* Units columns */}
|
||||||
<td className={cn("px-3 py-2.5", isPinned('unitsOuter') && "sticky bg-slate-800")} style={isPinned('unitsOuter') ? { left: getStickyLeft('unitsOuter') ?? 0, zIndex: getStickyRank('unitsOuter') ?? 0 } : {}}>
|
<td className={cn("px-3 py-2.5", isPinned('unitsOuter') && "sticky bg-slate-800")} style={isPinned('unitsOuter') ? { left: getStickyLeft('unitsOuter') ?? 0, zIndex: getStickyRank('unitsOuter') ?? 0 } : {}}>
|
||||||
{unitOuterBadge(row[unitsOuterIdx])}
|
{(() => {
|
||||||
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === unitsOuterIdx;
|
||||||
|
const isSaving = savingCell?.rowIndex === dataIndex && savingCell?.colIndex === unitsOuterIdx;
|
||||||
|
const value = getEditableCellValue(row[unitsOuterIdx]);
|
||||||
|
return isEditing ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingCell.value}
|
||||||
|
onChange={e => setEditingCell(prev => prev ? { ...prev, value: e.target.value } : null)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={commitEdit}
|
||||||
|
className="w-full bg-slate-900 border border-blue-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(dataIndex, unitsOuterIdx, value)}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between gap-1 px-2 py-1 rounded text-xs transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
|
||||||
|
!value || Number(value) === 0
|
||||||
|
? 'text-amber-400 border border-amber-500/40 bg-amber-500/5'
|
||||||
|
: 'text-slate-200 border border-transparent hover:border-slate-600'
|
||||||
|
)}
|
||||||
|
title={isSaving ? 'Saving…' : 'Click to edit Units/Outer'}
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<span className="text-slate-500 italic truncate">saving…</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{unitOuterBadge(row[unitsOuterIdx])}
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</td>
|
</td>
|
||||||
<td className={cn("px-3 py-2.5 text-slate-400 font-mono text-xs truncate", isPinned('outerW') && "sticky bg-slate-800")} style={isPinned('outerW') ? { left: getStickyLeft('outerW') ?? 0, zIndex: getStickyRank('outerW') ?? 0 } : {}}>
|
<td className={cn("px-3 py-2.5 text-slate-400 font-mono text-xs truncate", isPinned('outerW') && "sticky bg-slate-800")} style={isPinned('outerW') ? { left: getStickyLeft('outerW') ?? 0, zIndex: getStickyRank('outerW') ?? 0 } : {}}>
|
||||||
{row[COLUMNS.OUTER_W] ?? '-'}
|
{(() => {
|
||||||
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === COLUMNS.OUTER_W;
|
||||||
|
const isSaving = savingCell?.rowIndex === dataIndex && savingCell?.colIndex === COLUMNS.OUTER_W;
|
||||||
|
const value = getEditableCellValue(row[COLUMNS.OUTER_W]);
|
||||||
|
return isEditing ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingCell.value}
|
||||||
|
onChange={e => setEditingCell(prev => prev ? { ...prev, value: e.target.value } : null)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={commitEdit}
|
||||||
|
className="w-full bg-slate-900 border border-blue-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(dataIndex, COLUMNS.OUTER_W, value)}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between gap-1 px-2 py-1 rounded text-xs transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
|
||||||
|
!value || Number(value) === 0
|
||||||
|
? 'text-amber-400 border border-amber-500/40 bg-amber-500/5'
|
||||||
|
: 'text-slate-200 border border-transparent hover:border-slate-600'
|
||||||
|
)}
|
||||||
|
title={isSaving ? 'Saving…' : 'Click to edit Outer W'}
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<span className="text-slate-500 italic truncate">saving…</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>{formatPlainValue(row[COLUMNS.OUTER_W])}</span>
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</td>
|
</td>
|
||||||
<td className={cn("px-3 py-2.5 text-slate-400 font-mono text-xs truncate", isPinned('outerL') && "sticky bg-slate-800")} style={isPinned('outerL') ? { left: getStickyLeft('outerL') ?? 0, zIndex: getStickyRank('outerL') ?? 0 } : {}}>
|
<td className={cn("px-3 py-2.5 text-slate-400 font-mono text-xs truncate", isPinned('outerL') && "sticky bg-slate-800")} style={isPinned('outerL') ? { left: getStickyLeft('outerL') ?? 0, zIndex: getStickyRank('outerL') ?? 0 } : {}}>
|
||||||
{row[COLUMNS.OUTER_L] ?? '-'}
|
{(() => {
|
||||||
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === COLUMNS.OUTER_L;
|
||||||
|
const isSaving = savingCell?.rowIndex === dataIndex && savingCell?.colIndex === COLUMNS.OUTER_L;
|
||||||
|
const value = getEditableCellValue(row[COLUMNS.OUTER_L]);
|
||||||
|
return isEditing ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingCell.value}
|
||||||
|
onChange={e => setEditingCell(prev => prev ? { ...prev, value: e.target.value } : null)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={commitEdit}
|
||||||
|
className="w-full bg-slate-900 border border-blue-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(dataIndex, COLUMNS.OUTER_L, value)}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between gap-1 px-2 py-1 rounded text-xs transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
|
||||||
|
!value || Number(value) === 0
|
||||||
|
? 'text-amber-400 border border-amber-500/40 bg-amber-500/5'
|
||||||
|
: 'text-slate-200 border border-transparent hover:border-slate-600'
|
||||||
|
)}
|
||||||
|
title={isSaving ? 'Saving…' : 'Click to edit Outer L'}
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<span className="text-slate-500 italic truncate">saving…</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>{formatPlainValue(row[COLUMNS.OUTER_L])}</span>
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</td>
|
</td>
|
||||||
<td className={cn("px-3 py-2.5 text-slate-400 font-mono text-xs truncate", isPinned('outerH') && "sticky bg-slate-800")} style={isPinned('outerH') ? { left: getStickyLeft('outerH') ?? 0, zIndex: getStickyRank('outerH') ?? 0 } : {}}>
|
<td className={cn("px-3 py-2.5 text-slate-400 font-mono text-xs truncate", isPinned('outerH') && "sticky bg-slate-800")} style={isPinned('outerH') ? { left: getStickyLeft('outerH') ?? 0, zIndex: getStickyRank('outerH') ?? 0 } : {}}>
|
||||||
{row[COLUMNS.OUTER_H] ?? '-'}
|
{(() => {
|
||||||
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === COLUMNS.OUTER_H;
|
||||||
|
const isSaving = savingCell?.rowIndex === dataIndex && savingCell?.colIndex === COLUMNS.OUTER_H;
|
||||||
|
const value = getEditableCellValue(row[COLUMNS.OUTER_H]);
|
||||||
|
return isEditing ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingCell.value}
|
||||||
|
onChange={e => setEditingCell(prev => prev ? { ...prev, value: e.target.value } : null)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={commitEdit}
|
||||||
|
className="w-full bg-slate-900 border border-blue-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(dataIndex, COLUMNS.OUTER_H, value)}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between gap-1 px-2 py-1 rounded text-xs transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
|
||||||
|
!value || Number(value) === 0
|
||||||
|
? 'text-amber-400 border border-amber-500/40 bg-amber-500/5'
|
||||||
|
: 'text-slate-200 border border-transparent hover:border-slate-600'
|
||||||
|
)}
|
||||||
|
title={isSaving ? 'Saving…' : 'Click to edit Outer H'}
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<span className="text-slate-500 italic truncate">saving…</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>{formatPlainValue(row[COLUMNS.OUTER_H])}</span>
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Container units columns */}
|
{/* Container units columns */}
|
||||||
{containerCols.map(col => {
|
{containerCols.map(col => {
|
||||||
const colKey = `con_${col.index}`;
|
const colKey = `con_${col.index}`;
|
||||||
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === col.index;
|
||||||
|
const isSaving = savingCell?.rowIndex === dataIndex && savingCell?.colIndex === col.index;
|
||||||
|
const value = getEditableCellValue(row[col.index]);
|
||||||
return (
|
return (
|
||||||
<td key={col.index} className={cn("px-3 py-2.5 truncate", isPinned(colKey) && "sticky bg-slate-800")} style={isPinned(colKey) ? { left: getStickyLeft(colKey) ?? 0, zIndex: getStickyRank(colKey) ?? 0 } : {}}>
|
<td key={col.index} className={cn("px-3 py-2.5 truncate", isPinned(colKey) && "sticky bg-slate-800")} style={isPinned(colKey) ? { left: getStickyLeft(colKey) ?? 0, zIndex: getStickyRank(colKey) ?? 0 } : {}}>
|
||||||
{unitBadge(row[col.index], col.name)}
|
{isEditing ? (
|
||||||
</td>
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingCell.value}
|
||||||
|
onChange={e => setEditingCell(prev => prev ? { ...prev, value: e.target.value } : null)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={commitEdit}
|
||||||
|
className="w-full bg-slate-900 border border-blue-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(dataIndex, col.index, value)}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between gap-1 px-2 py-1 rounded text-xs transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
|
||||||
|
!value || Number(value) === 0
|
||||||
|
? 'text-amber-400 border border-amber-500/40 bg-amber-500/5'
|
||||||
|
: 'text-slate-200 border border-transparent hover:border-slate-600'
|
||||||
|
)}
|
||||||
|
title={isSaving ? 'Saving…' : `Click to edit ${col.name}`}
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<span className="text-slate-500 italic truncate">saving…</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{unitBadge(row[col.index], col.name)}
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{/* MOQ cell */}
|
{/* MOQ cell */}
|
||||||
<td className={cn("px-3 py-2.5 truncate", isPinned('moq') && "sticky bg-slate-800")} style={isPinned('moq') ? { left: getStickyLeft('moq') ?? 0, zIndex: getStickyRank('moq') ?? 0 } : {}}>
|
<td className={cn("px-3 py-2.5 truncate", isPinned('moq') && "sticky bg-slate-800")} style={isPinned('moq') ? { left: getStickyLeft('moq') ?? 0, zIndex: getStickyRank('moq') ?? 0 } : {}}>
|
||||||
{row[COLUMNS.MOQ] !== undefined && row[COLUMNS.MOQ] !== null ? row[COLUMNS.MOQ] : '-'}
|
{(() => {
|
||||||
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === COLUMNS.MOQ;
|
||||||
|
const isSaving = savingCell?.rowIndex === dataIndex && savingCell?.colIndex === COLUMNS.MOQ;
|
||||||
|
const value = getEditableCellValue(row[COLUMNS.MOQ]);
|
||||||
|
return isEditing ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingCell.value}
|
||||||
|
onChange={e => setEditingCell(prev => prev ? { ...prev, value: e.target.value } : null)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={commitEdit}
|
||||||
|
className="w-full bg-slate-900 border border-blue-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(dataIndex, COLUMNS.MOQ, value)}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between gap-1 px-2 py-1 rounded text-xs font-mono transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
|
||||||
|
!value || Number(value) === 0
|
||||||
|
? 'text-amber-400 border border-amber-500/40 bg-amber-500/5'
|
||||||
|
: 'text-slate-200 border border-transparent hover:border-slate-600'
|
||||||
|
)}
|
||||||
|
title={isSaving ? 'Saving…' : 'Click to edit MOQ'}
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<span className="text-slate-500 italic truncate">saving…</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>{formatPlainValue(row[COLUMNS.MOQ])}</span>
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Issues column (weight only) */}
|
{/* Issues column (weight only) */}
|
||||||
@@ -2334,7 +2547,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
<p className="text-xs text-slate-500 pb-1">
|
<p className="text-xs text-slate-500 pb-1">
|
||||||
Showing {paginatedRows.length} of {sortedRows.length} products {totalPages > 1 && `(${currentPage}/${totalPages})`}
|
Showing {paginatedRows.length} of {sortedRows.length} products {totalPages > 1 && `(${currentPage}/${totalPages})`}
|
||||||
{pricingEditableCols.length > 0 && (
|
{pricingEditableCols.length > 0 && (
|
||||||
<> · Click any <span className="text-blue-400">price cell</span> to edit inline</>
|
<> · Click editable <span className="text-blue-400">price / units / MOQ</span> cells to edit inline</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{dragSelectedRows.size > 0 && (
|
{dragSelectedRows.size > 0 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user