mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 12:35:25 +02:00
feat: add TYPE column to Pricing view with autocomplete suggestions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
77819f5d3a
commit
f15871f89f
+2
-1
@@ -126,7 +126,8 @@ export default function App() {
|
|||||||
COLUMNS.UNITS_OUTER, COLUMNS.MOQ,
|
COLUMNS.UNITS_OUTER, COLUMNS.MOQ,
|
||||||
COLUMNS.VERIFIED_DIMS,
|
COLUMNS.VERIFIED_DIMS,
|
||||||
COLUMNS.VALIDATED_CHECK,
|
COLUMNS.VALIDATED_CHECK,
|
||||||
COLUMNS.VALIDATED_NOTE
|
COLUMNS.VALIDATED_NOTE,
|
||||||
|
COLUMNS.PRODUCT_TYPE
|
||||||
]);
|
]);
|
||||||
|
|
||||||
headers.forEach((h: any, i: number) => {
|
headers.forEach((h: any, i: number) => {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
articleName: 220,
|
articleName: 220,
|
||||||
line: 100,
|
line: 100,
|
||||||
classification: 130,
|
classification: 130,
|
||||||
|
productType: 140,
|
||||||
unitsOuter: 100,
|
unitsOuter: 100,
|
||||||
outerW: 80,
|
outerW: 80,
|
||||||
outerL: 80,
|
outerL: 80,
|
||||||
@@ -79,6 +80,9 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
const [outerLFilter, setOuterLFilter] = useState<string[]>([]);
|
const [outerLFilter, setOuterLFilter] = useState<string[]>([]);
|
||||||
const [outerHFilter, setOuterHFilter] = useState<string[]>([]);
|
const [outerHFilter, setOuterHFilter] = useState<string[]>([]);
|
||||||
const [dynamicColFilters, setDynamicColFilters] = useState<Record<number, string[]>>({});
|
const [dynamicColFilters, setDynamicColFilters] = useState<Record<number, string[]>>({});
|
||||||
|
const [editingType, setEditingType] = useState<{ rowIndex: number; value: string } | null>(null);
|
||||||
|
const [typeSuggestions, setTypeSuggestions] = useState<string[]>([]);
|
||||||
|
const typeInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||||
const [selectedSearchItems, setSelectedSearchItems] = useState<Set<number>>(new Set());
|
const [selectedSearchItems, setSelectedSearchItems] = useState<Set<number>>(new Set());
|
||||||
@@ -354,6 +358,35 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
if (e.key === 'Escape') cancelEdit();
|
if (e.key === 'Escape') cancelEdit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── All unique product types (for autocomplete) ───────────────────────────
|
||||||
|
const allProductTypes = useMemo(() =>
|
||||||
|
Array.from(new Set(data.map(r => String(r[COLUMNS.PRODUCT_TYPE] || '')).filter(Boolean))).sort()
|
||||||
|
, [data]);
|
||||||
|
|
||||||
|
const startEditType = (rowIndex: number, currentValue: string) => {
|
||||||
|
setEditingType({ rowIndex, value: currentValue });
|
||||||
|
setTypeSuggestions(allProductTypes);
|
||||||
|
setTimeout(() => typeInputRef.current?.focus(), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const commitTypeEdit = useCallback(async (rowIndex: number, value: string) => {
|
||||||
|
setEditingType(null);
|
||||||
|
setTypeSuggestions([]);
|
||||||
|
const original = data[rowIndex];
|
||||||
|
const newRow = [...original];
|
||||||
|
newRow[COLUMNS.PRODUCT_TYPE] = value;
|
||||||
|
onCaptureState(`Updated type for ${original[COLUMNS.ARTICLE_NO]}`);
|
||||||
|
await onSaveRow(rowIndex, newRow);
|
||||||
|
}, [data, onSaveRow, onCaptureState]);
|
||||||
|
|
||||||
|
const handleTypeInputChange = (value: string) => {
|
||||||
|
setEditingType(prev => prev ? { ...prev, value } : null);
|
||||||
|
const lower = value.toLowerCase();
|
||||||
|
setTypeSuggestions(
|
||||||
|
lower ? allProductTypes.filter(t => t.toLowerCase().includes(lower)) : allProductTypes
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// ── Column helpers ────────────────────────────────────────────────────────
|
// ── Column helpers ────────────────────────────────────────────────────────
|
||||||
const pricingEditableCols: DetectedCol[] = [
|
const pricingEditableCols: DetectedCol[] = [
|
||||||
...(uvpIdx >= 0 ? [{ index: uvpIdx, name: headers[uvpIdx] || 'UVP' }] : []),
|
...(uvpIdx >= 0 ? [{ index: uvpIdx, name: headers[uvpIdx] || 'UVP' }] : []),
|
||||||
@@ -747,6 +780,11 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
<ResizeHandle onMouseDown={e => handleResizeStart(e, 'classification', columnWidths.classification)} />
|
<ResizeHandle onMouseDown={e => handleResizeStart(e, 'classification', columnWidths.classification)} />
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
|
<th style={{ width: columnWidths.productType }} className="text-left px-3 py-3 text-xs font-semibold text-purple-400 uppercase tracking-wider border-b border-slate-700 whitespace-nowrap relative">
|
||||||
|
<span>Type</span>
|
||||||
|
<ResizeHandle onMouseDown={e => handleResizeStart(e, 'productType', columnWidths.productType)} />
|
||||||
|
</th>
|
||||||
|
|
||||||
{pricingEditableCols.map(col => {
|
{pricingEditableCols.map(col => {
|
||||||
const colKey = `prc_${col.index}`;
|
const colKey = `prc_${col.index}`;
|
||||||
const width = columnWidths[colKey] ?? 100;
|
const width = columnWidths[colKey] ?? 100;
|
||||||
@@ -1007,6 +1045,52 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
{/* TYPE cell */}
|
||||||
|
<td className="px-3 py-2 overflow-visible relative">
|
||||||
|
{editingType?.rowIndex === dataIndex ? (
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
ref={typeInputRef}
|
||||||
|
type="text"
|
||||||
|
value={editingType.value}
|
||||||
|
onChange={e => handleTypeInputChange(e.target.value)}
|
||||||
|
onKeyDown={e => {
|
||||||
|
if (e.key === 'Enter') commitTypeEdit(dataIndex, editingType.value);
|
||||||
|
if (e.key === 'Escape') { setEditingType(null); setTypeSuggestions([]); }
|
||||||
|
}}
|
||||||
|
onBlur={() => setTimeout(() => { commitTypeEdit(dataIndex, editingType?.value ?? ''); }, 150)}
|
||||||
|
className="w-full bg-slate-900 border border-purple-500 rounded px-2 py-1 text-sm text-white focus:outline-none focus:ring-1 focus:ring-purple-500"
|
||||||
|
/>
|
||||||
|
{typeSuggestions.length > 0 && (
|
||||||
|
<ul className="absolute top-full left-0 mt-1 w-full min-w-[160px] bg-slate-800 border border-slate-600 rounded-lg shadow-2xl z-50 max-h-48 overflow-y-auto">
|
||||||
|
{typeSuggestions.map(t => (
|
||||||
|
<li
|
||||||
|
key={t}
|
||||||
|
onMouseDown={e => { e.preventDefault(); commitTypeEdit(dataIndex, t); }}
|
||||||
|
className="px-3 py-1.5 text-xs text-slate-200 hover:bg-purple-500/20 cursor-pointer truncate"
|
||||||
|
>
|
||||||
|
{t}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => startEditType(dataIndex, String(row[COLUMNS.PRODUCT_TYPE] || ''))}
|
||||||
|
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.PRODUCT_TYPE]
|
||||||
|
? 'text-purple-300 border border-transparent hover:border-slate-600'
|
||||||
|
: 'text-slate-500 border border-dashed border-slate-600 hover:border-purple-500/50'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="truncate">{row[COLUMNS.PRODUCT_TYPE] || 'Add type…'}</span>
|
||||||
|
<Edit2 className="w-2.5 h-2.5 opacity-0 group-hover:opacity-50 shrink-0" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
|
||||||
{/* Pricing editable cells */}
|
{/* Pricing editable cells */}
|
||||||
{pricingEditableCols.map(col => {
|
{pricingEditableCols.map(col => {
|
||||||
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === col.index;
|
const isEditing = editingCell?.rowIndex === dataIndex && editingCell?.colIndex === col.index;
|
||||||
|
|||||||
+2
-1
@@ -38,5 +38,6 @@ export const COLUMNS = {
|
|||||||
OUTER_H: 49,
|
OUTER_H: 49,
|
||||||
VERIFIED_DIMS: 100,
|
VERIFIED_DIMS: 100,
|
||||||
VALIDATED_CHECK: 101,
|
VALIDATED_CHECK: 101,
|
||||||
VALIDATED_NOTE: 102
|
VALIDATED_NOTE: 102,
|
||||||
|
PRODUCT_TYPE: 103
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user