Imported from wodore/wodore-frontend-quasar (
AGENTS.md). Install upstream withnpx skills add wodore/wodore-frontend-quasar. Copyright stays with the author.
Agent Guide
Quick reference when working on wodore-frontend-quasar (short wodore-frontend or wd-frontend).
Note: This file should be updated whenever important development information, patterns, or infrastructure details are discovered during work on the project.
Important Development Rules
DO NOT revert changes made by the user: When the user has explicitly configured values (constants, icons, styling, etc.), do NOT change them back to what you think they should be.
Specifications
Feature specifications and design guidelines are located in docs/specs/:
wd_design.md- Design system, colors, typography, componentswd_hut_search.md- Hut search feature specification- Other feature specs as they are added
Design source of truth
The authoritative design spec lives in the separate wodore-design repo
(/home/tobias/git/wodore/wodore-design/), maintained with the impeccable
skill. Two files matter:
DESIGN.md— design tokens (colors, type, spacing, motion), principles and component patterns. Covers BOTH lighting conditions (day/night themes).PRODUCT.md— product definition, personas and tone, owner-confirmed.
The frontend repo carries a read-only copy at src/assets/wodore-design/
(synced on design changes — never edit the copy, edit the source repo and
re-copy). When reviewing UI work, DESIGN.md is the reference the
implementation must match.
Essential Commands
Use yarn run command. Check package.json for details.
Development
# Install dependencies
yarn
# Generate assets (API client, icons, favicons)
yarn gen:api # OpenAPI client from backend
yarn gen:api-local # OpenAPI client from local backend
yarn gen:icons # Custom wd icons from SVG files
yarn gen:favs # Favicons from icongenie
# Development server (default: PWA mode on port 9000)
yarn dev # or yarn dev:pwa
yarn dev:spa # SPA mode
yarn dev:ssr # SSR mode
# Build for production
yarn build # or yarn build:pwa
yarn build:spa # SPA build
yarn build:ssr # SSR build
# Serve production build locally
yarn serve # or yarn serve:pwa
yarn serve:spa
yarn serve:ssr
# Code quality
yarn lint # Check code
yarn lint:fix # Fix linting issues
yarn format # Format with Prettier
# Component development (Histoire)
yarn story:dev # Start Histoire dev server
yarn story:build # Build static Histoire site
yarn story:preview # Preview built Histoire site
Testing
# Unit tests (Vitest) — run in CI on every PR, Allure report posted to the PR
yarn test:unit # run once
yarn test:unit:watch # watch mode
yarn test:unit:coverage # with coverage
# E2E smoke suite (Playwright) — LOCAL ONLY, never in CI
yarn dev # 1. start the dev server (required!)
yarn test:e2e # 2. run the suite (mobile-chrome project)
# Allure reports
yarn allure:generate # merge unit + e2e results, generate report
yarn allure:open # open the generated report in a browser
yarn allure:clean # remove all results and reports
Test structure: tests/unit/ (Vitest, node env; store specs use happy-dom via a
// @vitest-environment happy-dom docblock) and tests/e2e/ (Playwright).
Test structure: tests/unit/ (Vitest, node env; store specs use happy-dom via a
// @vitest-environment happy-dom docblock) and tests/e2e/ (Playwright).
Dev server ports & worktree testing: Quasar's PWA mode defaults to port 9200
when no port is passed — always pass one explicitly: yarn dev:pwa -p 9000. When
working in git worktrees (parallel branches/PRs), use ports 9001-9010 so multiple
dev servers can run side by side. Note:
.env.local is gitignored and does not propagate to new worktrees — copy it
from the main checkout, or API hosts / map keys will be missing. Also run
git submodule update --init in new worktrees — src/assets/wodore-design
(the map/overlay icon assets) is a submodule; without it the overlay and
map-picker icons 404.
Capacitor (Android on-device dev): yarn quasar dev -m capacitor -T android prompts
for the LAN IP, serves the dev server on it (port 9500), and opens Android Studio
(snap install — path set via bin.linuxAndroidStudio in quasar.config.ts); run the
app from the IDE onto a USB device (USB debugging enabled). The phone needs TCP access
to the dev machine — open firewall ports
(sudo ufw allow from 192.168.1.0/24 to any port <port> proto tcp) for 9500 (dev
server), 8000 (backend — start Django with runserver 0.0.0.0:8000, it binds
localhost-only by default), 8075 (tiles) and 8079 (imagor). ping working is
NOT enough — ufw allows ICMP but blocks TCP by default (symptom: black screen in the
app). Debug the WebView with
~/Android/Sdk/platform-tools/adb logcat -s Capacitor chromium Console or full
DevTools via chrome://inspect on desktop Chrome. App id: com.wodore.app
(src-capacitor/capacitor.config.json). Android variants (flavors std/stg × build types
debug/dev/release, all coexisting on one device): release std+release = com.wodore.app
("Wodore", Play Store, semver from tags via release.sh); CI staging preview stg+dev =
com.wodore.stg.dev ("Wodore Preview", assembleStgDev, built when a BUILD:android-labeled
PR merges); CI release candidate std+dev = com.wodore.app.dev ("Wodore RC", assembleStdDev,
production backend, built while a BUILD:android-labeled PR is still open and on version tags);
Android Studio std+debug = com.wodore.app.local ("Wodore Dev"). Dev/preview/RC versionNames
carry yyyyMMddHHmmss-commit (UTC timestamp + hash) instead of a semver guess. Launcher icons & splash screens are generated from the design
submodule via icongenie profiles (src/assets/icongenie/icongenie-capacitor-*.json), included in yarn gen:favs.
Local Android builds (no CI round-trip): scripts/build-android-local.sh stg --install
(preview variant) or std (RC variant). Requirements: a full JDK 21 — not the Android Studio snap JBR
(since the 2026 refresh it is Java 25, which Gradle 8.11 cannot load — 'Unsupported class file
major version 69' / lint 25.0.3 failures); install Temurin 21 to ~/jdks/ (script default
~/jdks/jdk-21.0.12.1+1). New worktrees need src-capacitor/android/local.properties
(sdk.dir=/home/tobias/Android/Sdk) and a yarn install inside src-capacitor. The script
stashes .env.local while baking the variant env (CI parity) and restores it after. CI signs
preview/RC builds with a stable dev keystore (WODORE_ANDROID_DEV_* secrets; local backup +
password in /home/tobias/git/wodore/wodore-ci-dev-keystore.txt) so devices update over
previous installs without uninstalling. Known env gaps: the staging API sends no CORS header
for the Capacitor origin (https://localhost) — preview builds stay data-less until the
backend allows it; api.wodore.com is IPv6-only, unreachable from IPv4 networks/emulators. Emulator testing:
avdmanager create avd -n wd-test -k "system-images;android-35;google_apis;x86_64" -d pixel_6,
headless boot emulator -avd wd-test -no-window -gpu swiftshader_indirect; MapLibre may freeze
the WebView render loop under software GL when the map style fails to load — prefer real devices
for map-state screenshots.
Note: OIDC (auth.burgdev.local.gd →
127.0.0.1) does not work on device — the hostname resolves to the phone itself.
Status reporting convention: when a dev server is running, always tell the
user where it is (full URL and which branch it serves). Always show active PRs
in the summary as markdown links (e.g. [PR #138](…/pull/138)).
E2E preconditions: dev server on http://localhost:9000 (yarn dev) and a reachable
backend (E2E_API_HOST, default http://127.0.0.1:8000). The hut deep-link test uses
E2E_HUT_SLUG (default aarbiwak) and skips when the hut is not found. E2E is
intentionally not part of CI (deterministic CI is handled by the unit suite).
CI: .github/workflows/test.yml runs the unit suite on every PR and posts the Allure
report via allure-framework/allure-action (same pattern as wodore-backend). Package
builds are label-gated on merged PRs: BUILD:docker triggers the Docker image build,
BUILD:android the Capacitor debug-APK build (.github/workflows/android.yml); both
also run on version tags and support manual dispatch.
IMPORTANT: Always run both yarn lint and npx vue-tsc --noEmit after making code changes to verify there are no ESLint warnings or TypeScript errors before committing.
CRITICAL: Check ESLint for all modified files to catch:
- Unused imports (e.g.,
Platformimported but never used) - Unused variables in catch blocks (use empty
catch {}for silent error handling) - Other code quality issues
Example ESLint check for specific files:
npx eslint src/stores/user-settings-store.ts src/stores/local-properties-store.ts
Related Projects
The Wodore ecosystem consists of multiple repositories:
- Frontend (this repository):
wodore-frontend-quasar/- Quasar/Vue.js frontend application - Backend:
../wodore-backend/- Django/Django-Ninja backend - Hut Services (Public):
../hut-services/- Public library for hut information schemas and base services - Hut Services (Private):
../hut-services-private/- Private implementations for external booking services (HRS, SAC, etc.)
All paths are relative to the repository root (wodore-frontend-quasar/).
Project Structure
wodore-frontend-quasar/
├── docs/
│ └── specs/ # Feature specifications and design docs
├── stories/ # Histoire component stories
│ ├── components/ # Component stories (mirrors src/components/)
│ └── README.md # Stories documentation
├── src/
│ ├── assets/ # Static assets (images, icons, etc.)
│ ├── boot/ # Quasar boot files (loaded before app starts)
│ ├── clients/ # Generated API clients (openapi-ts)
│ ├── components/ # Vue components
│ ├── composables/ # Vue composition functions
│ ├── css/ # Global styles
│ ├── extras/ # Extra resources
│ │ └── icons/ # Custom icon generation
│ ├── i18n/ # Internationalization
│ ├── layouts/ # Page layouts
│ ├── pages/ # Route pages
│ ├── router/ # Vue Router configuration
│ ├── services/ # Business logic and services
│ ├── stores/ # Pinia stores (state management)
│ ├── types/ # TypeScript type definitions
│ ├── histoire-setup.ts # Histoire configuration/setup
│ ├── App.vue # Root component
│ └── env.d.ts # Environment type definitions
├── src-pwa/ # PWA-specific files
├── scripts/ # Build and deployment scripts
├── docker/ # Docker-related files
├── .env # Environment variables (committed)
├── .env.local # Local overrides (gitignored)
├── .env.[dev|prod] # Environment-specific vars
├── histoire.config.ts # Histoire configuration
├── quasar.config.ts # Quasar framework configuration
├── Dockerfile # Multi-stage Docker build
└── package.json # Dependencies and scripts
API Documentation
OpenAPI schema available at:
- Local: http://localhost:8000/v1/openapi.json
- Production: https://hub.wodore.com (may not be up-to-date during development)
Generate TypeScript types from OpenAPI schema:
yarn gen:api # Production API
yarn gen:api-local # Local development API
Tech Stack
Core Framework
- Framework: Vue 3 with Quasar Framework
- Build Tool: Vite
- Language: TypeScript
- State Management: Pinia
- Routing: Vue Router
- i18n: Vue I18n
Specialized Agents Available:
- quasar agent (
.claude/agents/quasar.md) - Quasar components, styling, theming - vueuse agent (
.claude/agents/vueuse.md) - VueUse composables and utilities - iconify agent (
.claude/agents/iconify.md) - Icon selection and implementation - maplibre agent (
.claude/agents/maplibre.md) - MapLibre GL implementation - code-review agent (
.claude/agents/code-review.md) - Code review and best practices
Key Libraries
- API Client: openapi-fetch with auto-generated types
- Maps: MapLibre GL with vue-maplibre-gl
- Authentication: oidc-client-ts (Zitadel)
- Payments: Stripe with @vue-stripe/vue-stripe
- HTTP Client: Axios
- Utilities: @vueuse/core
Development Tools
- Package Manager: Yarn
- Linting: ESLint with TypeScript and Vue plugins
- Formatting: Prettier
- Component Development: Histoire - Component story/playground tool
- Icons:
- Quasar Icons (Material Icons, etc.)
- Custom
wdicons via Fantasticon - Iconify via unplugin-icons
- Favicons: Icongenie
Infrastructure
Docker Compose Services
Services are defined in ../wodore-backend/docker-compose.yml:
- Imagor (Image Processing)
Environment Variables
Environment files are loaded in this order (later files override earlier ones):
.env- Base configuration (committed to git).env.local- Local overrides (gitignored).env.[dev|prod]- Environment-specific (committed).env.local.[dev|prod]- Local environment overrides (gitignored)
Key Variables
See .env file for all available variables
Common Patterns
i18n / Language Switch
- Supported UI languages:
de,en,fr,it(seesrc/i18n/index.ts; German is the master message schema, English is the fallback locale). - First visit: the system/browser language is detected (
detectSystemLocale, English fallback) and persisted; a manually selected language always wins. The active locale is persisted in the user settings store (ui.language, localStoragewodore:userSettings) — never in the URL. src/services/locale.tsowns the vue-i18n instance:setLocale()switches vue-i18n + Quasar lang pack + persists the setting;currentLocale()is the reactive read.- Localized API calls pass
lang: currentLocale()(the backend supportslangon data endpoints with fallback). - Stores/components holding localized remote data watch
currentLocaleand refetch on language change. - User-visible strings belong in
src/i18n/locales/*.json; all four files must keep an identical key set (de.json is the master schema — vue-i18n typing flags drift).
Best Practices
IMPORTANT Development Guidelines:
-
Use VueUse composables whenever possible: The project uses
@vueuse/coreextensively. Before implementing manual solutions (timers, watchers, event listeners, etc.), check if VueUse provides a composable for that use case.- Examples:
useDebounceFn,useThrottleFn,useLocalStorage,useIntersectionObserver,useEventListener, etc. - See: https://vueuse.org/
- Examples:
-
Prefer Quasar components without manual modifications: Use Quasar's built-in components and props as much as possible. Avoid adding custom styles or HTML unless absolutely necessary for specific custom functionality.
- Quasar provides extensive theming and styling options through props and CSS variables
- Only add custom styles when implementing truly unique designs not covered by Quasar
-
Minimize custom styling: Keep custom CSS/SCSS to a minimum. Only add styles when:
- Implementing custom brand-specific designs
- Working with unique layouts not provided by Quasar
- Fine-tuning specific edge cases
Icons
The project uses a custom icon system based on wd prefixed icons.
When you need to find or add an icon, use the iconify agent (.claude/agents/iconify.md).
The iconify agent will:
- Search existing custom
wdicons first - Download and integrate new icons from Iconify if needed
- Verify icon licenses (MIT, Apache 2.0, CC0 only)
- Provide implementation guidance
Quick syntax reference:
<q-icon name="wd-add-outline" />
<!-- Custom wd icon (preferred) -->
<q-icon name="add" />
<!-- Quasar built-in -->
See .claude/agents/iconify.md for detailed workflow and usage examples.
CSS and Styling
Quasar Color System
Quasar provides a comprehensive color system. The project extends it in src/css/app.scss:
<!-- Use Quasar color classes -->
<div class="text-primary bg-secondary">...</div>
<div class="text-positive bg-negative">...</div>
<!-- Custom shade classes (100-900) -->
<div class="text-primary-700 bg-accent-100">...</div>
<div class="bg-white text-black">...</div>
<!-- Custom effects -->
<div class="text-primary--halo">Text with halo effect</div>
Components
Naming
Use PascalCase for component files and registration:
WdHutCard.vue
State Management (Pinia)
Stores are located in src/stores/:
// In a component
import { useAuthStore } from 'stores/auth-store';
import { useHutsStore } from 'stores/huts-store';
const authStore = useAuthStore();
const hutsStore = useHutsStore();
API Calls
Use the auto-generated OpenAPI client:
import createClient from 'openapi-fetch';
import type { paths } from 'clients/wodore_v1';
const client = createClient<paths>({ baseUrl: 'https://api.wodore.com' });
// Type-safe API calls
const { data, error } = await client.GET('/v1/huts/{id}', {
params: { path: { id: '123' } },
});
Routing
Router configuration in src/router/:
// Use router in components
import { useRouter } from 'vue-router';
const router = useRouter();
router.push({ name: 'hut-detail', params: { id: '123' } });
Troubleshooting
Common Issues
- API Types Not Updating: Run
yarn gen:api-localafter backend changes - Icons Not Showing: Run
yarn gen:iconsafter adding SVG files - Environment Variables Not Working: Check
quasar.config.tsenv section and restart dev server - Docker Build Fails: Ensure
GIT_HASHbuild arg is provided - Authentication Issues: Check OIDC configuration in
.env.local
