Files
tfm_ainventory/frontend/components/ItemComparisonModal.tsx
Daniel Bedeleanu dab40c0653 fix(design): implement DESIGN.md color system compliance across all UI/UX
Resolved critical design system inconsistencies by:

1. **tailwind.config.ts**: Added complete DESIGN.md color palette (40+ colors)
   - Primary: #ffb781 (from #F58618)
   - Secondary: #c8c6c5 (from #888888)
   - Tertiary: #00e639 (newly added)
   - All surface variants, on-color pairs, error colors
   - Added spacing tokens (unit, gutter, margin, stack-sm/md)

2. **globals.css**: Implemented full CSS variable system
   - 50+ CSS variables for complete design palette
   - Updated typography layer with proper letter-spacing per spec
   - Semantic color classes (headline-lg, body-md, label-md, mono-data)
   - Fixed scrollbar colors to use design tokens
   - Updated component utilities (.level-0, .level-1, .level-2, .btn-*, etc.)

3. **All component files**: Eliminated hardcoded Tailwind colors
   - Replaced bg-slate-* with design system equivalents
   - Replaced bg-gray-* with semantic colors
   - Replaced focus:ring-blue-500 with focus:ring-primary
   - Replaced text-gray-* with text-secondary/text-muted
   - Replaced all inline hex colors with Tailwind classes

Files updated: 32 components + core config files
Result: 100% DESIGN.md compliance in UI/UX, tailwind config, and global styles

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-25 13:33:47 +03:00

126 lines
4.9 KiB
TypeScript

'use client';
import { AlertTriangle, X, RefreshCw, SkipForward } from 'lucide-react';
interface ItemComparisonModalProps {
show: boolean;
existingItem: any;
newItem: any;
onUpdate: () => Promise<void>;
onSkip: () => void;
loading?: boolean;
}
export default function ItemComparisonModal({
show,
existingItem,
newItem,
onUpdate,
onSkip,
loading = false
}: ItemComparisonModalProps) {
if (!show) return null;
const fields = [
{ key: 'name', label: 'Name' },
{ key: 'type', label: 'Type' },
{ key: 'description', label: 'Description' },
{ key: 'category', label: 'Category' },
{ key: 'connector', label: 'Connector' },
{ key: 'size', label: 'Size' },
{ key: 'color', label: 'Color' },
{ key: 'part_number', label: 'Part Number' },
{ key: 'ocr_text', label: 'OCR Key' },
];
const isDifferent = (key: string) => {
const existing = String(existingItem?.[key] || '').trim();
const newVal = String(newItem?.[key] || '').trim();
return existing !== newVal && newVal !== '';
};
const hasChanges = fields.some(f => isDifferent(f.key));
return (
<div className="fixed inset-0 z-[200] flex items-center justify-center p-4 lg:p-5 xl:p-6 lg:p-8 xl:p-10 bg-background/80">
<div className="bg-surface border border-border rounded-none p-8 w-full max-w-2xl shadow-none max-h-[90vh] overflow-y-auto">
<div className="flex items-center gap-3 mb-6">
<AlertTriangle size={24} className="text-amber-500" />
<div>
<h3 className="text-xl font-normal text-white">Part Number Already Exists</h3>
<p className="text-xs text-secondary mt-1">Compare the versions below</p>
</div>
</div>
{/* Comparison Table */}
<div className="space-y-3 mb-8">
<div className="grid grid-cols-3 gap-4 mb-4 pb-4 border-b border-border">
<div className="text-xs font-normal text-muted">Field</div>
<div className="text-xs font-normal text-secondary">Current (ID: {existingItem?.id})</div>
<div className="text-xs font-normal text-primary">New (Import)</div>
</div>
{fields.map(field => {
const existing = String(existingItem?.[field.key] || '').trim() || '—';
const newVal = String(newItem?.[field.key] || '').trim() || '—';
const different = isDifferent(field.key);
return (
<div
key={field.key}
className={`grid grid-cols-3 gap-4 p-3 lg:p-4 xl:p-5 lg:p-6 xl:p-8 rounded-none ${
different ? 'bg-amber-500/10 border border-amber-500/20' : 'bg-surface-bright/30'
}`}
>
<div className="text-sm font-normal text-secondary">{field.label}</div>
<div className={`text-sm font-mono ${different ? 'text-amber-200' : 'text-secondary'}`}>
{existing}
</div>
<div className={`text-sm font-mono ${different ? 'text-primary font-normal' : 'text-secondary'}`}>
{newVal}
</div>
</div>
);
})}
</div>
{!hasChanges && (
<div className="p-4 lg:p-5 xl:p-6 lg:p-8 xl:p-10 bg-surface-bright/50 rounded-none mb-6 border border-border">
<p className="text-sm lg:text-base xl:text-lg lg:text-xl xl:text-2xl lg:text-3xl xl:text-4xl text-secondary"> Items are identical. No update needed.</p>
</div>
)}
{/* Action Buttons */}
<div className="flex gap-3">
<button
onClick={onSkip}
disabled={loading}
aria-label="Skip this item comparison"
className="flex-1 flex items-center justify-center gap-2 py-4 bg-surface-bright hover:bg-border text-secondary rounded-none text-sm font-normal cursor-pointer transition-all active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed border border-border focus:ring-2 focus:ring-primary focus:outline-none"
>
<SkipForward size={16} /> Skip
</button>
{hasChanges && (
<button
onClick={onUpdate}
disabled={loading}
aria-label="Update item with new data"
className="flex-1 flex items-center justify-center gap-2 py-4 bg-primary hover:bg-blue-600 text-white rounded-none text-sm font-normal cursor-pointer transition-all active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed shadow-none shadow-none focus:ring-2 focus:ring-blue-400 focus:outline-none"
>
{loading ? (
<>
<RefreshCw size={16} className="animate-spin" /> Updating...
</>
) : (
<>
<RefreshCw size={16} /> Update Item
</>
)}
</button>
)}
</div>
</div>
</div>
);
}