Summary
Event series are the brand identity behind recurring events. “La Prosperosa”, “Pullup”, etc. are series — each venue occurrence is a separate events row linked via series_id. Searching for a series shows upcoming dates, past edition photos, and aggregated star ratings.
Files Changed
lib/shared/models/event_series_model.dart— newEventSeriesmodel, plain Dart (no codegen),fromRow,copyWith,recurrenceLabellib/core/network/event_series_api_service.dart— newEventSeriesApiServicesingleton:searchSeries(query),getSeriesDetail(seriesId)lib/features/events/screens/series_detail_screen.dart— new full detail screenlib/features/events/screens/event_details_screen.dart— added_seriesInfostate,_buildSeriesBanner, parallel series fetch in_loadPastEditionslib/features/search/screens/search_screen.dart—_seriesResultsstate, series query in_performSearch,_buildSeriesTile,_seriesAvatarFallback, series section at top of “Tutto” tablib/core/router/app_router.dart— new route/series/:seriesId→SeriesDetailScreenlib/shared/models/event_model.dart+.g.dart—seriesIdfield added toEvent
Architecture
event_series (DB table)
↑ series_id FK
events rows ← EventSeriesApiService.getSeriesDetail()
├─ upcoming: events WHERE series_id = X AND start_date > now()
└─ past photos: events WHERE series_id = X AND end_date < now()
DB triggers (migration 20260428061017_event_series_and_social_cleanup):
trg_sync_series_rating— updatesevent_series.avg_rating+total_ratingson INSERT/UPDATE/DELETE ofevent_ratingstrg_sync_series_editions— updatesevent_series.total_editionson events INSERT/UPDATE/DELETE
Key Decisions
- No codegen for
EventSeries— model is only ever hydrated from one source (API service); codegen adds build complexity without benefit here. - Parallel fetch in
getSeriesDetail— upcoming + past photos fetched viaFuture.wait, not sequential. ~2× faster on slow connections. - Series results first in search — when a query matches a series name exactly (e.g. “La Prosperosa”), users almost always want the series, not individual event instances. Series tiles appear at the top of the “Tutto” tab.
- Deterministic cover gradient — series without a
cover_image_urlget a gradient derived fromname.codeUnits.fold(% 60). Unique per series, zero randomness, no storage needed. initialSeriespass-through —SeriesDetailScreenaccepts a pre-loadedEventSeries?for instant first-paint from search (no blank screen), then reloads full detail in background.
User Flows
- From search: type “La Prosperosa” → coral series tile at top of results → tap →
SeriesDetailScreen - From event detail: any event with
seriesId != nullshows a coral “PARTE DELLA SERIE” banner → tap →SeriesDetailScreen - From future wizard: event creation wizard will link an event to an existing series (not yet built — see
EventApiService.createEventTODO)
SeriesDetailScreen Sections
SliverAppBarwith cover image or deterministic gradient fallback- Rating row:
★ X.X · N valutazioni · N edizionichip - Upcoming editions list — date stamp column (day + month + time) + venue name + address + thumbnail
- Past edition photos — horizontal scroll, 200×150 thumbnails
- About — description text (if set)
Route
/series/:seriesId → SeriesDetailScreen(seriesId: ...)
Accessible via context.push('/series/$id') from anywhere.
Related
- database-schema —
event_seriesandevent_ratingstable definitions, trigger docs - feat-event-discovery-live — event feed and search
- feature-event-details-v2 — event detail screen