4.7 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 sibling layout shells (src/layouts/) sharing the same sidebar + topbar chrome:
AdminLayout— global/admin shell. Sidebar: Projects (/projects), Users (/users), Settings (/settings/*).MainLayout— project-workspace shell. Sidebar: Tasks / Documents / Members for the current:idproject; topbar has the project switcher.
App.vue hosts the global Toast, ConfirmDialog, and LoadingOverlay. LoadingOverlay is non-blocking (pointer-events: none) and driven by useLoading, a counter incremented/decremented by the axios interceptors.
Routes (src/router/index.ts):
/login— public./select-project— standalone, read-only project picker (no CRUD) for non-admin users./→AdminLayout:/projects(admin-only project list with full CRUD),/users,/settings/{masterdata,roles,permissions}./projects/:id→MainLayout:tasks/board,documents,members.
Role-based landing: auth.isAdmin (a user whose roleName contains Admin) lands on /projects; everyone else on /select-project. meta.adminOnly on /projects is enforced in the guard. Guards redirect unauthenticated users to /login; unknown paths redirect to the role landing page.
Domain areas (each is a folder under views/):
auth/—LoginView.vueprojects/—ProjectsListView.vue(admin),MembersView.vue,ProjectSelectView.vuedocuments/— tree + editortasks/— kanban board + detail dialogusers/,settings/— admin management
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
Aurapreset (customized insrc/theme.ts) 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 (AdminLayoutfor admin/top-level pages,MainLayoutfor 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.