62 lines
2.6 KiB
Markdown
62 lines
2.6 KiB
Markdown
# CLAUDE.md — app/
|
|
|
|
## Nuxt Frontend (SPA Mode)
|
|
|
|
This is a **client-side only** Nuxt 4 app (`ssr: false`). No server routes, no `server/` directory. All data comes from PocketBase via its SDK.
|
|
|
|
## PocketBase Client Access
|
|
|
|
```ts
|
|
const { pb, authStore } = usePocketBase()
|
|
```
|
|
|
|
- `pb` — PocketBase SDK instance (configured in `plugins/pocketbase.ts`)
|
|
- `authStore` — reactive Vue ref wrapping PocketBase's auth state (`.isValid`, `.record`, `.token`)
|
|
|
|
**Never instantiate PocketBase directly.** Always use the `usePocketBase()` composable.
|
|
|
|
## Stores (Pinia)
|
|
|
|
All backend interaction goes through Pinia stores in `stores/`:
|
|
|
|
| Store | Collection | Key patterns |
|
|
|-------|-----------|--------------|
|
|
| `user.ts` | `users` | Auth flows (OTP, OAuth), profile CRUD, `isAuthenticated` getter |
|
|
| `counter.ts` | `counters` | Realtime subscription, increment/reset |
|
|
| `notifications.ts` | `notifications` + `fcm_tokens` | Realtime subscription, FCM token registration |
|
|
| `avatar.ts` | `users` (file field) | Avatar URL getter, file upload |
|
|
|
|
**Realtime pattern:** Stores call `pb.collection('x').subscribe('*', callback)` in a `subscribeToChanges()` action. Components call this on mount and `unsubscribe()` on unmount.
|
|
|
|
## Routing & Middleware
|
|
|
|
Pages use Nuxt file-based routing. Two middleware:
|
|
|
|
- `middleware/auth.ts` — route-level guard, add via `definePageMeta({ middleware: 'auth' })`
|
|
- `middleware/00.fetchUser.global.ts` — runs on every navigation, refreshes auth token
|
|
|
|
Two layouts: `default` (with AppHeader) and `empty` (login/confirm pages).
|
|
|
|
## Components
|
|
|
|
Organized by feature domain: `App/` (header, notifications), `Counter/` (widget), `Profile/` (avatar, upload, lang select). Components use Nuxt UI v3 primitives (`UButton`, `UCard`, `UForm`, `UModal`, etc.).
|
|
|
|
## Styling
|
|
|
|
Tailwind CSS v4 + Nuxt UI. Theme colors set in `app.config.ts` (primary: teal, neutral: slate). Custom font: Public Sans (defined in `assets/css/main.css`).
|
|
|
|
## i18n
|
|
|
|
Strategy: `no_prefix` — URLs are not prefixed with locale. Two locales: `en`, `de`. Translation files in `i18n/locales/`. Use `$t('key')` in templates or `const { t } = useI18n()` in setup.
|
|
|
|
`LanguageCode` type defined in `types/i18n.types.ts`.
|
|
|
|
## Types
|
|
|
|
- `types/pocketbase.types.ts` — **auto-generated**, do not edit. Contains typed collection names (`Collections` enum) and record interfaces. Regenerate with `pnpm pocketbase:types`.
|
|
- `types/i18n.types.ts` — manual type for supported language codes.
|
|
|
|
## Mobile (Capacitor)
|
|
|
|
`app.vue` handles Capacitor-specific setup: deep link listener (`App.addListener('appUrlOpen')`), push notification registration, and safe-area CSS. Check `Capacitor.isNativePlatform()` before using native APIs.
|