fix: Make compact list readable on mobile by replacing wide bag select with compact 4-char tag pill and allowing full item text

This commit is contained in:
airent106 committed 2026-10-15 22:40:21 -06:00
1 parent 946a7780f6
commit be586ea386
2 files changed
+82 -52

No files matched your search

+13 -13
View File
@@ -95,7 +95,7 @@ export const BagOverview: React.FC<BagOverviewProps> = ({
</button> </button>
</div> </div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-2 sm:gap-3">
{visibleBags.map((b) => { {visibleBags.map((b) => {
const bagItems = items.filter((i) => i.bag === b.id); const bagItems = items.filter((i) => i.bag === b.id);
const bagPacked = bagItems.filter((i) => i.packed).length; const bagPacked = bagItems.filter((i) => i.packed).length;
@@ -112,43 +112,43 @@ export const BagOverview: React.FC<BagOverviewProps> = ({
sound.playClick(); sound.playClick();
onSelectBag(isSelected ? 'all' : b.id); onSelectBag(isSelected ? 'all' : b.id);
}} }}
className={`relative text-left p-3.5 border-2 border-ink transition-all flex flex-col justify-between ${ className={`relative text-left p-2.5 sm:p-3.5 border-2 border-ink transition-all flex flex-col justify-between ${
isSelected isSelected
? 'bg-canary-light shadow-card-hard border-ink translate-x-0.5 translate-y-0.5' ? 'bg-canary-light shadow-card-hard border-ink translate-x-0.5 translate-y-0.5'
: 'bg-ticket-50 hover:bg-ticket-100 shadow-card-hard-sm' : 'bg-ticket-50 hover:bg-ticket-100 shadow-card-hard-sm'
}`} }`}
> >
{/* Perforation holes on card edges */} {/* Perforation holes on card edges */}
<div className="ticket-notch-left" /> <div className="ticket-notch-left hidden sm:block" />
<div className="ticket-notch-right" /> <div className="ticket-notch-right hidden sm:block" />
<div> <div>
{/* Top Barcode Header */} {/* Top Barcode Header */}
<div className="flex items-center justify-between border-b border-ink/20 pb-2 mb-2 font-mono text-[10px]"> <div className="flex items-center justify-between border-b border-ink/20 pb-1.5 sm:pb-2 mb-1.5 sm:mb-2 font-mono text-[10px] gap-1">
<span className="bg-ink text-ticket-100 px-1 py-0.5 font-bold uppercase"> <span className="bg-ink text-ticket-100 px-1 py-0.5 font-bold uppercase shrink-0">
{b.tagCode} {b.tagCode}
</span> </span>
<span className="text-ink-muted tracking-tight truncate max-w-[130px]"> <span className="text-ink-muted tracking-tight truncate">
{b.sub} {b.sub}
</span> </span>
</div> </div>
{/* Title & Icon */} {/* Title & Icon */}
<div className="flex items-center justify-between gap-2 mb-1"> <div className="flex items-center justify-between gap-1.5 mb-1">
<div className="flex items-center gap-2 text-ink min-w-0"> <div className="flex items-center gap-1.5 sm:gap-2 text-ink min-w-0">
<span className="text-ink shrink-0">{getBagIcon(b.iconKey)}</span> <span className="text-ink shrink-0 hidden sm:inline">{getBagIcon(b.iconKey)}</span>
<span className="font-display text-xl tracking-wide uppercase leading-tight truncate"> <span className="font-display text-base sm:text-xl tracking-wide uppercase leading-tight truncate">
{b.label} {b.label}
</span> </span>
</div> </div>
{isFull && ( {isFull && (
<CheckCircle2 size={18} className="text-stamp-green shrink-0" /> <CheckCircle2 size={16} className="text-stamp-green shrink-0" />
)} )}
</div> </div>
{/* TSA / Bag Advisory Tip */} {/* TSA / Bag Advisory Tip */}
{b.tsaTip && ( {b.tsaTip && (
<p className="font-sans text-[11px] text-ink-muted leading-tight mb-3 line-clamp-2"> <p className="font-sans text-[11px] text-ink-muted leading-tight mb-3 line-clamp-2 hidden sm:block">
{b.tsaTip} {b.tsaTip}
</p> </p>
)} )}
+69 -39
View File
@@ -53,42 +53,51 @@ export const ItemRow: React.FC<ItemRowProps> = ({
if (compact) { if (compact) {
return ( return (
<div <div
className={`group relative flex items-center justify-between border-2 py-1.5 px-3 mb-1.5 transition-all shadow-xs ${ className={`group relative flex items-center justify-between gap-2 border-2 py-2 px-2.5 sm:px-3 mb-1.5 transition-all shadow-xs ${
item.packed item.packed
? 'bg-[#ECE8DC]/70 border-ink/40 text-ink/60' ? 'bg-[#ECE8DC]/80 border-ink/40 text-ink/60'
: 'bg-ticket-50 hover:bg-ticket-100 border-ink' : 'bg-white hover:bg-ticket-50 border-ink text-ink'
}`} }`}
> >
{/* Left: Checkbox & Name */} {/* Left: Tap anywhere here to toggle packed */}
<div className="flex items-center gap-2.5 min-w-0 flex-1"> <div
onClick={() => {
sound.playTear();
onTogglePacked(item.id);
}}
className="flex items-center gap-2 sm:gap-2.5 min-w-0 flex-1 cursor-pointer select-none"
>
<button <button
type="button" type="button"
onClick={() => { onClick={(e) => {
e.stopPropagation();
sound.playTear(); sound.playTear();
onTogglePacked(item.id); onTogglePacked(item.id);
}} }}
className={`w-5 h-5 rounded border-2 flex items-center justify-center font-mono font-bold shrink-0 transition-transform ${ className={`w-5 h-5 rounded border-2 flex items-center justify-center font-mono font-bold shrink-0 transition-transform ${
item.packed item.packed
? 'border-stamp-green bg-stamp-green text-white rotate-[-3deg]' ? 'border-stamp-green bg-stamp-green text-white rotate-[-3deg]'
: 'border-ink/60 bg-white hover:border-ink' : 'border-ink bg-white hover:border-ink'
}`} }`}
title={item.packed ? 'Mark as unpacked' : 'Check off'} title={item.packed ? 'Mark as unpacked' : 'Check off'}
> >
{item.packed ? <Check size={12} strokeWidth={3} /> : null} {item.packed ? <Check size={13} strokeWidth={3} /> : null}
</button> </button>
{/* Qty indicator */} {/* Qty indicator */}
<span className="font-mono text-xs font-bold text-ink shrink-0 bg-ticket-200 border border-ink/20 px-1.5 py-0.2 rounded-xs"> <span
className={`font-mono text-xs font-bold shrink-0 px-1.5 py-0.5 rounded-xs border ${
item.packed
? 'bg-ticket-200/60 border-ink/20 text-ink/50'
: 'bg-canary/40 border-ink/40 text-ink'
}`}
>
{item.quantity}× {item.quantity}×
</span> </span>
{/* Name */} {/* Item Name — full readability on mobile */}
<span <span
onClick={() => { className={`font-grotesk font-bold text-sm sm:text-base leading-snug break-words min-w-0 flex-1 ${
sound.playTear();
onTogglePacked(item.id);
}}
className={`font-grotesk font-bold text-xs sm:text-sm truncate cursor-pointer select-none ${
item.packed ? 'line-through text-ink/50' : 'text-ink' item.packed ? 'line-through text-ink/50' : 'text-ink'
}`} }`}
> >
@@ -96,34 +105,55 @@ export const ItemRow: React.FC<ItemRowProps> = ({
</span> </span>
{item.essential && ( {item.essential && (
<span className="font-mono text-[9px] font-bold bg-gate-orange text-white px-1 py-0.2 uppercase shrink-0 hidden sm:inline"> <>
ESSENTIAL <span
</span> className="w-2 h-2 rounded-full bg-gate-orange shrink-0 sm:hidden"
title="Essential item"
/>
<span className="font-mono text-[9px] font-bold bg-gate-orange text-white px-1.5 py-0.5 uppercase shrink-0 hidden sm:inline">
ESSENTIAL
</span>
</>
)} )}
</div> </div>
{/* Right: Bag Selector & Category Pill & Actions */} {/* Right: Compact Bag Tag Pill & Desktop Actions */}
<div className="flex items-center gap-2 shrink-0 ml-2"> <div className="flex items-center gap-1.5 sm:gap-2 shrink-0">
{/* Bag Selector Dropdown Pill */} {/* Compact Bag Tag Pill with invisible native select overlay so it never stretches mobile width */}
<select <div
value={item.bag} className="relative inline-flex items-center"
onChange={(e) => { title={`Assigned to: ${currentBag.label}`}
sound.playClick();
onUpdateBag(item.id, e.target.value);
}}
className="bg-white border border-ink font-mono text-[10px] font-bold px-1.5 py-0.5 cursor-pointer hover:bg-canary-light"
title="Assigned Bag"
> >
{availableBags.map((b) => ( <span
<option key={b.id} value={b.id}> className={`font-mono text-[11px] font-bold px-2 py-0.5 border border-ink pointer-events-none flex items-center gap-1 ${
{b.tagCode} ({b.label}) item.packed
</option> ? 'bg-ticket-100 text-ink/60 border-ink/40'
))} : 'bg-ticket-100 text-ink shadow-2xs'
</select> }`}
>
<span>{currentBag.tagCode}</span>
<span className="text-[8px] opacity-60">▾</span>
</span>
<select
value={item.bag}
onChange={(e) => {
sound.playClick();
onUpdateBag(item.id, e.target.value);
}}
aria-label={`Assign ${item.name} to bag`}
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
>
{availableBags.map((b) => (
<option key={b.id} value={b.id}>
{b.tagCode} — {b.label}
</option>
))}
</select>
</div>
{/* Category Pill */} {/* Category Pill (Desktop only) */}
<span <span
className={`font-mono text-[9px] font-bold px-1.5 py-0.2 border border-ink/20 hidden md:inline ${catStyle.bg} ${catStyle.text}`} className={`font-mono text-[9px] font-bold px-1.5 py-0.5 border border-ink/20 hidden md:inline ${catStyle.bg} ${catStyle.text}`}
> >
{catStyle.label} {catStyle.label}
</span> </span>
@@ -135,10 +165,10 @@ export const ItemRow: React.FC<ItemRowProps> = ({
sound.playClick(); sound.playClick();
onDelete(item.id); onDelete(item.id);
}} }}
className="p-1 text-ink/30 hover:text-stamp-red transition-colors" className="p-1 text-ink/35 hover:text-stamp-red transition-colors"
title="Delete item" title="Delete item"
> >
<Trash2 size={13} /> <Trash2 size={14} />
</button> </button>
</div> </div>
</div> </div>