Files
shiftcraft/app/CLAUDE.md
2026-04-17 23:26:01 +00:00

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.