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:
1 parent
946a7780f6
commit
be586ea386
2 files changed
+82
-52
No files matched your search
@@ -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
@@ -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>
|
||||||
|
|||||||
Reference in new issue
Block a user