Files
mws.frontend.vue/CLAUDE.md
T
2026-08-24 08:07:23 +07:00

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 (default http://localhost:5000). Set in .env.local for dev, or pass --build-arg VITE_API_BASE_URL=... to Docker.
  • Backend dev port is 5000, not the production 8080. The api.ts interceptor reads localStorage.mws_token and attaches Authorization: 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 global Toast and ConfirmDialog (in App.vue).
  • ProjectLayout — header + tabs (Overview / Documents / Tasks / Members). Loads the project by :id route 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.vue
  • projects/ — list, overview, members
  • documents/ — tree + editor
  • tasks/ — 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/vite plugin (no tailwind.config.js — theme is CSS-only).
  • PrimeVue Lara preset wired in main.ts. Use PrimeVue components first; reach for raw HTML when PrimeVue has no equivalent.
  • src/style.css defines a .field label helper used across forms.

Adding a new view / feature

  1. Add typed function(s) to services/backend.ts or services/modules.ts, types to types/index.ts.
  2. Create the .vue under the matching views/<area>/ folder.
  3. Register the route in router/index.ts — pick the right layout (MainLayout for top-level, ProjectLayout for project-scoped tabs).
  4. Use auto-imported PrimeVue components and composables — no manual import for 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.