docs: add IDAA client module doc, minor whitespace cleanup
- CLIENT__IDAA_and_customized_mods.md: New comprehensive doc covering IDAA architecture, all 4 submodules (Archives, BB, Recovery Meetings, Jitsi), Novi UUID auth system, permission levels, state stores, iframe integration, and testing requirements. Reverse-engineered from source 2026-02-26. - MODULE__AE_Events_Badges.md: trailing whitespace only - tests/README.md: blank line only
This commit is contained in:
352
documentation/CLIENT__IDAA_and_customized_mods.md
Normal file
352
documentation/CLIENT__IDAA_and_customized_mods.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# CLIENT: IDAA — International Doctors in Alcoholics Anonymous
|
||||
|
||||
**Client:** International Doctors in Alcoholics Anonymous (IDAA)
|
||||
**Module Path:** `src/routes/idaa/`
|
||||
**State Stores:** `src/lib/stores/ae_idaa_stores.ts`
|
||||
**Last Updated:** 2026-02-26
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ CRITICAL PRIVACY REQUIREMENT
|
||||
|
||||
**ALL IDAA content is PRIVATE. Authentication is required for ALL modules.**
|
||||
|
||||
IDAA serves a sensitive population — physicians in addiction recovery. Content exposure to the public is a **severe security failure** and a violation of member trust.
|
||||
|
||||
- A previous AI agent accidentally exposed IDAA Bulletin Board content publicly. This must never happen again.
|
||||
- Every route, component, and API call in this module must enforce authentication.
|
||||
- When in doubt: **it's private**.
|
||||
|
||||
**Required access level:** `trusted_access` or higher for all IDAA content.
|
||||
|
||||
---
|
||||
|
||||
## What IDAA Is
|
||||
|
||||
IDAA is a private membership organization for physicians in recovery. They use the Aether platform for:
|
||||
- A private document archive (historical materials, meeting records)
|
||||
- A members-only bulletin board (community posts and discussion)
|
||||
- A searchable directory of in-person and virtual recovery meetings
|
||||
- Video conferencing (Jitsi-based)
|
||||
|
||||
IDAA's Aether instance is embedded as an **iframe inside their existing Novi-powered website** (`idaa.org`). Novi is their external Association Management System (AMS) — it handles membership records and authentication. Aether receives the member context via URL parameters on iframe load.
|
||||
|
||||
---
|
||||
|
||||
## Architecture: Composite Module
|
||||
|
||||
IDAA is **not a standalone module** — it is a **composition of three existing Aether modules**, access-gated and branded for the IDAA client.
|
||||
|
||||
| IDAA Feature | Aether Module Used | Library |
|
||||
|---|---|---|
|
||||
| Archives | Archives module | `src/lib/ae_archives/` |
|
||||
| Bulletin Board (BB) | Posts module | `src/lib/ae_posts/` |
|
||||
| Recovery Meetings | Events module (repurposed) | `src/lib/ae_events/` |
|
||||
| Video Conferences | Jitsi (external embed) | External |
|
||||
|
||||
There is **no `src/lib/ae_idaa/`** library directory. IDAA-specific state and logic lives in `ae_idaa_stores.ts` and the route components only.
|
||||
|
||||
This design allows the IDAA module to be removed or updated without touching core modules.
|
||||
|
||||
---
|
||||
|
||||
## Route Structure
|
||||
|
||||
```
|
||||
src/routes/idaa/
|
||||
├── +layout.svelte # Root layout: Novi UUID extraction, iframe height sync
|
||||
├── (idaa)/
|
||||
│ ├── +layout.svelte # Access gate: blocks render if unauthorized; permission upgrade
|
||||
│ ├── +page.svelte # IDAA dashboard — 3-module selector
|
||||
│ ├── archives/ # Archives submodule
|
||||
│ │ ├── +page.svelte # Archive list (LiveQuery)
|
||||
│ │ └── [archive_id]/
|
||||
│ │ ├── +page.svelte # Archive detail + content viewer
|
||||
│ │ ├── ae_idaa_comp__archive_obj_id_view.svelte
|
||||
│ │ ├── ae_idaa_comp__archive_obj_id_edit.svelte
|
||||
│ │ ├── ae_idaa_comp__archive_content_obj_id_edit.svelte
|
||||
│ │ └── ae_idaa_comp__modal_media_player.svelte
|
||||
│ ├── bb/ # Bulletin Board (Posts) submodule
|
||||
│ │ ├── +page.svelte # Post list (LiveQuery, archive-filtered)
|
||||
│ │ └── [post_id]/
|
||||
│ │ ├── +page.svelte # Post detail + comments
|
||||
│ │ ├── ae_idaa_comp__post_obj_id_view.svelte
|
||||
│ │ ├── ae_idaa_comp__post_obj_id_edit.svelte
|
||||
│ │ └── ae_idaa_comp__post_comment_obj_id_edit.svelte
|
||||
│ ├── recovery_meetings/ # Recovery Meetings (Events repurposed)
|
||||
│ │ ├── +page.svelte # Meeting list + search filters
|
||||
│ │ └── [event_id]/
|
||||
│ │ ├── +page.svelte # Meeting detail
|
||||
│ │ ├── ae_idaa_comp__event_obj_id_view.svelte
|
||||
│ │ └── ae_idaa_comp__event_obj_id_edit.svelte
|
||||
│ └── video_conferences/ # Jitsi video conference integration
|
||||
└── jitsi_reports/ # External Jitsi reporting
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication: Novi UUID System
|
||||
|
||||
IDAA members do not log in through Aether — they log in through Novi (idaa.org), and Novi passes their identity to the Aether iframe via URL parameters.
|
||||
|
||||
### URL Parameters (on iframe load)
|
||||
```
|
||||
?uuid=<36-char-uuid>
|
||||
&email=<url-encoded-email>
|
||||
&full_name=<url-encoded-name>
|
||||
&iframe=true
|
||||
```
|
||||
|
||||
### Permission Levels (Ascending)
|
||||
| Level | Condition | Access |
|
||||
|---|---|---|
|
||||
| Anonymous | No UUID or unrecognized | No access |
|
||||
| Authenticated | Valid UUID (36 chars) | View own content, limited actions |
|
||||
| Trusted | UUID in `novi_trusted_li` | Full member access to all IDAA content |
|
||||
| Administrator | UUID in `novi_admin_li` | Full access + edit/manage |
|
||||
|
||||
`novi_trusted_li` and `novi_admin_li` are managed in Aether site config (not in Novi directly).
|
||||
|
||||
### Permission Upgrade Rule
|
||||
```
|
||||
// RULE: Only UPGRADE to Novi-based permissions, NEVER downgrade.
|
||||
// If a user has a higher global Aether role (site manager, super),
|
||||
// their global role is preserved and not overwritten by Novi auth.
|
||||
```
|
||||
|
||||
This ensures that OSIT staff with `super` or `manager` roles retain full access regardless of Novi UUID status.
|
||||
|
||||
### Access Gate (`(idaa)/+layout.svelte`)
|
||||
The inner layout blocks ALL rendering if the user is not authorized:
|
||||
- Anonymous → "Access Denied" error page
|
||||
- Access check runs before any child routes render
|
||||
|
||||
---
|
||||
|
||||
## Module 1: Archives
|
||||
|
||||
**Route:** `/idaa/archives/`
|
||||
**Library:** `src/lib/ae_archives/`
|
||||
**Types:** `ae_Archive`, `ae_ArchiveContent`
|
||||
|
||||
The Archives module stores IDAA historical content — meeting records, conference proceedings, historical documents, and media.
|
||||
|
||||
### Object Types
|
||||
|
||||
**Archive (Container)**
|
||||
- Represents a collection (e.g., "2019 Conference Proceedings")
|
||||
- Key fields: `name`, `description`, `original_datetime`, `original_location`, `archive_on`
|
||||
- `archive_on` — date when this archive collection is auto-hidden (scheduled visibility control)
|
||||
|
||||
**ArchiveContent (Items)**
|
||||
- Individual items within an archive
|
||||
- Supports multiple content types: `'text'`, `'file'`, `'url'`, `'video'`
|
||||
- Key fields: `archive_content_type`, `content_html`, `url`, `hosted_file_id`, `duration`
|
||||
- Video/audio content has a dedicated media player component
|
||||
|
||||
### Database (Dexie)
|
||||
```
|
||||
db_archives.archive — Archive containers
|
||||
db_archives.content — Archive content items (linked by archive_id)
|
||||
```
|
||||
|
||||
### Demo / Test IDs
|
||||
- Archive: `nAA2bHLv8RK` (id: 1) "One Sky Test Archive"
|
||||
- Archive Content: `UjKzrk-GKu5` (id: 1) "Hosted File Test"
|
||||
|
||||
---
|
||||
|
||||
## Module 2: Bulletin Board (BB)
|
||||
|
||||
**Route:** `/idaa/bb/`
|
||||
**Library:** `src/lib/ae_posts/`
|
||||
**Types:** `ae_Post`, `ae_PostComment`
|
||||
|
||||
The BB is the IDAA members-only community discussion board. It is the **most sensitive module** — public exposure must never occur.
|
||||
|
||||
### Object Types
|
||||
|
||||
**Post (Thread)**
|
||||
- Key fields: `title`, `content`, `anonymous`, `full_name`, `email`
|
||||
- `archive_on` — date after which the post is hidden from all views
|
||||
- `archive` — boolean flag for immediate archival
|
||||
- `enable_comments` — controls whether replies are allowed
|
||||
- `post_comment_count` — cached count of replies
|
||||
|
||||
**PostComment (Reply)**
|
||||
- Key fields: `post_id`, `content`, `anonymous`, `full_name`, `email`
|
||||
- Replies inherit the parent post's visibility rules
|
||||
|
||||
### Post Visibility / Archival Filter
|
||||
Posts with `archive_on` set to a past date are **automatically hidden** from all queries. This is enforced at the component level via a LiveQuery filter:
|
||||
|
||||
```typescript
|
||||
// This filter is REQUIRED — do not remove it
|
||||
filter((x) => !x.archive_on || archiveDate > now)
|
||||
```
|
||||
|
||||
Archived posts are soft-deleted — they remain in the database for audit purposes but are not shown to members.
|
||||
|
||||
Most recent first (sorted `updated_on DESC`).
|
||||
|
||||
### Database (Dexie)
|
||||
```
|
||||
db_posts.post — Posts (threads)
|
||||
db_posts.comment — Post comments (linked by post_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module 3: Recovery Meetings
|
||||
|
||||
**Route:** `/idaa/recovery_meetings/`
|
||||
**Library:** `src/lib/ae_events/` (standard Events module, repurposed)
|
||||
**Types:** `ae_Event` (standard event type, filtered for meeting context)
|
||||
|
||||
Recovery Meetings reuses the Aether Events object to represent AA recovery meetings. These are NOT conferences — they are regular ongoing meetings (weekly, monthly, etc.) available to IDAA members.
|
||||
|
||||
### Search Filters
|
||||
Members can filter meetings by:
|
||||
- **Fulltext search** — name, location
|
||||
- **Physical** — in-person meetings
|
||||
- **Virtual** — online meetings (Zoom, Google Meet, etc.)
|
||||
- **Meeting type** — specific meeting format categories
|
||||
|
||||
Search is debounced (250ms) and uses the standard Aether SWR pattern.
|
||||
|
||||
### Jitsi Integration
|
||||
Some virtual meetings are hosted via Jitsi. Members with a Jitsi moderator UUID (`novi_jitsi_mod_li`) have elevated permissions in video sessions.
|
||||
|
||||
### Demo / Test IDs
|
||||
No dedicated IDAA recovery meeting demo records — uses the standard Event demo record for dev:
|
||||
- Event: `pjrcghqwert` (id: 1) "Demo One Sky IT Conference"
|
||||
|
||||
---
|
||||
|
||||
## Module 4: Video Conferences (Jitsi)
|
||||
|
||||
**Route:** `/idaa/video_conferences/`
|
||||
|
||||
Embeds Jitsi video conferences directly in the IDAA module. Separate from Recovery Meetings — this is for IDAA board meetings or special sessions, not regular AA meetings.
|
||||
|
||||
Moderation permissions are controlled by `novi_jitsi_mod_li` in the IDAA store.
|
||||
|
||||
---
|
||||
|
||||
## State Management (`ae_idaa_stores.ts`)
|
||||
|
||||
Four stores manage all IDAA state:
|
||||
|
||||
### `idaa_loc` (localStorage — persistent across sessions)
|
||||
Stores Novi auth context and per-submodule query settings:
|
||||
```typescript
|
||||
{
|
||||
novi_uuid: string | null // Member UUID from Novi
|
||||
novi_email: string | null
|
||||
novi_full_name: string | null
|
||||
novi_admin_li: string[] // Admin UUID list (from site config)
|
||||
novi_trusted_li: string[] // Trusted member UUID list
|
||||
novi_jitsi_mod_li: string[] // Jitsi moderator UUIDs
|
||||
|
||||
archives: { enabled, hidden, limit, offset, edit__archive_obj, edit__archive_content_obj }
|
||||
bb: { enabled, hidden, limit, offset, edit__post_obj, edit__post_comment_obj }
|
||||
recovery_meetings: { qry__fulltext_str, qry__physical, qry__virtual, qry__type, qry__limit, edit__event_obj }
|
||||
}
|
||||
```
|
||||
|
||||
### `idaa_sess` (sessionStorage — cleared on tab close)
|
||||
UI state per submodule:
|
||||
```typescript
|
||||
{
|
||||
archives: { qry__status, show__modal_edit__archive_id, show__modal_view__archive_id, obj_changed }
|
||||
bb: { qry__status, show__modal_edit__post_id, show__modal_view__post_id, obj_changed }
|
||||
recovery_meetings: { qry__status, show__modal_edit, show__modal_view, attend_platform, obj_changed }
|
||||
}
|
||||
```
|
||||
|
||||
### `idaa_slct` (sessionStorage — selection tracking)
|
||||
```typescript
|
||||
{
|
||||
event_id: string | null
|
||||
archive_id: string | null
|
||||
archive_content_id: string | null
|
||||
post_id: string | null
|
||||
post_comment_id: string | null
|
||||
}
|
||||
```
|
||||
|
||||
### `idaa_trig` / `idaa_prom`
|
||||
Trigger flags and promise tracking for async operations (standard Aether pattern).
|
||||
|
||||
---
|
||||
|
||||
## Iframe Integration
|
||||
|
||||
The IDAA module is embedded in `idaa.org` via iframe. This requires:
|
||||
|
||||
1. **Height sync** — The root layout posts `message` events to the parent frame for dynamic height adjustment (content length varies)
|
||||
2. **URL parameter auth** — Novi passes member context via query string on load
|
||||
3. **No standard navigation** — Members navigate within the iframe; Aether's nav chrome is hidden or minimal in this context
|
||||
|
||||
---
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Auth Gate Tests Come First
|
||||
**For every IDAA submodule, the first test written must be an authentication enforcement test.**
|
||||
|
||||
```typescript
|
||||
// ✅ Required test pattern for each IDAA module
|
||||
test('Archives - unauthenticated user cannot access content', async ({ page }) => {
|
||||
// Inject localStorage WITHOUT trusted_access
|
||||
// Navigate to /idaa/archives/
|
||||
// Assert: access denied message shown, no archive content visible
|
||||
});
|
||||
|
||||
test('Archives - trusted member can access content', async ({ page }) => {
|
||||
// Inject localStorage WITH trusted_access + novi_uuid
|
||||
// Navigate to /idaa/archives/
|
||||
// Assert: archive list renders
|
||||
});
|
||||
```
|
||||
|
||||
### Privacy in Test Data
|
||||
- Never use real member data in test fixtures
|
||||
- Use canonical demo IDs from `tests/_helpers/env.ts` only
|
||||
- Test names should document the privacy rule being enforced, not just the behavior
|
||||
|
||||
### Trusted Access State Injection
|
||||
Tests that need authenticated IDAA access must set `trusted_access: true` and `novi_uuid` in the injected `ae_loc` localStorage:
|
||||
```typescript
|
||||
// In addInitScript or env helper
|
||||
ae_loc.trusted_access = true;
|
||||
ae_loc.idaa_loc = { novi_uuid: 'test-uuid-value', ... };
|
||||
```
|
||||
|
||||
### Current Test Coverage (as of 2026-02-26)
|
||||
| Module | State | Notes |
|
||||
|---|---|---|
|
||||
| Archives | ⚠️ Smoke only | `archive_content.test.ts` — no auth gate test |
|
||||
| Bulletin Board | ❌ None | Priority — most sensitive module |
|
||||
| Recovery Meetings | ❌ None | — |
|
||||
| Video Conferences | ❌ None | Jitsi complexity, lower priority |
|
||||
|
||||
---
|
||||
|
||||
## External Links (idaa.org)
|
||||
- Archives: `https://www.idaa.org/idaa-archives`
|
||||
- Bulletin Board: `https://www.idaa.org/idaa-bulletin-board`
|
||||
- Meetings: `https://www.idaa.org/idaa-meetings`
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
- [AE API V3 for Frontend](./GUIDE__AE_API_V3_for_Frontend.md)
|
||||
- [Development Guide](./GUIDE__Development.md)
|
||||
- [Naming Conventions](./AE__Naming_Conventions.md)
|
||||
- [Playwright Test README](../tests/README.md)
|
||||
|
||||
---
|
||||
|
||||
**Document Status:** ✅ Complete (initial)
|
||||
**Last Verified:** 2026-02-26 — reverse-engineered from source code
|
||||
@@ -378,7 +378,7 @@ Button has `data-testid="badge-print-btn"` and shows loading/done/error states w
|
||||
|
||||
### Print Workflow
|
||||
1. **Pre-Print:** Check `print_count` to warn if already printed
|
||||
2. **Print:** `window.print()` — standard browser print dialog. Works well in Chrome, Chromium, and Firefox. Chrome is the recommended browser for onsite badge printing (most stable in production).
|
||||
2. **Print:** `window.print()` — standard browser print dialog. Works well in Chrome, Chromium, and Firefox. Chrome is the recommended browser for onsite badge printing (most stable in production).
|
||||
3. **Post-Print:** Handled by `handle_print_badge()` — count + timestamps updated
|
||||
4. **Audit:** Print history available for staff review
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ Help
|
||||
* Aether development API: 'https://dev-api.oneskyit.com'
|
||||
|
||||
These are IDs for records that we can use for testing. Please do not delete them. They are also used for demo purposes with clients.
|
||||
|
||||
### Core Modules
|
||||
* Aether test/demo Account: '_XY7DXtc9MY' (1) "One Sky IT Demo"
|
||||
* Aether test/demo Site: '92vkYC4fVEl' (12) "One Sky IT Demo"
|
||||
|
||||
Reference in New Issue
Block a user