docs(badges): comprehensive module documentation
COMPLETE DOCUMENTATION: - Override fields pattern (*_override) and sync behavior - External system integration (iMIS, Zoom, Novi, Impexium, Confex, Cvent) - Cron sync safety rules (override fields NEVER overwritten) - Access level permissions (Authenticated → Super) - Search/filter capabilities (fulltext, badge type, print status, affiliations) - Quick edit implementation details - Database schema and API functions - Component architecture - Testing status and known issues - Development guidelines KEY CONCEPTS DOCUMENTED: 1. Override fields protect staff/attendee edits from automated syncs 2. Data flow is pull-only (read from external systems, never push back) 3. Display priority: override field → regular field → fallback 4. Access-based editing (currently placeholder, future JSON config) 5. All 7 override fields explained with examples SYNC BEHAVIOR CLARIFIED: - Cron jobs CAN update regular fields - Cron jobs CANNOT touch override fields - Manual staff edits CAN update any field - Attendee self-service limited to specific overrides per event config
This commit is contained in:
586
documentation/MODULE__AE_Events_Badges.md
Normal file
586
documentation/MODULE__AE_Events_Badges.md
Normal file
@@ -0,0 +1,586 @@
|
||||
# MODULE: Aether Events — Badges
|
||||
|
||||
**Module Path:** `src/routes/events/[event_id]/(badges)/badges/`
|
||||
**API Module:** `src/lib/ae_events/ae_events__event_badge.ts`
|
||||
**Database:** `db_events.badge` (Dexie IndexedDB table)
|
||||
**Last Updated:** 2026-02-26
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Badges module manages event attendee badges with support for:
|
||||
- **External system imports** (iMIS, Zoom, Novi, Impexium, Confex, Cvent, and others)
|
||||
- **Field override protection** to prevent staff/attendee edits from being overwritten by automated syncs
|
||||
- **Multi-tier access control** for field editing
|
||||
- **QR code generation** for badge scanning
|
||||
- **Print tracking** (count, first/last print datetime)
|
||||
- **Advanced search and filtering**
|
||||
|
||||
---
|
||||
|
||||
## Critical Design Pattern: Override Fields
|
||||
|
||||
### Purpose
|
||||
The `*_override` fields pattern protects data from being overwritten during scheduled cron syncs from external systems. This is essential because:
|
||||
1. Staff may need to correct imported data
|
||||
2. Attendees may be allowed to self-update certain fields (e.g., preferred name, pronouns)
|
||||
3. External systems often have outdated or incorrect data
|
||||
4. Changes should persist across multiple sync cycles
|
||||
|
||||
### How It Works
|
||||
|
||||
**Import Behavior:**
|
||||
```
|
||||
External System → Aether API → Populates REGULAR fields only
|
||||
(never touches *_override fields)
|
||||
```
|
||||
|
||||
**Display Behavior:**
|
||||
```
|
||||
UI Display Logic:
|
||||
1. IF `*_override` field has value → USE IT (highest priority)
|
||||
2. ELSE IF regular field has value → USE IT (fallback)
|
||||
3. ELSE → Display placeholder/empty
|
||||
```
|
||||
|
||||
**Example — Full Name:**
|
||||
```typescript
|
||||
// API imports from iMIS
|
||||
badge.given_name = "Robert"
|
||||
badge.family_name = "Smith"
|
||||
badge.full_name = "Robert Smith" // Auto-computed
|
||||
|
||||
// Staff edits to preferred name
|
||||
badge.full_name_override = "Bob Smith"
|
||||
|
||||
// Display in UI
|
||||
display_name = badge.full_name_override || badge.full_name || "-- no name --"
|
||||
// Result: "Bob Smith"
|
||||
|
||||
// Next cron sync from iMIS
|
||||
// ✅ badge.full_name updated to "Robert J. Smith" (middle initial added)
|
||||
// ✅ badge.full_name_override remains "Bob Smith" (PROTECTED)
|
||||
// ✅ Display still shows "Bob Smith"
|
||||
```
|
||||
|
||||
### Override Fields (7 total)
|
||||
|
||||
| Regular Field | Override Field | Purpose | Editable By |
|
||||
|---|---|---|---|
|
||||
| `professional_title` | `professional_title_override` | Job title display | Staff, Attendee |
|
||||
| `full_name` | `full_name_override` | Preferred name, pronouns | Staff, Attendee |
|
||||
| `affiliations` | `affiliations_override` | Organization display | Staff, Attendee |
|
||||
| `email` | `email_override` | Contact email override | Staff only |
|
||||
| `location` | `location_override` | City/State/Country display | Staff, Attendee |
|
||||
| `badge_type` | `badge_type_override` | Badge category label | Staff only |
|
||||
| `badge_type_code` | `badge_type_code_override` | Badge access level code | Staff only |
|
||||
|
||||
### Sync Safety Rules
|
||||
|
||||
**Automated Sync (Cron Jobs):**
|
||||
- ✅ CAN update: All regular fields (`given_name`, `family_name`, `email`, `affiliations`, etc.)
|
||||
- ❌ CANNOT update: Any `*_override` field
|
||||
- ❌ CANNOT delete: Any `*_override` value
|
||||
|
||||
**Manual Staff Edit:**
|
||||
- ✅ CAN update: Any field (including overrides)
|
||||
- ✅ CAN clear: Override fields (reverts to regular field)
|
||||
|
||||
**Attendee Self-Service Edit:**
|
||||
- ✅ CAN update: Only specific override fields (per event config)
|
||||
- ✅ CAN clear: Their own override fields
|
||||
- ❌ CANNOT edit: Regular fields, badge_type, email_override
|
||||
|
||||
---
|
||||
|
||||
## External System Integration
|
||||
|
||||
### Supported Import Sources
|
||||
- **iMIS** (Association Management)
|
||||
- **Zoom** (Virtual event registration)
|
||||
- **Novi AMS** (Association Management)
|
||||
- **Impexium** (Association Management)
|
||||
- **Confex** (Event abstract management)
|
||||
- **Cvent** (Event registration)
|
||||
- **Custom CSV/Excel** imports
|
||||
|
||||
### Data Flow Direction
|
||||
```
|
||||
External Systems ─────────> Aether
|
||||
(READ ONLY) (WRITE + DISPLAY)
|
||||
```
|
||||
|
||||
**Important:** Aether is **pull-only** — does not push changes back to external systems. This prevents sync conflicts and maintains external systems as the source of truth for base data.
|
||||
|
||||
### Sync Behavior
|
||||
- **Frequency:** Scheduled cron jobs (typically hourly, daily, or on-demand)
|
||||
- **Method:** Full sync or incremental (depends on external system API)
|
||||
- **Conflict Resolution:** Override fields always win
|
||||
|
||||
**Pseudocode:**
|
||||
```python
|
||||
def sync_badge_from_external(external_badge_data, existing_badge):
|
||||
# Update regular fields from external source
|
||||
existing_badge.given_name = external_badge_data.first_name
|
||||
existing_badge.family_name = external_badge_data.last_name
|
||||
existing_badge.email = external_badge_data.email
|
||||
existing_badge.affiliations = external_badge_data.organization
|
||||
existing_badge.badge_type_code = external_badge_data.registration_type
|
||||
|
||||
# NEVER TOUCH OVERRIDE FIELDS
|
||||
# existing_badge.full_name_override ← PROTECTED
|
||||
# existing_badge.affiliations_override ← PROTECTED
|
||||
# existing_badge.email_override ← PROTECTED
|
||||
|
||||
return existing_badge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Access Control & Edit Permissions
|
||||
|
||||
### Access Levels (Ascending)
|
||||
1. **Anonymous** — No access to badges
|
||||
2. **Public** — View public event info only (no badge access)
|
||||
3. **Authenticated** — View own badge, limited self-edit
|
||||
4. **Trusted** — Search all badges, view all, edit own
|
||||
5. **Administrator** — Full CRUD, bulk operations, override any field
|
||||
6. **Manager** — All administrator + event configuration
|
||||
7. **Super** — All manager + cross-event operations
|
||||
|
||||
### Current Implementation (v3)
|
||||
|
||||
**Quick Edit Feature** ([badge_id]/+page.svelte → ae_comp__badge_obj_view.svelte)
|
||||
|
||||
**Edit Mode Trigger:**
|
||||
- URL hash `#review` enables edit mode
|
||||
- Sets `$ae_loc.edit_mode = true`
|
||||
- Shows Save/Cancel buttons
|
||||
|
||||
**Currently Editable Fields:**
|
||||
```typescript
|
||||
// Lines 90-96 in ae_comp__badge_obj_view.svelte
|
||||
editable_full_name_override: string | null
|
||||
editable_professional_title_override: string | null
|
||||
editable_affiliations_override: string | null // textarea
|
||||
editable_location_override: string | null
|
||||
editable_allow_tracking: boolean | null
|
||||
editable_email: string | null
|
||||
editable_badge_type_code: string | null
|
||||
```
|
||||
|
||||
**UI Components:**
|
||||
- Input fields shown when `edit_mode_active === true`
|
||||
- Save button calls `handle_save_changes()` (Line 365)
|
||||
- Cancel button calls `handle_cancel_changes()` (Line 450)
|
||||
- Only changed fields sent to API (`update_ae_obj__event_badge`)
|
||||
|
||||
### Future Planned Enhancement
|
||||
|
||||
**Event-Level Configuration:** `event.mod_badges_json.edit_permissions`
|
||||
|
||||
```json
|
||||
{
|
||||
"authenticated": {
|
||||
"can_edit": ["full_name_override", "professional_title_override", "affiliations_override", "location_override"],
|
||||
"requires_approval": false
|
||||
},
|
||||
"trusted": {
|
||||
"can_edit": ["full_name_override", "professional_title_override", "affiliations_override", "location_override", "email_override"],
|
||||
"can_view_all": true,
|
||||
"requires_approval": false
|
||||
},
|
||||
"administrator": {
|
||||
"can_edit": "*",
|
||||
"can_bulk_edit": true,
|
||||
"can_delete": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Status:** Placeholder UI exists, JSON config not yet implemented.
|
||||
|
||||
---
|
||||
|
||||
## Search & Filter Capabilities
|
||||
|
||||
### Search Component
|
||||
**File:** `ae_comp__badge_search.svelte`
|
||||
|
||||
### Available Filters
|
||||
|
||||
**Fulltext Search** (All Users)
|
||||
- Searches: `default_qry_str` database field
|
||||
- Includes: Name, email, external IDs
|
||||
- Type: `LIKE %query%` (case-insensitive)
|
||||
- Trigger: Enter key or 3+ characters typed
|
||||
|
||||
**Advanced Filters** (Trusted Access & Above)
|
||||
```typescript
|
||||
// Badge Type Filter
|
||||
badge_type_code: 'current_member' | 'inactive_member' | 'ex_all' | 'staff' | etc.
|
||||
|
||||
// Print Status Filter
|
||||
qry_printed_status: 'all' | 'printed' | 'not_printed'
|
||||
|
||||
// Affiliations Search
|
||||
qry_affiliations: string // Separate filter for organization search
|
||||
|
||||
// Sort Options
|
||||
qry_sort_order:
|
||||
- 'name_asc' / 'name_desc'
|
||||
- 'updated_desc' / 'updated_asc'
|
||||
- 'print_count_desc'
|
||||
- 'print_first_desc' / 'print_last_desc'
|
||||
- 'badge_type_asc'
|
||||
- 'affiliations_asc'
|
||||
```
|
||||
|
||||
### QR Scan Search
|
||||
- Scans badge QR code
|
||||
- Extracts badge ID
|
||||
- Auto-fills search with ID
|
||||
- Jumps to badge detail view
|
||||
|
||||
### Search Implementation Pattern
|
||||
**File:** `badges/+page.svelte` (Lines 117-365)
|
||||
|
||||
**Strategy:** Standardized Reactive Search Pattern (Aether UI V3)
|
||||
1. **Isolate dependencies** into stable `$derived` object
|
||||
2. **Debounced effect** (300ms) triggers search
|
||||
3. **Fast Path:** Search IDB first (if not `remote_first`)
|
||||
4. **Revalidate:** API request updates IDB
|
||||
5. **LiveQuery:** UI auto-updates from IDB changes
|
||||
|
||||
**Search API:** `events_func.search__event_badge()`
|
||||
```typescript
|
||||
await search__event_badge({
|
||||
api_cfg: $ae_api,
|
||||
event_id: event_id,
|
||||
fulltext_search_qry_str: qry_str || null,
|
||||
type_code: type_code || null,
|
||||
printed_status: printed_status,
|
||||
affiliations_qry_str: aff_str || null,
|
||||
order_by_li: order_by_li,
|
||||
limit: 150,
|
||||
log_lvl: 0
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Badge Display Logic
|
||||
|
||||
### Name Display Priority
|
||||
```typescript
|
||||
// Component: ae_comp__badge_obj_li.svelte (Lines 113-121)
|
||||
if (event_badge_obj?.full_name_override)
|
||||
display: full_name_override
|
||||
else if (event_badge_obj?.full_name)
|
||||
display: full_name
|
||||
else
|
||||
display: given_name + ' ' + family_name
|
||||
```
|
||||
|
||||
### Badge View Page
|
||||
**Route:** `/events/[event_id]/badges/[badge_id]`
|
||||
|
||||
**Components:**
|
||||
- `+page.svelte` — Container with LiveQuery for badge data
|
||||
- `ae_comp__badge_obj_view.svelte` — Full badge display + edit UI
|
||||
|
||||
**LiveQueries:**
|
||||
```typescript
|
||||
lq__event_badge_obj = liveQuery(() => db_events.badge.get(event_badge_id))
|
||||
lq__event_badge_template_obj = liveQuery(() =>
|
||||
db_events.badge_template.get(badge.event_badge_template_id)
|
||||
)
|
||||
```
|
||||
|
||||
**Loading States:**
|
||||
- `is_loading_idb` — Waiting for initial IDB lookup
|
||||
- If badge not found → "Badge Not Found" error with reload button
|
||||
- Loader spinner while fetching
|
||||
|
||||
---
|
||||
|
||||
## Badge Templates
|
||||
|
||||
### Purpose
|
||||
Badge templates define the visual layout and content structure for printed badges:
|
||||
- Header images/logos
|
||||
- Field positions and font sizes
|
||||
- QR code placement
|
||||
- Ticket/option indicator display
|
||||
- WiFi credentials display
|
||||
|
||||
### Template Selection
|
||||
Each badge references an `event_badge_template_id`. The template controls:
|
||||
- Layout (front/back)
|
||||
- Branding elements
|
||||
- Which fields to show
|
||||
- Field formatting rules
|
||||
|
||||
### Template Loading
|
||||
Templates are loaded alongside badges via `inc_template` parameter:
|
||||
```typescript
|
||||
load_ae_obj_id__event_badge({
|
||||
event_badge_id: badge_id,
|
||||
inc_template: true // Also loads template
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Print Tracking
|
||||
|
||||
### Print Fields
|
||||
```typescript
|
||||
print_count: number // Increments each print
|
||||
print_first_datetime: string // ISO datetime of first print
|
||||
print_last_datetime: string // ISO datetime of most recent print
|
||||
```
|
||||
|
||||
### Print Workflow (Future)
|
||||
1. **Pre-Print:** Check `print_count` to warn if already printed
|
||||
2. **Print:** Send to printer via Electron native bridge or browser print
|
||||
3. **Post-Print:** Increment `print_count`, update `print_last_datetime`
|
||||
4. **Audit:** Print history available for staff review
|
||||
|
||||
**Current Status:** UI placeholder for print button, tracking fields exist in database, print functionality pending.
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
### IndexedDB Table: `badge`
|
||||
**File:** `src/lib/ae_events/db_events.ts` (Lines 841-852)
|
||||
|
||||
**Indexed Fields:**
|
||||
```typescript
|
||||
badge: `
|
||||
event_badge_id_random, event_badge_id, id,
|
||||
event_id, event_id_random,
|
||||
full_name, full_name_override, email, email_override,
|
||||
affiliations, affiliations_override,
|
||||
badge_type, badge_type_code, badge_type_code_override, badge_type_override,
|
||||
external_event_id, external_id, external_person_id,
|
||||
default_qry_str,
|
||||
alert,
|
||||
tmp_sort_1, tmp_sort_2,
|
||||
print_count, print_first_datetime, print_last_datetime,
|
||||
enable, hide, priority, sort, group, notes, created_on, updated_on
|
||||
`
|
||||
```
|
||||
|
||||
### Saved Properties
|
||||
**File:** `ae_events__event_badge.ts` (Lines 495-563)
|
||||
|
||||
**Complete field list** (67 fields total):
|
||||
- Identity: `id`, `event_badge_id`, `event_id`, `event_badge_template_id`
|
||||
- Name: `pronouns`, `informal_name`, `title_names`, `given_name`, `middle_name`, `family_name`, `designations`
|
||||
- Professional: `professional_title`, `professional_title_override`
|
||||
- Display: `full_name`, `full_name_override`
|
||||
- Organization: `affiliations`, `affiliations_override`
|
||||
- Contact: `email`, `email_override`
|
||||
- Address: `address_line_1`, `address_line_2`, `address_line_3`, `city`, `country_subdivision_code`, `state_province`, `state_province_abb`, `postal_code`, `country_alpha_2_code`, `country`, `full_address`
|
||||
- Location: `location`, `location_override`
|
||||
- Classification: `badge_type`, `badge_type_code`, `badge_type_override`, `badge_type_code_override`
|
||||
- External: `external_event_id`, `external_id`, `external_person_id`
|
||||
- Search: `query_str`, `default_qry_str`
|
||||
- System: `alert`, `enable`, `hide`, `priority`, `sort`, `group`, `notes`, `created_on`, `updated_on`
|
||||
- Print: `print_count`, `print_first_datetime`, `print_last_datetime`
|
||||
- Sorting: `tmp_sort_1`, `tmp_sort_2`
|
||||
- Person Link: `person_external_id`, `person_external_sys_id`, `person_given_name`, `person_family_name`, `person_full_name`, `person_professional_title`, `person_affiliations`, `person_primary_email`, `person_passcode`
|
||||
|
||||
---
|
||||
|
||||
## API Functions
|
||||
|
||||
### CRUD Operations
|
||||
**File:** `src/lib/ae_events/ae_events__event_badge.ts`
|
||||
|
||||
```typescript
|
||||
// Load single badge
|
||||
load_ae_obj_id__event_badge({ event_badge_id, event_id, inc_template })
|
||||
|
||||
// Load badge list
|
||||
load_ae_obj_li__event_badge({ event_id, view, limit, order_by_li })
|
||||
|
||||
// Search badges (V3 API)
|
||||
search__event_badge({
|
||||
event_id,
|
||||
fulltext_search_qry_str,
|
||||
type_code,
|
||||
printed_status,
|
||||
affiliations_qry_str,
|
||||
order_by_li
|
||||
})
|
||||
|
||||
// Create badge
|
||||
create_ae_obj__event_badge({ event_id, data_kv })
|
||||
|
||||
// Update badge
|
||||
update_ae_obj__event_badge({ event_badge_id, event_id, data_kv })
|
||||
|
||||
// Delete badge
|
||||
delete_ae_obj_id__event_badge({ event_badge_id, event_id, method })
|
||||
```
|
||||
|
||||
### Field Processing
|
||||
**Function:** `process_ae_obj__event_badge_props()`
|
||||
|
||||
**Processing Steps:**
|
||||
1. Map `*_random` fields to clean names (`event_badge_id_random` → `event_badge_id`)
|
||||
2. Set primary `id` field from `event_badge_id`
|
||||
3. Ensure `event_id` is set (from function parameter if missing)
|
||||
4. Calculate `tmp_sort_1` and `tmp_sort_2` for efficient sorting
|
||||
5. Return processed objects
|
||||
|
||||
**Critical Fix (2026-02-26):** All CRUD functions now return **processed** data (matches IDB cache) instead of raw API responses. This ensures consistency between function return values and cached data.
|
||||
|
||||
---
|
||||
|
||||
## Component Architecture
|
||||
|
||||
### Route Structure
|
||||
```
|
||||
/events/[event_id]/(badges)/badges/
|
||||
├── +layout.svelte # Layout wrapper (minimal)
|
||||
├── +page.svelte # Badge list + search
|
||||
├── ae_comp__badge_search.svelte # Search form + filters
|
||||
├── ae_comp__badge_obj_li.svelte # Badge list display
|
||||
├── ae_comp__badge_create_form.svelte # (Not actively used)
|
||||
├── ae_comp__badge_upload_form.svelte # Bulk CSV upload
|
||||
└── [badge_id]/
|
||||
├── +page.svelte # Badge detail container
|
||||
├── +page.ts # Badge loader (non-blocking)
|
||||
└── ae_comp__badge_obj_view.svelte # Badge display + edit
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
**Badge List Page** (`+page.svelte`)
|
||||
- **LiveQuery:** Reactive badge list from IDB
|
||||
- **Search Pattern:** Debounced search with fast path + revalidation
|
||||
- **ID List:** `event_badge_id_li` drives LiveQuery
|
||||
- **Loading State:** Shows spinner when `search_status === 'loading'`
|
||||
|
||||
**Badge Search** (`ae_comp__badge_search.svelte`)
|
||||
- **Form Mode:** Toggle between search form and QR scanner
|
||||
- **Filters:** Badge type, print status, affiliations, sort order (trusted+ only)
|
||||
- **Fulltext:** Name/email search (all users)
|
||||
- **QR Scan:** Integrated QR scanner for badge ID lookup
|
||||
|
||||
**Badge List Display** (`ae_comp__badge_obj_li.svelte`)
|
||||
- **Visibility Filter:** Respects `hide` flag (trusted+ sees all)
|
||||
- **Display Logic:** Override → regular → fallback pattern
|
||||
- **Print Indicator:** Green checkmark badge shows `print_count`
|
||||
- **Metadata:** ID, created/updated timestamps (edit mode only)
|
||||
|
||||
**Badge Detail View** (`ae_comp__badge_obj_view.svelte`)
|
||||
- **Edit Mode:** Activated by `#review` URL hash or edit button
|
||||
- **Form Binding:** Direct `bind:value` on editable fields
|
||||
- **Dynamic Sizing:** Font size adjusts based on text length
|
||||
- **Print Preview:** Full badge layout with template
|
||||
- **Save Handler:** Only sends changed fields to API
|
||||
|
||||
---
|
||||
|
||||
## Testing Status
|
||||
|
||||
### Current Test Coverage
|
||||
- ❌ Badge list cold-start (failing — mock API issue)
|
||||
- ❌ Badge data integrity (failing — mock API issue)
|
||||
- ❌ Badge template list (failing — route mismatch)
|
||||
- ✅ Badge type filter tests
|
||||
- ✅ Badge template relationship tests
|
||||
- ✅ Electron bridge compatibility (graceful degradation)
|
||||
|
||||
### Test Issues
|
||||
**Root Cause:** Mock API routes not matching actual request patterns. Tests provide correct mock data but page shows "No badges found".
|
||||
|
||||
**Browser Error:** `Object is missing a valid ID for table "badge"` — Mock data reaching IDB with only `{tmp_sort_1, tmp_sort_2, event_id}`, all other fields stripped.
|
||||
|
||||
**Fix Required:** Debug actual API request URLs, adjust mock route patterns.
|
||||
|
||||
**Manual Testing Recommended:** Navigate to `/events/{event_id}/badges` in browser to verify actual functionality before fixing test infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## Known Issues & Future Enhancements
|
||||
|
||||
### Known Issues
|
||||
1. **Test Infrastructure:** Mock API routes not connecting to page requests
|
||||
2. **Session Cold-Start:** Potential race condition on first load (same as pres mgmt module)
|
||||
3. **Type Definitions:** Some TypeScript errors on external package types (pre-existing)
|
||||
|
||||
### Future Enhancements
|
||||
1. **Access-Based Edit Permissions:** Implement JSON config for field-level access control
|
||||
2. **Print Functionality:** Wire up print button to Electron native print or browser print API
|
||||
3. **Batch Operations:** Bulk update, bulk print, bulk export
|
||||
4. **Audit Log:** Track who edited which fields and when
|
||||
5. **Photo Badges:** Support badge photo upload and display
|
||||
6. **Custom Badge Layouts:** Dynamic template selection per badge type
|
||||
7. **Real-Time Sync:** WebSocket updates for multi-device badge printing stations
|
||||
8. **Approval Workflow:** Require manager approval for certain field changes
|
||||
|
||||
---
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Adding New Override Fields
|
||||
1. Add `{field}_override` to database schema
|
||||
2. Add to `properties_to_save` array in `ae_events__event_badge.ts`
|
||||
3. Update display logic to check override first
|
||||
4. Add to editable fields in `ae_comp__badge_obj_view.svelte`
|
||||
5. Update access control config
|
||||
6. Document in this file
|
||||
|
||||
### Testing Override Fields
|
||||
```typescript
|
||||
// Simulate external sync
|
||||
badge.given_name = "External Value"
|
||||
|
||||
// User edits
|
||||
badge.given_name_override = "User Value"
|
||||
|
||||
// Next sync (should NOT change override)
|
||||
badge.given_name = "Updated External Value"
|
||||
|
||||
// Display should still show "User Value"
|
||||
assert(display === badge.given_name_override)
|
||||
```
|
||||
|
||||
### Debugging Search Issues
|
||||
```typescript
|
||||
// Enable search logging
|
||||
log_lvl: 2
|
||||
|
||||
// Check search params object
|
||||
console.log('Search params:', search_params)
|
||||
|
||||
// Verify API request
|
||||
console.log('API request:', { event_id, fulltext_search_qry_str, type_code })
|
||||
|
||||
// Check returned IDs
|
||||
console.log('Badge IDs:', event_badge_id_li)
|
||||
|
||||
// Verify IDB contents
|
||||
db_events.badge.toArray().then(console.log)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
- [AE API V3 for Frontend](./GUIDE__AE_API_V3_for_Frontend.md)
|
||||
- [Development Guide](./GUIDE__Development.md)
|
||||
- [Events Launcher Native Integration](./PROJECT__AE_Events_Launcher_Native_integration.md)
|
||||
- [Naming Conventions](./AE__Naming_Conventions.md)
|
||||
|
||||
---
|
||||
|
||||
**Document Status:** ✅ Complete
|
||||
**Last Verified:** 2026-02-26
|
||||
**Verified Against:** Code commit b28595da (badge data fixes)
|
||||
Reference in New Issue
Block a user