mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 13:55:23 +02:00
UI: Add 'Check Anna' column to Pricing Units view
Mirrors Check Ying with its own checkbox and note editor (pink theme). Stored in virtual columns ANNA_CHECK (105) and ANNA_NOTE (106). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
aca37c4a99
commit
f545776e15
@@ -592,6 +592,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
}, [headers, displayedIndices]);
|
||||
|
||||
const [noteEditor, setNoteEditor] = useState<{ rowIndex: number; text: string } | null>(null);
|
||||
const [annaEditor, setAnnaEditor] = useState<{ rowIndex: number; text: string } | null>(null);
|
||||
|
||||
const handleToggleCheck = async (rowIndex: number, checked: boolean) => {
|
||||
const original = data[rowIndex];
|
||||
@@ -612,6 +613,25 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
await onSaveRow(rowIndex, newRow);
|
||||
};
|
||||
|
||||
const handleToggleAnnaCheck = async (rowIndex: number, checked: boolean) => {
|
||||
const original = data[rowIndex];
|
||||
const newRow = [...original];
|
||||
newRow[COLUMNS.ANNA_CHECK] = checked;
|
||||
onCaptureState(`${checked ? 'Anna checked' : 'Anna unchecked'} ${original[COLUMNS.ARTICLE_NO]}`);
|
||||
await onSaveRow(rowIndex, newRow);
|
||||
};
|
||||
|
||||
const saveAnnaNote = async () => {
|
||||
if (!annaEditor) return;
|
||||
const { rowIndex, text } = annaEditor;
|
||||
const original = data[rowIndex];
|
||||
const newRow = [...original];
|
||||
newRow[COLUMNS.ANNA_NOTE] = text;
|
||||
setAnnaEditor(null);
|
||||
onCaptureState(`Updated Anna note for ${original[COLUMNS.ARTICLE_NO]}`);
|
||||
await onSaveRow(rowIndex, newRow);
|
||||
};
|
||||
|
||||
// ── Column detection notice ───────────────────────────────────────────────
|
||||
const missingCols: string[] = [];
|
||||
if (uvpIdx < 0) missingCols.push('UVP');
|
||||
@@ -627,6 +647,39 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-4 relative">
|
||||
{/* ── Anna Note Editor Modal ── */}
|
||||
{annaEditor && (
|
||||
<div className="fixed inset-0 bg-slate-950/50 backdrop-blur-sm z-[100] flex items-center justify-center p-4">
|
||||
<div className="bg-slate-800 border border-slate-700 rounded-xl shadow-2xl w-full max-w-md animate-in zoom-in-95 duration-200">
|
||||
<div className="p-4 border-b border-slate-700 flex items-center justify-between">
|
||||
<h3 className="font-semibold text-white flex items-center gap-2">
|
||||
<MessageSquare className="w-4 h-4 text-pink-400" />
|
||||
Anna Note
|
||||
</h3>
|
||||
<p className="text-[10px] text-slate-500 font-mono">{data[annaEditor.rowIndex][COLUMNS.ARTICLE_NO]}</p>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<textarea
|
||||
value={annaEditor.text}
|
||||
onChange={e => setAnnaEditor(prev => prev ? { ...prev, text: e.target.value } : null)}
|
||||
placeholder="Write a note about this product..."
|
||||
className="w-full h-32 bg-slate-900 border border-slate-700 rounded-lg p-3 text-sm text-white focus:outline-none focus:border-pink-500 resize-none"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4 bg-slate-900/50 rounded-b-xl flex justify-end gap-3">
|
||||
<button onClick={() => setAnnaEditor(null)} className="px-4 py-2 text-sm text-slate-400 hover:text-white transition-colors">Cancel</button>
|
||||
<button
|
||||
onClick={saveAnnaNote}
|
||||
className="px-4 py-2 bg-pink-600 hover:bg-pink-700 text-white text-sm font-bold rounded-lg shadow-lg active:scale-95 transition-all"
|
||||
>
|
||||
Save Note
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Note Editor Modal ── */}
|
||||
{noteEditor && (
|
||||
<div className="fixed inset-0 bg-slate-950/50 backdrop-blur-sm z-[100] flex items-center justify-center p-4">
|
||||
@@ -1127,6 +1180,11 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
<ResizeHandle onMouseDown={e => { e.stopPropagation(); handleResizeStart(e, 'check', columnWidths.check); }} />
|
||||
</th>
|
||||
|
||||
<th style={{ width: columnWidths.checkAnna ?? 100 }} className="text-left px-3 py-3 text-xs font-semibold text-pink-400 uppercase tracking-wider group cursor-pointer hover:bg-slate-800/50 transition-colors relative" onClick={() => handleSort(COLUMNS.ANNA_CHECK)}>
|
||||
<span className="flex items-center gap-1">Check Anna <SortIcon current={sortConfig.key === COLUMNS.ANNA_CHECK ? sortConfig.direction : null} /></span>
|
||||
<ResizeHandle onMouseDown={e => { e.stopPropagation(); handleResizeStart(e, 'checkAnna', columnWidths.checkAnna ?? 100); }} />
|
||||
</th>
|
||||
|
||||
{matrixCols.map(col => {
|
||||
const colKey = `mat_${col.index}`;
|
||||
const width = columnWidths[colKey] ?? 120;
|
||||
@@ -1350,7 +1408,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
})()}
|
||||
</td>
|
||||
|
||||
{/* Check Column (Validated + Note) */}
|
||||
{/* Check Ying Column (Validated + Note) */}
|
||||
<td className="px-3 py-2.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -1380,6 +1438,42 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* Check Anna Column */}
|
||||
{(() => {
|
||||
const annaChecked = !!row[COLUMNS.ANNA_CHECK];
|
||||
const annaNote = String(row[COLUMNS.ANNA_NOTE] || '');
|
||||
return (
|
||||
<td className="px-3 py-2.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={annaChecked}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleAnnaCheck(dataIndex, e.target.checked)}
|
||||
className="w-4 h-4 rounded border-slate-600 bg-slate-900 text-pink-600 focus:ring-pink-500 focus:ring-offset-slate-800 cursor-pointer"
|
||||
/>
|
||||
<div className="relative group">
|
||||
<button
|
||||
onClick={() => setAnnaEditor({ rowIndex: dataIndex, text: annaNote })}
|
||||
className={cn(
|
||||
"p-1.5 rounded-md transition-all shrink-0",
|
||||
annaNote
|
||||
? "bg-pink-500/10 text-pink-400 border border-pink-500/20 hover:bg-pink-500/20"
|
||||
: "text-slate-500 hover:text-slate-300 hover:bg-slate-700/50"
|
||||
)}
|
||||
>
|
||||
<MessageSquare className={cn("w-3.5 h-3.5", annaNote && "fill-pink-400/20")} />
|
||||
</button>
|
||||
{annaNote && (
|
||||
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 w-48 p-2 bg-pink-900/90 border border-pink-500/50 rounded text-xs text-white shadow-lg z-50 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
|
||||
{annaNote}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Matrix columns cells */}
|
||||
{matrixCols.map(col => (
|
||||
<td key={col.index} className="px-3 py-2.5 text-slate-500 text-xs max-w-[150px] truncate" title={String(row[col.index] || '')}>
|
||||
|
||||
+6
-2
@@ -41,7 +41,9 @@ export const COLUMNS = {
|
||||
VALIDATED_CHECK: 101,
|
||||
VALIDATED_NOTE: 102,
|
||||
PRODUCT_TYPE: 103,
|
||||
ITEM_TO_LOGISTIC: 104
|
||||
ITEM_TO_LOGISTIC: 104,
|
||||
ANNA_CHECK: 105,
|
||||
ANNA_NOTE: 106
|
||||
};
|
||||
|
||||
// Search patterns for dynamic detection
|
||||
@@ -77,7 +79,9 @@ export const COLUMN_PATTERNS: Record<keyof typeof COLUMNS, string[]> = {
|
||||
VALIDATED_CHECK: ['validated'],
|
||||
VALIDATED_NOTE: ['note'],
|
||||
PRODUCT_TYPE: ['product', 'type'],
|
||||
ITEM_TO_LOGISTIC: ['item', 'to', 'logistic']
|
||||
ITEM_TO_LOGISTIC: ['item', 'to', 'logistic'],
|
||||
ANNA_CHECK: ['anna', 'check'],
|
||||
ANNA_NOTE: ['anna', 'note']
|
||||
};
|
||||
|
||||
export function resolveColumnIndices(headers: string[]): typeof COLUMNS {
|
||||
|
||||
Reference in New Issue
Block a user