Skip to main content

Design Doc β€” Event Categories, Phase 1 (Admin Categories)

DraftAuthor: @relbnsClickUp: CU-86c1p963gPhase 1 of N
Scope of this document

The Phase-1 Design Doc β€” the engineering HOW for the single admin screen Categories, end-to-end (backend β†’ frontend β†’ design-system), at a depth sufficient to run the DR, break into tasks, and estimate. Phase 1 is taxonomy management only β€” creating/organising the categories. Tagging events with categories is a later phase (so Phase 1 touches no SQL β€” see Β§3). Design-system-anchored (the contract is components + tokens, not pixels); carries the UI reference (Β§4) the PM (Gali) reviews against. The WHAT/WHY lives in the PRD (event-categories-resident-interests, on its own branch) and is not restated. Backend is outline-level (decisions + contracts + risks for the DR). Assumptions are marked (assumption β€” confirm).

1. TL;DR​

Phase 1 delivers the admin taxonomy screen in organization-dashboard (embedded via iframe from the community gear-menu): create / edit / delete / deactivate categories, assign them to communities, import in bulk, batch-act, filter, search, paginate. This is a new, independent category system β€” a fresh Mongo-based foundation that will, in a future phase, replace the legacy system-categories/communities-categories (the legacy system is not a reference and is not touched here). Notifications and subscriptions are settings + placeholder counts only; event tagging, consumer filters, and dispatch are later phases. The screen reuses the dashboard's DataGrid pattern; the design system needs a Chip status-badge variant + a communities cell. Everything in Phase 1 lives in MongoDB β€” zero SQL schema change.

2. Approach & key decisions​

DecisionWhyAlternative (rejected)
New, independent category system (not reuse of system-categories)Product decision: this is the future foundation; the legacy taxonomy will be replaced by it later. Legacy is not a reference, no coexistence wiring.Reuse/extend legacy system-categories β€” explicitly declined by product
Store everything in MongoDB; zero SQL in Phase 1Strategic direction is to move off SQL; taxonomy management needs no SQL entity. Org/community are referenced by value (their MySQL int id stored as a Number), the established Mongo pattern β€” no new SQL couplingA SQL table for categories β€” adds coupling to the store we're moving away from
Tagging events deferred to a later phasePhase 1 is taxonomy only; events live in MySQL (groups) β€” deferring keeps Phase 1 SQL-freeBuild event↔category tagging now β€” out of phase + couples to SQL
Reuse organization-dashboard DataGrid + table-view-providerMatches communities-table/resources-table; server pagination solvedHand-roll a table β€” more work, divergent
Anchor UI to the Claude artifactProduct decision; simpler column setFigma Make per-hub model β€” deferred (OQ-A)
Build order: design-system + backend API before FE wiringFE depends on bothParallel-blind β€” FE rework

3. Architecture​

Phase 1 is Mongo-only. The existing entities it references (organizations, communities, events) live in Aurora/MySQL and are co-owned by the legacy PHP β€” Phase 1 references them by value (their integer id stored as a Number), with no foreign key and no SQL change.

  • Embedding: the screen is a React app embedded via iframe inside the legacy PHP (the established mechanism until PHP is retired).
  • Auth: the existing dashboard auth β€” Cognito JWT (bearer + refresh) β€” already works for the embedded screen; no new auth pattern needed (verified against the existing dashboard).

4. UI reference​

The canonical visual the build is checked against and Gali reviews at the end:

  • Claude artifact (interactive): https://claude.ai/public/artifacts/2c7d7677-1242-445c-ae16-89f455f3523c β†’ tab Admin Categories. Local: ~/Downloads/categories-ui-preview (1).html; screenshot ~/categories-admin-claude-artifact.jpeg.
  • Secondary (deferred): Figma Make figma.com/make/Q7qP6JwTSt8cHgAefLrLyd (Level / Resident-Visibility / Icon / per-hub β€” OQ-A).

5. Scope​

In scope (Phase 1 β€” admin taxonomy)Out / later phases
Category CRUD; 2-level subcategories (in the form); colour, statusEvent tagging (category ↔ event) β€” later phase
Community assignment (which communities a category applies to)Consumer filters & feed
List Β· filter (status/community/search) Β· server paginationResident subscription / opt-in UI
Batch activate/deactivate/delete; Import (CSV/XLSX)Notification dispatch (Phase 3)
Stat cards (Total/Active/Subscribers/Tagged) β€” last two placeholderAI auto-categorization
Notification settings modal (settings only)Replacing the legacy system-categories (future migration)
Feature flag + RBAC; Mongo-only, zero SQLβ€”

6. Frontend composition​

Single screen "Categories". Each region β†’ an existing @bewith-dev/design-system component or the dashboard table pattern. new = a design-system delta (Β§12).

RegionComponentsNotes
Header + actionsTypography; Button Γ—3 (primary "New Category", secondary "Import", "Notification Settings")Mirrors in RTL.
Stat cards (4)SummaryCard Γ—4 (+ SummaryCardSkeleton)Subscribers/Tagged = placeholders.
FiltersSearch Β· Select (Status) Β· Select (Community)
Table + paginationdashboard DataGrid + table-view-provider (reuse)Server paging (page/pageSize=25); {records, pagination:{currentPage,pageSize,totalResults}}.
ColsCategory (dot + name + desc) Β· Status Chip status-badge new Β· Communities Chip cell + "+N" new Β· Events Β· Subscribers Β· Actions (IconButton edit/delete)Subscribers = placeholder.
Create/Edit modalModal, TextField, ColorPicker, Select/Toggle, MultiSelectSearchInput, subcategories editor, ButtonValidation: duplicate name, required.
Manage CommunitiesModal, MultiSelectSearchInput, ButtonTitle Manage Communities β€” <name>.
Delete/DeactivateConfirmationModalDeactivate (kept, hidden) vs Delete (impact summary).
ImportModal, DropZone (≀5MB), template Button, preview tablePer-row validation.
Batchtoolbar + BatchWarningModalActivate/Deactivate/Delete.
Notification SettingsModal, per-category settingsSettings only β€” no dispatch.

Import flow:

7. States​

Drawn in the artifact: populated, empty (CTA), batch toolbar, all modals, create toast. To build (undrawn β€” OQ-9): loading/skeleton, error / access-error, pagination loading / last-page. Per-state a11y in Β§13.

8. Backend β€” data model, API, integrity (outline)​

A new, independent system in backend-services (NestJS) on MongoDB. Names proposed β€” OQ-1. No SQL schema change in Phase 1; org/community referenced by their MySQL integer id stored as a Number.

Collections (Mongo)

CollectionPurposeKey fields
event-categoriescategory / subcategory (the new taxonomy)key (stable, immutable), name, description, color, status, parentId? (2-level), scope (system/org/community), organizationId (Number), audit, i18n
event-category-communitieswhich communities a category applies tocategoryId, communityId (Number)

The default category list (SYSTEM scope). A future PM-provided default set (Gali) will seed scope: system rows into event-categories via migrations-tool, read-only to customer admins. Phase 1 fixes the location + shape for it; the list content arrives later (OQ-E). It is a new, independent list β€” not derived from the legacy system-categories.

Integrity rules

  • Unique name per scope + community (FR-16) β€” index + validation.
  • key: a stable, immutable identifier per category β€” every later phase (tagging, subscriptions, AI) will reference it; getting it right now is cheap, retrofitting is not.
  • 2-level max: a parentId must reference a top-level category; reject depth > 2.
  • Delete vs Deactivate: deactivate keeps the row + community links (hidden); delete removes the category + its *-communities rows.
  • Scope: system (default set, read-only to customer admins), org, community; every query scoped by organizationId / communityId.

API (resource categories, /api/v2 via external-gateway):

Method Β· pathPurposeFR
GET /categorieslist β€” filters (status, community, search) + paginationFR-02/03
POST /categories Β· PUT /:id Β· DELETE /:idCRUDFR-01/04/05
POST /categories/batchbatch activate/deactivate/deleteFR-06
POST /categories/importimport (validate, per-row errors)FR-07
PUT /categories/:id/communitiesassign communitiesFR-08
GET /categories/statsstat-card countsFR-09

Event tagging β€” deferred, recommended shape (for the later phase, reserved now). When events get tagged, do it in a new Mongo collection (e.g. event-category-tags), one doc per event↔category link, holding categoryId + groupId (the MySQL event int id, by value) + organizationId + communityId + source, with a unique index on groupId + categoryId. Rationale: zero SQL schema, zero new SQL coupling β€” aligned with moving off SQL (mirrors the existing resource-waitlist-member pattern). Integrity is service-layer (no cross-store FK); orphan tags are cleaned on event-delete. Rejected: a MySQL groups_categories table β€” it deepens SQL coupling.

9. Security & multi-tenancy​

ActorCreate/Edit/DeleteAssign communitiesSYSTEM-scope rows
Org adminall org/community categoriesall org communitiesread-only
Community adminonly within their communitiestheir communities onlyread-only
Systemplatform-managed (seed)β€”manage
  • SYSTEM read-only enforced server-side (not just hidden) β€” FR-09.
  • Guard: the existing category CRUD is community-admin (@CommunityGuard()); a SYSTEM/ORG/COMMUNITY model also needs an org-admin guard for org-scope actions (OQ-F).
  • Tenant isolation: every read/write scoped by organizationId / communityId; reject cross-tenant ids.
  • Import safety: size cap (≀5MB), format allow-list (CSV/XLSX), row cap (assumption), CSV-formula-injection sanitisation, server-side validation.
  • Auth/embedding: existing Cognito JWT; the iframe-embedded screen uses the established dashboard auth β€” no new pattern (confirmed).

10. Non-functional​

  • Scale (assumption β€” confirm): tens–low-hundreds of categories per org; pageSize 25.
  • List query indexed on organizationId + status + parentId; name search.
  • Batch cap per op (assumption β€” ≀100); complete < 5s, progress if slower.
  • Import sync for small files (assumption β€” async if large).

11. Migration & existing data​

  • Phase 1 is purely additive and Mongo-only β€” new collections, no SQL migration, reversible (flag off).
  • The legacy system-categories / communities-categories are not touched and not referenced. This new system will replace them in a future migration (separate effort, out of Phase-1 scope) β€” flag as an ADR when that work is scoped.
  • No event↔category data in Phase 1 (tagging deferred).

12. Design-system deltas​

TargetDeltaDS task
Chip β€” status badgevariant (Active/Inactive, semantic tokens)/add-variant Chip status-badge
Communities cellchips + "+N" overflow (composes Chip)/add-composite CommunitiesCell (or reuse)

Reused: table + pagination ride the dashboard DataGrid + table-view-provider; Modal, ConfirmationModal, BatchWarningModal, ColorPicker, MultiSelectSearchInput, DropZone, SummaryCard(+Skeleton), Search, Select, Button, Typography, IconButton β€” all in COMPONENTS.md. Tokens only, no raw hex.

13. i18n / RTL & Accessibility​

  • i18n/RTL: English + Hebrew; copy via t() (Hebrew strings in PRD Β§7.4 / PM spec); logical CSS + theme direction; DataGrid verified both directions.
  • a11y (WCAG 2.1 AA, validated by /design-review): table semantics + per-category checkbox names + status-not-colour-only + announced pagination; modal-form aria-invalid/aria-describedby errors stating problem+correction + focus-first-invalid + labelled Selects/ColorPicker; modal focus trap/restore + alertdialog for destructive; empty/access-error headings + focus; toasts aria-live; translated icon-button names; AA token contrast.

14. Test plan & acceptance criteria​

Levels: unit (services/validation), integration (API + Mongo), e2e (screen flows incl RTL). Key scenarios:

ScenarioFR
Create valid; reject duplicate name in scopeFR-01/16
Edit; delete vs deactivate (impact preserved)FR-04/05
Batch activate/deactivate/deleteFR-06
Import: valid rows + per-row errorsFR-07
Assign communitiesFR-08
Filter (status/community) + search + paginationFR-02/03
RBAC: community-admin cross-community denied; SYSTEM read-onlyFR-09
RTL render + a11y AAβ€”

Acceptance criteria: the PRD Β§9 / PM-spec criteria for Phase-1 admin scope (not restated) β€” each maps to a scenario above.

15. Open questions​

The agenda to close in the DR with Aviad. None blocks authoring this doc.

#QuestionOwnerBlocks build?
OQ-1Final names (flag, collection) + copy ("Categories" vs "Category Settings")Aviadyes (contract)
OQ-AScope/Level (System/Org/Community) β€” the artifact has no Level column, but the model is scope-based. Surface scope in the UI (column/badge), or keep it data-only in P1?Aviad / PMscope
OQ-BSubscribers card+column β€” placeholder now, or remove until later-phase data?PMno
OQ-CHierarchy: 2 levels (category β†’ subcategory), managed in the form, table flat (assumed) β€” confirm; or show subcategories in the table (expand)?PM / designno
OQ-EThe PM-provided default category list (Gali) β€” timing, format, breadth. Blocks the SYSTEM seed, not the P1 screen.Gali / PMseed only
OQ-Forg-admin guard for org-scope actions (existing CRUD guard is community-admin)auth / backendyes
OQ-9Undrawn states (loading/error/access-error) β€” frames or accept Β§7 as contractdesignno

Resolved this round: tagging deferred (taxonomy-only); new independent system replacing the legacy (no coexistence/reference); event-tag junction β†’ Mongo (minimal SQL coupling); iframe-auth β†’ existing Cognito JWT (OQ-D closed); machine-readable source (OQ-8 closed).

16. Risks & dependencies​

Risk / dependencyImpactMitigation
OQ-1 naming unresolvedblocks model/API contractresolve in DR before T3/T4
OQ-F org-admin guardorg-scope actions can't be authoriseddecide guard in DR; route to auth-security
OQ-E default-list timingSYSTEM seed can't shipP1 screen works without it; seed is a separate deliverable
design-system deltas land lateblocks FE T9/T11sequence T1/T2 first
Future SQL decouplingthe eventual event-tagging keeps off SQLreserved Mongo junction shape (Β§8)

17. Task breakdown & effort​

Effort is relative (S/M/L), to confirm in planning β€” not hours. Feeds /breakdown. All backend work is Mongo-only.

#AreaTaskEffortDepends on
T1design-systemChip status-badge variant + tokensSβ€”
T2design-systemCommunities cell (chips + "+N")Sβ€”
T3backendevent-categories Mongo model + indexes + keySOQ-1
T4backendCRUD API + list filters + paginationMT3
T5backendBatch endpointST4
T6backendImport (parse/validate/per-row errors)MT4
T7backendCommunity assignment + statsST4
T8backendRBAC (org + community guard) + SYSTEM read-only + feature flagMT4, OQ-F
T9frontendScreen on DataGrid + provider (columns/cells/pagination)MT1,T2,T4
T10frontendStat cards + filtersST4,T7
T11frontendCreate/Edit modalMT1,T4
T12frontendManage-Communities / Import / Batch / Delete-Deactivate / Notification-Settings modalsLT4,T5,T6
T13frontendStates + toastsST9
T14frontendi18n (he/en) + RTL passST9–T13
T15qaUnit + integration + e2e (Β§14 scenarios)Mmost

18. Definition of Done (Phase 1)​

  • All P0 FRs implemented; OQ-1 / OQ-A / OQ-C / OQ-F resolved.
  • /design-review pass (design-system anchoring + a11y AA + RTL verified).
  • Β§14 scenarios green; lint / typecheck / tests / build green; no secrets; CU-86c1p963g linked.
  • Behind the feature flag; RBAC + SYSTEM read-only enforced server-side; no SQL change.
  • Reviewed with Aviad (DR) before build; Β§17 broken out via /breakdown and estimated.

19. Downstream​

/plan-feature / /breakdown consume Β§17; design-system ships Β§12; frontend-developer + backend-developer build on the existing patterns; later phases (event tagging, consumer filters, subscriptions, dispatch, AI, and the legacy-replacement migration) get sibling docs in event-categories/.