feat: tap-to-filter on mobile schedule, replace filter bar

On mobile, tapping a trainer name or class type in the agenda list
filters to show all their sessions across the week. Active filter
shown as a banner with clear button. Desktop filters unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 19:10:58 +03:00
parent 22a59ae9af
commit 303c52653c

View File

@@ -169,9 +169,9 @@ export function Schedule() {
</div>
</Reveal>
{/* Compact filters — scrollable on mobile */}
{/* Compact filters — desktop only */}
<Reveal>
<div className="mt-5 flex items-center justify-center gap-1.5 overflow-x-auto pb-1 scrollbar-hide sm:flex-wrap sm:overflow-visible sm:pb-0">
<div className="mt-5 hidden sm:flex items-center justify-center gap-1.5 flex-wrap">
{/* Class types */}
{types.map((type) => (
<button
@@ -251,9 +251,36 @@ export function Schedule() {
</Reveal>
</div>
{/* Mobile: compact agenda list */}
{/* Mobile: compact agenda list with tap-to-filter */}
<Reveal>
<div className="mt-6 px-4 sm:hidden">
{/* Active filter indicator */}
{hasActiveFilter && (
<div className="mb-3 flex items-center justify-between rounded-xl bg-[#c9a96e]/10 px-4 py-2.5 dark:bg-[#c9a96e]/5">
<div className="flex items-center gap-2 text-xs font-medium text-[#a08050] dark:text-[#d4b87a]">
{filterTrainer && (
<span className="flex items-center gap-1">
<User size={11} />
{filterTrainer}
</span>
)}
{filterType && (
<span className="flex items-center gap-1">
<span className={`h-1.5 w-1.5 rounded-full ${TYPE_DOT[filterType] ?? "bg-white/30"}`} />
{filterType}
</span>
)}
</div>
<button
onClick={clearFilters}
className="flex items-center gap-1 rounded-full px-2 py-1 text-[11px] text-[#a08050] dark:text-[#d4b87a] active:bg-[#c9a96e]/20"
>
<X size={12} />
Сбросить
</button>
</div>
)}
{filteredDays.length > 0 ? (
<div className="space-y-1">
{filteredDays.map((day) => (
@@ -280,12 +307,15 @@ export function Schedule() {
{cls.time}
</span>
{/* Info */}
{/* Info — tappable trainer & type */}
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1.5">
<span className="truncate text-sm font-medium text-neutral-800 dark:text-white/80">
<button
onClick={() => setFilterTrainer(filterTrainer === cls.trainer ? null : cls.trainer)}
className={`truncate text-sm font-medium text-left active:opacity-60 ${filterTrainer === cls.trainer ? "text-[#c9a96e] underline underline-offset-2" : "text-neutral-800 dark:text-white/80"}`}
>
{cls.trainer}
</span>
</button>
{cls.hasSlots && (
<span className="shrink-0 rounded-full bg-emerald-500/15 border border-emerald-500/25 px-1.5 py-px text-[9px] font-semibold text-emerald-600 dark:text-emerald-400">
места
@@ -302,10 +332,13 @@ export function Schedule() {
</span>
)}
</div>
<div className="mt-0.5 flex items-center gap-1.5">
<button
onClick={() => setFilterType(filterType === cls.type ? null : cls.type)}
className={`mt-0.5 flex items-center gap-1.5 active:opacity-60 ${filterType === cls.type ? "opacity-100" : ""}`}
>
<span className={`h-1.5 w-1.5 shrink-0 rounded-full ${TYPE_DOT[cls.type] ?? "bg-white/30"}`} />
<span className="text-[11px] text-neutral-400 dark:text-white/30">{cls.type}</span>
</div>
<span className={`text-[11px] ${filterType === cls.type ? "text-[#c9a96e] underline underline-offset-2" : "text-neutral-400 dark:text-white/30"}`}>{cls.type}</span>
</button>
</div>
</div>
))}