# Widget Component Pattern

## Purpose
Provide an extensible, consistent way to add new digital signage widgets (Queue, Weather, Social, QR, etc.) without requiring changes across the display player, admin UI, and API for every new widget type.

## Core Ideas
- **Registry-based discovery**: widget types are registered once and exposed via `/api/signage/widget-types`.
- **Config-first**: each widget instance stores JSON config and validates it against a per-widget schema.
- **Separation of concerns**:
  - PHP services/entities handle config validation and render-data assembly.
  - Twig templates handle markup/visual structure for each widget type.
  - Player JS handles client-side behavior (rotation, media playback, Ably updates, offline cache).

## Directory Conventions
- PHP domain + services: `userfrosting/src/BuyerKiosk/DigitalSign/`
  - Registry: `Services/WidgetRegistry.php`
  - Widget types: `Domain/Widget/*Widget.php`
- Twig templates:
  - Widget templates: `userfrosting/templates/themes/default/ds/widgets/*.html`
  - Layout templates: `userfrosting/templates/themes/default/ds/layouts/*.html`
- Player JS:
  - Core player: `public_html/js/digitalsign/player/*`
  - Widget JS components: `public_html/js/digitalsign/player/widgets/*`

## Widget Interface (Conceptual)
Every widget type implements the same conceptual interface:
- `static getType(): string` (e.g., `queue-current`, `weather-simple`)
- `static getDisplayName(): string`
- `static getDescription(): string`
- `static getIcon(): string` (admin picker)
- `static getConfigSchema(): array` (JSON Schema)
- `validate(config): void` (throws on invalid config)
- `getRenderData(context): array` (returns render-ready data; must fail gracefully)

Where `context` commonly includes:
- `typeNum`, `zoneId`, `pageId`, `deviceId`
- request time (for scheduling)
- cache/version info (for offline strategy)

## Registry Responsibilities
The registry is the single source of truth for:
- Supported widget types and their metadata (name, icon, description)
- Config schema (for admin forms + server-side validation)
- Zone/layout compatibility rules (optional, if enforced server-side)

## Error Handling Contract
Widget rendering must be non-fatal for the display:
- On external data failure: return cached data when available; otherwise return a minimal placeholder state.
- On invalid config: return `{success:false, error:'invalid_config'}` and render a safe placeholder.
- Never throw uncaught exceptions during display rendering.

## DB Shape (High-Level)
- Widget instances live in `dsPageWidgets` with:
  - `widgetType` (registry key)
  - `config` (JSON blob)
  - `(pageId, layoutZoneId)` unique constraint (one widget per zone in v1)

## Adding a New Widget (Checklist)
1. Add a widget class under `Domain/Widget/`
2. Register it in `WidgetRegistry`
3. Add Twig template under `templates/themes/default/ds/widgets/`
4. Add admin configurator schema/form mapping (based on `getConfigSchema()`)
5. (Optional) Add player JS component if the widget needs client-side behavior
6. Add tests: schema validation, render-data error handling, compatibility checks
