PHASE 3 - COMPONENT SPACING OPTIMIZATION: components/IdentityCheckOverlay.tsx: - Modal padding: p-8 sm:p-10 → p-4 md:p-6 - Spacing: space-y-8 → space-y-3 md:space-y-4 components/CameraView.tsx: - Container gaps: gap-6 → gap-3 md:gap-4 components/AIOnboarding.tsx (4 fixes): - Main sections: gap-6 → gap-3 md:gap-4 - Live mode: gap-6 → gap-3 md:gap-4 - Results form: gap-6 → gap-3 md:gap-4 - Step view: gap-6 → gap-3 md:gap-4 components/ScannerSection.tsx: - Container spacing: space-y-6 → space-y-3 md:space-y-4 - Scanner section: space-y-4 → space-y-2 md:space-y-3 components/LogsTable.tsx (2 fixes): - Empty state: gap-6 → gap-3 md:gap-4 - Modal: p-6 sm:p-10 space-y-8 → p-4 md:p-6 space-y-3 md:space-y-4 Results: - All 291 tests passing - Zero TypeScript errors - Mobile-first spacing applied throughout - Consistent responsive pattern across all components
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
'use client';
|
|
|
|
import { X, ArrowDownCircle, ArrowUpCircle, Trash2 } from 'lucide-react';
|
|
import Scanner from '@/components/Scanner';
|
|
import NewItemDialog from '@/components/NewItemDialog';
|
|
import { clsx, type ClassValue } from 'clsx';
|
|
import { twMerge } from 'tailwind-merge';
|
|
|
|
function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
|
|
interface ScannerSectionProps {
|
|
mode: string;
|
|
onModeChange: (mode: string) => void;
|
|
showScanner: boolean;
|
|
onShowScanner: (show: boolean) => void;
|
|
onScanSuccess: (result: any) => void;
|
|
onOCRMatch: (result: any) => void;
|
|
onAddItemClick: () => void;
|
|
}
|
|
|
|
export default function ScannerSection({
|
|
mode,
|
|
onModeChange,
|
|
showScanner,
|
|
onShowScanner,
|
|
onScanSuccess,
|
|
onOCRMatch,
|
|
onAddItemClick,
|
|
}: ScannerSectionProps) {
|
|
return (
|
|
<div className="w-full px-1 space-y-3 md:space-y-4">
|
|
{/* Mode Switcher */}
|
|
<div className="flex p-1.5 bg-surface rounded-2xl shadow-inner w-full gap-1">
|
|
{[
|
|
{ id: 'CHECK_IN', label: 'Check In', icon: ArrowDownCircle },
|
|
{ id: 'CHECK_OUT', label: 'Check Out', icon: ArrowUpCircle },
|
|
{ id: 'TRASH', label: 'Trash', icon: Trash2 }
|
|
].map((m) => (
|
|
<button
|
|
key={m.id}
|
|
data-testid={m.id === 'CHECK_IN' ? 'operation-checkin' : m.id === 'CHECK_OUT' ? 'operation-checkout' : undefined}
|
|
onClick={() => onModeChange(m.id)}
|
|
className={cn(
|
|
"flex-1 py-3.5 rounded-xl text-xs sm:text-sm font-normal transition-all flex items-center justify-center gap-3",
|
|
mode === m.id ? "bg-slate-800 text-primary shadow-lg ring-1 ring-primary/20" : "text-muted hover:text-secondary"
|
|
)}
|
|
>
|
|
<m.icon size={18} className={mode === m.id ? "scale-110 transition-transform" : ""} />
|
|
<span className="truncate">{m.label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Scanner Section */}
|
|
<section className="glass-card rounded-3xl p-6">
|
|
{showScanner ? (
|
|
<div className="space-y-2 md:space-y-3">
|
|
<div className="flex justify-between items-center">
|
|
<h2 className="text-lg font-normal">scanning...</h2>
|
|
<button
|
|
onClick={() => onShowScanner(false)}
|
|
className="p-2.5 bg-surface border border-slate-800 text-secondary rounded-xl hover:text-rose-500 transition-all active:scale-95"
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
<Scanner onScanSuccess={onScanSuccess} onOCRMatch={onOCRMatch} />
|
|
</div>
|
|
) : (
|
|
<NewItemDialog
|
|
onScannerClick={() => onShowScanner(true)}
|
|
onAddItemClick={onAddItemClick}
|
|
/>
|
|
)}
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|