3.9 KiB
CLAUDE.md
Vue 3 + TypeScript SPA — frontend of MWS (My Workspace), an internal tool for small teams to manage projects, documents, and tasks. Talks to a .NET 10 backend over REST + JWT.
Commands
npm install # install deps
npm run dev # vite dev server on :5173, calls backend at http://localhost:5000
npm run build # vue-tsc typecheck + vite build → dist/
npm run preview # serve built dist locally
No test runner, no linter configured. vue-tsc -b during build is the only static check.
Environment
VITE_API_BASE_URL— backend base URL (defaulthttp://localhost:5000). Set in.env.localfor dev, or pass--build-arg VITE_API_BASE_URL=...to Docker.- Backend dev port is 5000, not the production 8080. The
api.tsinterceptor readslocalStorage.mws_tokenand attachesAuthorization: Bearer …; 401s clear storage and redirect to/login.
Layout & routing
Two nested layouts (src/layouts/):
MainLayout— app shell with sidebar + topbar. Wraps all authenticated routes. Hosts the globalToastandConfirmDialog(inApp.vue).ProjectLayout— header + tabs (Overview / Documents / Tasks / Members). Loads the project by:idroute param on mount and renders<router-view />for the tab content.
Routes (src/router/index.ts) guard everything except /login via meta.public. Auth users hitting /login redirect to /projects. Unknown paths redirect to /projects.
Domain areas (each is a folder under views/):
auth/—LoginView.vueprojects/— list, overview, membersdocuments/— tree + editortasks/— list, kanban board, detail dialog
Service layer pattern
Three files in src/services/:
api.ts— shared axios instance + interceptors +errorMessage(error)helper for surfacing backend{ message }errors.backend.ts— auth, projects, members, users (small subset).modules.ts— documents + tasks (split here to keep files scannable).
Every API call is a thin typed wrapper around api.get/post/put/delete. Views call these directly — there is no separate data layer / composable cache. New endpoints → add a typed function to backend.ts or modules.ts and a matching interface in src/types/index.ts.
State
Only one Pinia store: src/stores/auth.ts. Token + user object, persisted to localStorage under mws_token / mws_user. Everything else (projects, documents, tasks) is fetched per-view — no client cache. Add a new store only if cross-view shared state actually appears.
Auto-imports
vite.config.ts auto-imports:
- Vue / vue-router / pinia globals
- PrimeVue composables:
useToast,useConfirm,useDialog(call them without imports in components) - PrimeVue components via
@primevue/auto-import-resolver(no need to import<Button>,<DataTable>, etc. —Components()resolves them)
Generated types live in src/auto-imports.d.ts and src/components.d.ts — do not hand-edit.
Styling
- Tailwind v4 via
@tailwindcss/viteplugin (notailwind.config.js— theme is CSS-only). - PrimeVue
Larapreset wired inmain.ts. Use PrimeVue components first; reach for raw HTML when PrimeVue has no equivalent. src/style.cssdefines a.fieldlabel helper used across forms.
Adding a new view / feature
- Add typed function(s) to
services/backend.tsorservices/modules.ts, types totypes/index.ts. - Create the
.vueunder the matchingviews/<area>/folder. - Register the route in
router/index.ts— pick the right layout (MainLayoutfor top-level,ProjectLayoutfor project-scoped tabs). - Use auto-imported PrimeVue components and composables — no manual
importfor them.
Container
Dockerfile is a multi-stage build (node:24-alpine → nginx:1.27-alpine). nginx.conf serves the SPA and proxies /api/ and /openapi/ to http://backend:8080. Production container listens on 80; docker-compose.yml maps host :5173 → 80.