State Management
Flo uses @ngrx/component-store for state management. The app currently defines 80 ComponentStore classes; the tables below list the main ones grouped by area.
Store Architecture
Shared & Core Stores
| Store | Scope | Key State |
|---|---|---|
GlobalStore | App-wide | Auth/session, users, activities, bookings, subscriptions, newsletter, feature flags, app configs |
SidebarStore | Navigation | Visible sections, mobile tabs, user view, user name/initials |
TranslationsStore | i18n | Runtime translation map, current language, loading/error |
ApiTokenStore | Public API | API tokens, permissions, loading flags |
LocationStore | Locations | Locations, current location, loading |
DynamicEntitiesStore | Dynamic entities | Available entity types, metadata map, load state |
DynamicEntityCrudStore | Dynamic entity CRUD | Entity rows, total count, metadata, validation |
MediaGalleryStore | Media (shared component) | Pending items, view mode, upload progress |
ImmobileMatchingStore | Immobili | Match list, active filter, archived toggle |
ImmobiliListStore | Immobili | Flagged media ids, scan issues |
CatalogoListStore | Catalogo | Creating flag, flagged media ids, scan issues |
CatalogoDetailStore | Catalogo | Categories, loading |
Dashboard Stores
| Store | Scope | Key State |
|---|---|---|
HomeStore | Dashboard landing | Home dashboard payload, loading, error |
PrenotazioniStore | Bookings | Subscription entitlement, subscription detail |
BookingPaymentStore | Booking checkout | Checkout, payment order, loading |
SchedulesGridStore | Scheduling | Week start/label, grouped schedules, day options |
MyStudioSubscriptionsStore | Account | Client subscriptions, check-ins, overview |
StudioPlanCommerceStore | Plan commerce | Studio plan offers, orders, loading |
WebAnalyticsStore | Web analytics | GA4/GSC status, dashboard, timeseries, breakdowns, config |
BusinessAnalyticsStore | Business analytics | Dashboard, selected months, last refresh |
Admin Stores (Amministrazione)
| Store | Scope | Key State |
|---|---|---|
AmministrazioneStore | Admin shell | Session invalidation state |
GestioneUtentiStore | Users | Users page, flagged ids, total count |
UserDetailStore | User detail | Full user detail, loading |
ProfessionalsStore | Team | Professionals, available users |
GalleryStore | Galleries / dynamic contents | Galleries, selection, flagged media, structure lock |
BlogStore | Blog (legacy Strapi shell) | Articles, categories, authors, comments |
CommentModerationStore | Blog comments | Comments, reports, totals |
NewsletterStore | Newsletter | Template, previews, template types |
ComunicazioneAutomazioniStore | Communication | Template, template types, auto-newsletter config |
SendJobsStore | Communication | Send jobs, pagination, selected job |
ContactsStore | Contacts | Contacts, current contact, total records |
FormsStore | Public forms | Form definitions, submissions, settings |
CommerceOrdersStore | Commerce | Orders, status filter, pagination |
PagamentiStore | Payments | Payment orders, status filter, pagination |
FatturazioneStore | Invoicing settings | Settings load/save/test flags |
FattureStore | E-invoices | Invoice list, pagination |
GestioneAbbonamentiClientiStore | Client subscriptions | Subscriptions, stats, plans, users |
BrandKitStore | Brand kit | Kit, asset slots, saving/uploading |
WhitelabelEditorStore | Whitelabel editor | Branding config, loading/saving/uploading |
WebhooksStore | Webhooks | Webhook configuration |
GestioneFunzionalitaStore | Feature toggles | Subscription features, saving |
LocalizzazioneStore | Localization | Localization settings, saving |
note
BlogStore backs the legacy Strapi-based blog admin shell. The native blog CMS (enable_native_blog) is built on the dynamic-entity infrastructure (article.config.ts, author.config.ts, category.config.ts), not on a dedicated ComponentStore.
Store Pattern
// State interface
export interface FeatureState {
items: Item[];
loading: boolean;
error: string | null;
}
@Injectable()
export class FeatureStore extends ComponentStore<FeatureState> {
constructor(private service: FeatureService) {
super({ items: [], loading: false, error: null });
}
// Selectors — derive data from state
readonly items$ = this.select(state => state.items);
readonly loading$ = this.select(state => state.loading);
// Updaters — synchronous state mutations
readonly setLoading = this.updater((state, loading: boolean) => ({
...state, loading
}));
// Effects — async operations (API calls)
readonly loadItems = this.effect<void>(trigger$ =>
trigger$.pipe(
tap(() => this.setLoading(true)),
switchMap(() => this.service.getAll().pipe(
tapResponse(
items => this.patchState({ items, loading: false }),
error => this.patchState({ error: error.message, loading: false })
)
))
)
);
}
Usage in Components
@Component({
providers: [FeatureStore], // Scoped to component lifecycle
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FeatureComponent {
private readonly store = inject(FeatureStore);
// Expose store data to template
protected readonly items$ = this.store.items$;
protected readonly loading$ = this.store.loading$;
ngOnInit(): void {
this.store.loadItems();
}
onDelete(item: Item): void {
this.store.deleteItem(item.id);
}
}
GlobalStore
The GlobalStore is the main application state, available throughout the app. It manages:
- Authentication state — Current user, session restore, default-password flag
- Users — Admin user list, booking user search results
- Activities — Activities, current activity, studio closures
- Bookings — Selected date/slots/bookings, booked users
- Subscriptions — User subscriptions
- Newsletter — Newsletter subscriptions and import result
- Feature flags — Loaded pre-auth during app initialization
- Configuration — App configs, external auth config, public localization settings
It is provided at the root level and injected wherever needed.
Best Practices
- One store per feature — Don't share stores across unrelated features
- Keep state flat — Avoid deeply nested state objects
- Derive, don't duplicate — Use selectors for computed values
- Side effects in effects — All API calls go through effects with
tapResponse - Scoped providers — Provide stores at component level when possible
- Never put business logic in components — Always delegate to stores