Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15 — Flutter Implementation Guide (Organizations Module)

Module extension of 00-shared/11. Structure, widgets, cubits, repositories, DTOs, models, navigation, theme, extensions, localization, testing, performance. Forward-looking.


1. Structure

lib/features/organizations/
├── data/
│   ├── dto/
│   │   ├── organization_dto.dart          # fromJson/toJson for envelope payload
│   │   ├── organization_create_dto.dart   # mirrors CreateOrganizationDto
│   │   ├── organization_update_dto.dart   # mirrors UpdateOrganizationDto
│   │   ├── organization_settings_dto.dart # mirrors UpdateOrganizationSettingsDto
│   │   ├── setting_dto.dart               # mirrors UpdateSettingDto (standalone)
│   │   └── feature_flag_dto.dart          # mirrors UpdateFeatureFlagDto
│   ├── models/
│   │   ├── organization.dart              # domain model (enums: status/plan/subStatus)
│   │   ├── organization_settings.dart
│   │   ├── org_feature_flags.dart
│   │   └── flag_catalog_entry.dart
│   └── repositories/
│       ├── organization_repository.dart   # E1–E9
│       └── org_config_repository.dart     # E10–E11 (settings + flags collections)
├── domain/
│   └── slugify.dart                       # pure fn mirroring organizations.service.ts:161-169
└── presentation/
    ├── cubit/
    │   ├── org_detail_cubit.dart
    │   ├── org_edit_cubit.dart
    │   ├── org_settings_cubit.dart
    │   ├── feature_flags_cubit.dart
    │   ├── members_cubit.dart
    │   └── tenants_cubit.dart
    ├── pages/
    │   ├── org_overview_page.dart         # S1
    │   ├── org_edit_page.dart             # S2
    │   ├── branding_page.dart             # S3
    │   ├── org_settings_page.dart         # S4
    │   ├── feature_flags_page.dart        # S5
    │   ├── members_page.dart              # S6
    │   ├── tenants_page.dart              # S7
    │   └── tenant_detail_page.dart        # S8 (+ create)
    └── widgets/
        ├── org_header.dart
        ├── status_badge.dart
        ├── setting_field_group.dart
        ├── color_picker_field.dart
        ├── day_chips.dart
        ├── flag_switch_row.dart
        ├── tenant_row.dart
        └── slug_preview.dart

2. Models & enums (exact from schema)

enum SubscriptionPlan { free, basic, premium, enterprise }   // organization.schema.ts:6-11
enum SubscriptionStatus { active, inactive, suspended, trial }// :13-18
enum OrganizationStatus { active, inactive, suspended, onboarding } // :20-25
enum SettingGroup { academic, attendance, grading, notification, theme, general } // setting.schema.ts:7-14

Organization fields mirror organization.schema.ts:28-152: name, slug, logoFileId, domain, contact{email,phone,website}, address{street,city,state,country,zip}, timezone, currency, academicYear{startDate,endDate,month}, subscriptionPlan/Status, settings, branding{primaryColor,secondaryColor,logo,favicon}, status, metadata, createdAt/updatedAt, version. Ids String; dates DateTime (parse ISO from envelope timestamps). Never send DTOs to widgets (00-shared/11 §4).

3. Repositories

OrganizationRepository (dio, via AppDio bearer/refresh/error interceptors — 00-shared/11 §5):

  • createOrg(CreateOrgDto) → Organization (E1)
  • listOrgs({page,limit,sort,q}) → Paginated<Organization> (E2)
  • getOrg(id) → Organization (E3)
  • updateOrg(id, UpdateOrgDto) → Organization (E4)
  • deleteOrg(id) → void (E5)
  • getSettings(id) → OrgSettings (E6), updateSettings(id, OrgSettings) → Organization (E7)
  • getFlagMap(id) → Map<String,bool> (E8), setFlagMap(id, Map<String,bool>) → Map<String,bool> (E9)
  • getSelfOrg() → Organization — OQ-1: calls (planned) /organizations/me; stopgap: listOrgs(q: tenantIdSlug) documented in code.

OrgConfigRepository: settings collection E10 + flags catalog E11.

Typed exceptions: ApiException(code, status, fieldDetails, message) from error interceptor (00-shared/06 §5).

4. Cubits (see 13)

OrgDetailCubit, OrgEditCubit, OrgSettingsCubit, FeatureFlagsCubit, MembersCubit, TenantsCubit (with PaginatedListMixin<Organization>00-shared/06 §3.2). Full-object save guard in OrgSettingsCubit.SaveAll (merge from lastServer + dirty). Optimistic rollback in FeatureFlagsCubit.Toggle. All pure-Dart, DI via get_it lazy factories (00-shared/11 §2).

5. Navigation (go_router)

GoRoute(path: '/organization', builder: OrgOverviewPage, guards: [authGuard, permissionGuard('organization.read')]),
GoRoute(path: '/organization/edit', ..., guards: [..., permissionGuard('organization.update')]),
GoRoute(path: '/organization/branding', ...),
GoRoute(path: '/organization/settings', ...),
GoRoute(path: '/organization/feature-flags', ...),
GoRoute(path: '/organization/members', ...),
GoRoute(path: '/admin/tenants', guards: [authGuard, platformAdminGuard]),
GoRoute(path: '/admin/tenants/new', ...),
GoRoute(path: '/admin/tenants/:id', ...),
GoRoute(path: '/admin/tenants/:id/edit', ...),

platformAdminGuard checks user.isPlatformAdmin (jwt-auth.guard.ts:54). Permission guards mirror permissions.constants.ts (organization.* :2-5, settings.* :75-77, feature-flags.* :78-80); server remains authoritative (403 → route redirect to 403 screen). Master-detail via StatefulShellBranch at ≥840 dp (00-shared/05 §3). Deep links (forward-looking): studylyon://organization/settings, studylyon://admin/tenants/:id.

6. Theme

Branding override: AppTheme.fromBranding(seed: org.branding.primaryColor)ColorScheme.fromSeed (D §7.5, 02 §1); preview mode in S3 renders with overridden scheme without persisting. themeMode from system or org pin (proposed) (OQ/D). No literal tokens in widgets (02 §10, 04 §7).

7. Extensions

Reuse shared (00-shared/11 §8): DateTime.toDisplayDate, String.initials, int.toMoney, context.showAppSnackbar. Module additions: OrganizationStatus.displayName, Organization.statusSemantics, Map<String,bool>.flagKeysBy(module), String.toSlug (wraps slugify).

8. Localization

Keys under features/organizations/ namespace in .arb (en + fr + hi smoke): org.title, org.status.onboarding, org.settings.saveAll, org.flags.toggle.rollback, org.tenants.delete.confirm, org.slug.conflict, weekday labels for chips. Server messages rendered via error-code→key map with business-4xx fallback (00-shared/11 §9, 07 §11).

9. Testing

LayerCoverage
Unitslugify parity tests vs organizations.service.ts:161-169 vectors ("St. Mary's School" → st-marys-school); enum mapping; formatters; full-object merge logic
CubitOrgSettingsCubit full-object save (assert sibling groups present in payload); FeatureFlagsCubit rollback; TenantsCubit pagination + search reset
WidgetS1 loading/error/empty; S4 3-tab dirty states; S5 toggle pending/rollback; S7 skeleton/infinite-scroll; typed-confirm disabled state
Golden8 screens × light/dark × 3 sizes; new components (07 §Golden)
Integrationprovision → register → overview journey; settings save → reload → values intact
E2E (device cloud)P0: platform creates tenant; admin registers; edits settings; toggles flag; deletes tenant

Run: flutter analyze, flutter test, flutter test integration_test (00-shared/11 §12).

10. Performance

  • ListView.builder for S5/S6/S7; AutomaticKeepAliveClientMixin for S4 tabs; RepaintBoundary around S3 preview.
  • Flag list grouped sections render lazily; collapse via AnimatedSize without layout rebuild storms.
  • SlugPreview debounced 150 ms; search debounce 300 ms.
  • Caches keyed sl:{tenant}:org… with TTLs from 13 §8; RefreshIndicator bypasses cache.
  • Profile on mid-range device against 10_QA_Baseline.md §1 budgets.

11. Open items to wire when backend lands

  1. GET /organizations/me (OQ-1) — remove stopgap lookup.
  2. JWT+RBAC guard + tenant scoping on organizations controller (OQ-4) — swap platformAdminGuard for server truth.
  3. TenantPurgeJob enqueue (OQ-5) — surface "purge scheduled" state if API returns it.
  4. Storage provider (R2/Appwrite) — logo upload real path (IMPLEMENTATION_PLAN.md:24-34).
  5. WS topics org.branding.updated / org.feature-flags.updated — subscribe + cache invalidation.
  6. Institution-type discriminator (Phase 6, IMPLEMENTATION_PLAN.md:251-287) — add institutionType to model + onboarding step (planned).