mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 14:45:24 +02:00
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:
+2
-1
@@ -147,7 +147,8 @@ export default function App() {
|
||||
resolvedCols.VERIFIED_DIMS,
|
||||
resolvedCols.VALIDATED_CHECK,
|
||||
resolvedCols.VALIDATED_NOTE,
|
||||
resolvedCols.PRODUCT_TYPE
|
||||
resolvedCols.PRODUCT_TYPE,
|
||||
resolvedCols.ITEM_TO_LOGISTIC
|
||||
]);
|
||||
|
||||
headers.forEach((h: any, i: number) => {
|
||||
|
||||
@@ -143,6 +143,8 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
outerH: row[COLUMNS.OUTER_H] !== undefined && row[COLUMNS.OUTER_H] !== null ? String(row[COLUMNS.OUTER_H]) : '',
|
||||
unitsOuter: row[COLUMNS.UNITS_OUTER] !== undefined && row[COLUMNS.UNITS_OUTER] !== null ? String(row[COLUMNS.UNITS_OUTER]) : '',
|
||||
moq: row[COLUMNS.MOQ] !== undefined && row[COLUMNS.MOQ] !== null ? String(row[COLUMNS.MOQ]) : '',
|
||||
productType: row[COLUMNS.PRODUCT_TYPE] !== undefined && row[COLUMNS.PRODUCT_TYPE] !== null ? String(row[COLUMNS.PRODUCT_TYPE]) : '',
|
||||
itemToLogistic: row[COLUMNS.ITEM_TO_LOGISTIC] !== undefined && row[COLUMNS.ITEM_TO_LOGISTIC] !== null ? String(row[COLUMNS.ITEM_TO_LOGISTIC]) : '',
|
||||
});
|
||||
|
||||
const [loadingField, setLoadingField] = useState<string | null>(null);
|
||||
@@ -178,6 +180,8 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
moq: COLUMNS.MOQ,
|
||||
detailsDe: COLUMNS.DETAILS_DE,
|
||||
detailsEn: COLUMNS.DETAILS_EN,
|
||||
productType: COLUMNS.PRODUCT_TYPE,
|
||||
itemToLogistic: COLUMNS.ITEM_TO_LOGISTIC,
|
||||
};
|
||||
const colIndex = (colMap as Record<string, number>)[field as string];
|
||||
if (colIndex === undefined) return false;
|
||||
@@ -307,6 +311,8 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
newRow[COLUMNS.MOQ] = formData.moq;
|
||||
newRow[COLUMNS.DETAILS_DE] = formData.detailsDe;
|
||||
newRow[COLUMNS.DETAILS_EN] = formData.detailsEn;
|
||||
newRow[COLUMNS.PRODUCT_TYPE] = formData.productType;
|
||||
newRow[COLUMNS.ITEM_TO_LOGISTIC] = formData.itemToLogistic;
|
||||
onSave(rowIndex, newRow);
|
||||
};
|
||||
|
||||
@@ -393,6 +399,11 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
<DimensionInput label="Units per Outer" value={formData.unitsOuter} field="unitsOuter" isModified={isModified('unitsOuter')} onChange={(val) => setFormData(p => ({...p, unitsOuter: val}))} />
|
||||
<DimensionInput label="MOQ" value={formData.moq} field="moq" isModified={isModified('moq')} onChange={(val) => setFormData(p => ({...p, moq: val}))} />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 pt-2 border-t border-slate-700/30">
|
||||
<DimensionInput label="Type" value={formData.productType} field="productType" isModified={isModified('productType')} onChange={(val) => setFormData(p => ({...p, productType: val}))} />
|
||||
<DimensionInput label="Item to Logistic" value={formData.itemToLogistic} field="itemToLogistic" isModified={isModified('itemToLogistic')} onChange={(val) => setFormData(p => ({...p, itemToLogistic: val}))} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FieldEditor
|
||||
|
||||
@@ -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;
|
||||
|
||||
+4
-2
@@ -40,7 +40,8 @@ export const COLUMNS = {
|
||||
VERIFIED_DIMS: 100,
|
||||
VALIDATED_CHECK: 101,
|
||||
VALIDATED_NOTE: 102,
|
||||
PRODUCT_TYPE: 103
|
||||
PRODUCT_TYPE: 103,
|
||||
ITEM_TO_LOGISTIC: 104
|
||||
};
|
||||
|
||||
// Search patterns for dynamic detection
|
||||
@@ -75,7 +76,8 @@ export const COLUMN_PATTERNS: Record<keyof typeof COLUMNS, string[]> = {
|
||||
VERIFIED_DIMS: ['verified'],
|
||||
VALIDATED_CHECK: ['validated'],
|
||||
VALIDATED_NOTE: ['note'],
|
||||
PRODUCT_TYPE: ['product', 'type']
|
||||
PRODUCT_TYPE: ['product', 'type'],
|
||||
ITEM_TO_LOGISTIC: ['item', 'to', 'logistic']
|
||||
};
|
||||
|
||||
export function resolveColumnIndices(headers: string[]): typeof COLUMNS {
|
||||
|
||||
Reference in New Issue
Block a user