Feature: Add ITEM TO LOGISTIC column to Pricing Units tab.

- Integrated new column after TYPE.
- Enabled inline editing with auto-save.
- Added field to the full row EditPanel.
- Configured dynamic column detection in types.ts.
This commit is contained in:
Christian Vidal Wolf
2026-04-23 12:58:51 +02:00
parent 617eb00edb
commit aca37c4a99
4 changed files with 71 additions and 4 deletions
+54 -1
View File
@@ -61,6 +61,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
line: 100,
classification: 130,
productType: 140,
itemToLogistic: 160,
unitsOuter: 100,
outerW: 80,
outerL: 80,
@@ -86,8 +87,10 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
const [dynamicColFilters, setDynamicColFilters] = useState<Record<number, string[]>>({});
const [weightIssueFilter, setWeightIssueFilter] = useState<'all' | 'with' | 'without'>('all');
const [editingType, setEditingType] = useState<{ rowIndex: number; value: string } | null>(null);
const [editingLogistic, setEditingLogistic] = useState<{ rowIndex: number; value: string } | null>(null);
const [typeSuggestions, setTypeSuggestions] = useState<string[]>([]);
const typeInputRef = useRef<HTMLInputElement>(null);
const logisticInputRef = useRef<HTMLInputElement>(null);
const [sortConfig, setSortConfig] = useState<{ key: string | number; direction: 'asc' | 'desc' | null }>({ key: null, direction: null });
const [isSearchOpen, setIsSearchOpen] = useState(false);
@@ -487,6 +490,20 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
);
};
const startEditLogistic = (rowIndex: number, currentValue: string) => {
setEditingLogistic({ rowIndex, value: currentValue });
setTimeout(() => logisticInputRef.current?.focus(), 0);
};
const commitLogisticEdit = useCallback(async (rowIndex: number, value: string) => {
setEditingLogistic(null);
const original = data[rowIndex];
const newRow = [...original];
newRow[COLUMNS.ITEM_TO_LOGISTIC] = value;
onCaptureState(`Updated logistic info for ${original[COLUMNS.ARTICLE_NO]}`);
await onSaveRow(rowIndex, newRow);
}, [data, onSaveRow, onCaptureState, COLUMNS]);
// ── Column helpers ────────────────────────────────────────────────────────
const pricingEditableCols: DetectedCol[] = [
...(uvpIdx >= 0 ? [{ index: uvpIdx, name: headers[uvpIdx] || 'UVP' }] : []),
@@ -885,6 +902,11 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
<ResizeHandle onMouseDown={e => { e.stopPropagation(); handleResizeStart(e, 'productType', columnWidths.productType); }} />
</th>
<th style={{ width: columnWidths.itemToLogistic }} className="text-left px-3 py-3 text-xs font-semibold text-pink-400 uppercase tracking-wider whitespace-nowrap group cursor-pointer hover:bg-slate-800/50 transition-colors relative" onClick={() => handleSort('itemToLogistic')}>
<span className="flex items-center gap-1">Item to Logistic <SortIcon current={sortConfig.key === 'itemToLogistic' ? sortConfig.direction : null} /></span>
<ResizeHandle onMouseDown={e => { e.stopPropagation(); handleResizeStart(e, 'itemToLogistic', columnWidths.itemToLogistic); }} />
</th>
{pricingEditableCols.map(col => {
const colKey = `prc_${col.index}`;
const width = columnWidths[colKey] ?? 100;
@@ -1071,7 +1093,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
<th style={{ width: 180 }} className="text-left px-3 py-3 text-xs font-semibold text-red-400 uppercase tracking-wider border-b border-slate-700 whitespace-nowrap group relative">
<div className="flex items-center gap-1">
<span>Issues</span>
<span>Weight Issues</span>
<button
onClick={() => setOpenFilter(openFilter === 'weightIssue' ? null : 'weightIssue')}
className={cn(
@@ -1222,6 +1244,37 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
)}
</td>
{/* ITEM TO LOGISTIC cell */}
<td className="px-3 py-2 overflow-visible relative">
{editingLogistic?.rowIndex === dataIndex ? (
<input
ref={logisticInputRef}
type="text"
value={editingLogistic.value}
onChange={e => setEditingLogistic(prev => prev ? { ...prev, value: e.target.value } : null)}
onKeyDown={e => {
if (e.key === 'Enter') commitLogisticEdit(dataIndex, editingLogistic.value);
if (e.key === 'Escape') setEditingLogistic(null);
}}
onBlur={() => commitLogisticEdit(dataIndex, editingLogistic.value)}
className="w-full bg-slate-900 border border-pink-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-pink-500"
/>
) : (
<button
onClick={() => startEditLogistic(dataIndex, String(row[COLUMNS.ITEM_TO_LOGISTIC] || ''))}
className={cn(
'group flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors hover:bg-slate-700/60 w-full overflow-hidden',
row[COLUMNS.ITEM_TO_LOGISTIC]
? 'text-pink-300 border border-transparent hover:border-slate-600'
: 'text-slate-500 border border-dashed border-slate-600 hover:border-pink-500/50'
)}
>
<span className="truncate">{row[COLUMNS.ITEM_TO_LOGISTIC] || 'Add info…'}</span>
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
</button>
)}
</td>
{/* Pricing editable cells */}
{pricingEditableCols.map(col => {
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === col.index;