Operations Dashboard
{alerts.length} active alerts
)
}
```
### Page Config
| Field | Type | Default | Description |
| ------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `name` | string | filename | Page identifier and URL slug |
| `description` | string | — | Shown in the page header and sidebar tooltip |
| `stores` | `string[]` | — | Stores this page reads from. Shown in the data source bar with links. |
| `automations` | `string[]` | — | Automations that populate the page data. Shown in the data source bar with status, toggle, and run button. |
| `icon` | string | — | Lucide icon name (e.g., `'shield'`, `'monitor'`, `'bar-chart'`) |
| `hidden` | boolean | `false` | If true, excluded from sidebar |
When `stores` or `automations` are declared, the runtime automatically shows a **data source bar** above the page with store links, automation live/paused toggle, schedule, and a run button. This is a platform feature — no code required in the page itself.
### SDK Hooks
Pages use hooks from `@amodalai/react` to access agent data:
| Hook | Description |
| -------------------------------- | ---------------------------------------------------------------- |
| `useStoreList(store, options)` | Fetch multiple documents with filtering, sorting, and pagination |
| `useStore(store, key)` | Fetch a single document by key |
| `useSkillAction(skill, options)` | Invoke a skill from the page |
#### useStoreList
```tsx
const { data, loading, error, refresh } = useStoreList('classified-alerts', {
filter: { severity: 'P1' },
sort: { field: 'timestamp', order: 'desc' },
limit: 50,
refreshInterval: 10000, // auto-refresh every 10s
})
```
#### useStore
```tsx
const { data: alert } = useStore('classified-alerts', alertId)
```
#### useSkillAction
```tsx
const { invoke, loading } = useSkillAction('triage')
const handleTriage = () => {
invoke({ query: 'Triage the latest alerts' })
}
```
### Sidebar Integration
Pages appear in the runtime app sidebar under a **Pages** section. The name is auto-formatted from the filename (`ops-dashboard` → "Ops Dashboard"). Click a page to navigate to `/pages/{pageName}`.
Hidden pages (`hidden: true`) are excluded from the sidebar but still accessible via direct URL — useful for detail pages navigated to from other pages.
### Context Pages
Pages with `context` params are detail views that receive route parameters:
```tsx
export const page = {
name: 'deal-detail',
icon: 'file-text',
context: { dealId: 'string' },
hidden: true, // navigated to, not shown in sidebar
}
export default function DealDetail({ dealId }: { dealId: string }) {
const { data: deal } = useStore('deals', dealId)
// ...
}
```
### Entity Pages vs. Composed Pages
* **Entity pages** are auto-generated from store definitions — list and detail views come for free without writing page files
* **Composed pages** are what you define in `pages/` — custom views that combine data from multiple stores with custom layout and logic
Only composed pages need explicit page files. If you just need a list/detail view of a single store, the runtime generates that automatically.
### Hot Reload
During `amodal dev`, changes to page files trigger hot module replacement (HMR) via the Vite plugin. Edit a page and see changes instantly in the browser.
### Example: Surveillance Dashboard
```tsx
import { useStoreList } from '@amodalai/react'
export const page = {
name: 'ops-dashboard',
icon: 'shield',
description: 'Facility surveillance overview',
}
export default function OpsDashboard() {
const { data: alerts } = useStoreList('classified-alerts', {
sort: { field: 'confidence', order: 'desc' },
limit: 10,
})
const { data: zones } = useStoreList('zone-status')
const { data: devices } = useStoreList('device-profiles')
return (