feat(events): filter the feed by day via the calendar popup

The calendar button now opens the date-picker popup instead of
navigating to a separate page. Picking a day filters the feed to that
day (button highlights while active) and shows a removable date chip
whose ✕ clears ONLY the date selection — distinct from category
clearing, which keeps its own clear in the filter dropdown. Dots in the
popup come from the full (unfiltered) event set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-16 12:28:13 +02:00
commit 9934abc079

View file

@ -8,7 +8,8 @@ import {
CollapsibleContent, CollapsibleContent,
CollapsibleTrigger, CollapsibleTrigger,
} from '@/components/ui/collapsible' } from '@/components/ui/collapsible'
import { SlidersHorizontal, CalendarDays, Plus } from 'lucide-vue-next' import { SlidersHorizontal, CalendarDays, Plus, X } from 'lucide-vue-next'
import { format } from 'date-fns'
import brandAppLogoUrl from '@brand-app-logo?url' import brandAppLogoUrl from '@brand-app-logo?url'
import brandAppBannerUrl from '@brand-app-banner?url' import brandAppBannerUrl from '@brand-app-banner?url'
// Brand name flows through VITE_APP_NAME (set in vite.events.config.ts // Brand name flows through VITE_APP_NAME (set in vite.events.config.ts
@ -26,23 +27,30 @@ import { useEventsStore } from '../stores/events'
import EventSearchOverlay from '../components/EventSearchOverlay.vue' import EventSearchOverlay from '../components/EventSearchOverlay.vue'
import TemporalFilterBar from '../components/TemporalFilterBar.vue' import TemporalFilterBar from '../components/TemporalFilterBar.vue'
import CategoryFilterBar from '../components/CategoryFilterBar.vue' import CategoryFilterBar from '../components/CategoryFilterBar.vue'
import EventCalendarPopup from '../components/EventCalendarPopup.vue'
import EventList from '../components/EventList.vue' import EventList from '../components/EventList.vue'
import { useDateLocale } from '../composables/useDateLocale'
import type { Event } from '../types/event' import type { Event } from '../types/event'
const router = useRouter() const router = useRouter()
const { t } = useI18n() const { t } = useI18n()
const { dateLocale } = useDateLocale()
const eventsStore = useEventsStore() const eventsStore = useEventsStore()
const { const {
events, events,
allEvents,
isLoading, isLoading,
error, error,
temporal, temporal,
selectedCategories, selectedCategories,
selectedDate,
hasActiveFilters, hasActiveFilters,
showPast, showPast,
onlyHosting, onlyHosting,
setTemporal, setTemporal,
selectDate,
clearSelectedDate,
toggleCategory, toggleCategory,
clearCategories, clearCategories,
togglePast, togglePast,
@ -51,6 +59,14 @@ const {
} = useEvents() } = useEvents()
const filtersOpen = ref(false) const filtersOpen = ref(false)
const calendarOpen = ref(false)
// Human label for the active day filter, shown as a removable chip.
const selectedDateLabel = computed(() =>
selectedDate.value
? format(selectedDate.value, 'EEE, MMM d', { locale: dateLocale.value })
: '',
)
// Badge count on the Filters trigger so the user can see at a glance // Badge count on the Filters trigger so the user can see at a glance
// that hidden toggles (categories) are currently active even when the // that hidden toggles (categories) are currently active even when the
@ -77,8 +93,9 @@ function openCreate() {
eventsStore.showCreateDialog = true eventsStore.showCreateDialog = true
} }
function openCalendar() { function onSelectDate(date: Date) {
router.push('/events/calendar') // The popup closes itself; just apply the day filter.
selectDate(date)
} }
</script> </script>
@ -161,15 +178,16 @@ function openCalendar() {
@toggle-past="togglePast" @toggle-past="togglePast"
/> />
</div> </div>
<!-- Calendar shortcut sits at the end of the filter row next to <!-- Calendar shortcut opens the date-picker popup to filter the
the temporal pills (the week-day strip was removed in favour feed to a single day. Highlighted while a day filter is
of the calendar for picking specific dates). --> active. -->
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
class="h-8 w-8 shrink-0" class="h-8 w-8 shrink-0"
:class="{ 'bg-accent text-accent-foreground': selectedDate }"
:aria-label="t('events.nav.calendar')" :aria-label="t('events.nav.calendar')"
@click="openCalendar" @click="calendarOpen = true"
> >
<CalendarDays class="h-4 w-4" /> <CalendarDays class="h-4 w-4" />
</Button> </Button>
@ -183,6 +201,23 @@ function openCalendar() {
</CollapsibleContent> </CollapsibleContent>
</Collapsible> </Collapsible>
<!-- Active day-filter chip removing it clears ONLY the date
selection (categories have their own clear in the filter
dropdown). Shown when a day is picked from the calendar popup. -->
<div v-if="selectedDate" class="mb-3 flex">
<Button
variant="secondary"
size="sm"
class="h-7 gap-1.5"
:aria-label="t('events.filters.clearDate', 'Clear date filter')"
@click="clearSelectedDate"
>
<CalendarDays class="w-3.5 h-3.5" />
{{ selectedDateLabel }}
<X class="w-3.5 h-3.5" />
</Button>
</div>
<!-- Create-activity CTA shown when the Hosting bottom-nav tab is <!-- Create-activity CTA shown when the Hosting bottom-nav tab is
active. Replaces the dedicated Create entry that used to live active. Replaces the dedicated Create entry that used to live
in the bottom nav; lives here so it shows up exactly when the in the bottom nav; lives here so it shows up exactly when the
@ -210,5 +245,15 @@ function openCalendar() {
:compact="onlyHosting" :compact="onlyHosting"
@select="handleSelectEvent" @select="handleSelectEvent"
/> />
<!-- Date-picker popup: month grid with per-day event dots. Picking a
day filters the feed to it and closes. -->
<EventCalendarPopup
v-model:open="calendarOpen"
:events="allEvents"
:title="t('events.nav.calendar', 'Calendar')"
:description="t('events.calendar.pickDay', 'Pick a day to see its events')"
@select-date="onSelectDate"
/>
</div> </div>
</template> </template>