Frontend
The frontend lives in the blitz-ui repo — a multi-app monorepo using Vite Module Federation.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Vue 3.5 (Composition API, <script setup>) |
| Language | TypeScript |
| UI Library | Vuetify 3.8 |
| Data Grid / Components | Syncfusion EJ2 (wrapped as Fin* components) |
| State Management | Pinia |
| Routing | Vue Router + unplugin-vue-router (file-based) |
| Build | Vite 7, Bun runtime |
| HTTP Client | $api2 (ofetch with automatic Bearer token auth) |
| Rich Text | TipTap editor |
| Module Federation | Vite Module Federation plugin |
Repository Structure
blitz-ui/src/
├── apps/
│ ├── ui/src/ # Main application shell (host)
│ │ ├── pages/ # File-based routes
│ │ │ ├── closehub/ # Close Hub module
│ │ │ ├── recon/ # Reconciliation module
│ │ │ ├── settings/ # Tenant admin (jobs, users, roles, periods, teams)
│ │ │ └── finhub/ # Finance hub
│ │ ├── stores/ # Pinia state
│ │ ├── composables/ # Vue composables
│ │ └── plugins/ # Vue plugins
│ └── closehub/src/ # Close Hub standalone app (Module Federation remote)
└── packages/
└── shared/src/ # Shared across apps
├── components/
│ └── fin-wrappers/ # FinDataGrid, FinExportPanel, etc.
├── navigation/ # Nav config
├── composables/
├── utils/ # $api2, $api2downloader, $dt
└── stores/ # Shared Pinia storesModule Federation Architecture
The frontend is split into a host app and 5 remote apps:
| App | Dev Port | Role |
|---|---|---|
ui | 10000 / 5173 | Main application shell (host) — loads remotes |
closehub | 5001 | Close Hub (remote) |
invoice | 5005 | Invoice management (remote) |
journal | 5003 | Journal entries (remote) |
recon | 5004 | Reconciliation (remote) |
collections | 5002 | Collections management (remote) |
Critical: Remote URLs are injected as VITE_*_REMOTE_URL environment variables and compiled into the bundle at build time. They cannot be changed at runtime. All 6 apps must be built and deployed in a coordinated pipeline with correct URLs.
Key Patterns
Shared Syncfusion Wrappers (fin-wrappers/)
Syncfusion components are wrapped with the Fin* prefix:
FinDataGrid.vue— Grid with selection, sorting, filtering, pagination, exportFinExportPanel.vue— Export dropdown (XLSX, CSV, PDF)
Always use these wrappers instead of raw Syncfusion components.
API Utility ($api2)
All API calls use $api2 (never raw fetch):
import { $api2 } from '@blitz/shared/utils/api2'
// Authenticated request — Bearer token injected automatically from cookies
const result = await $api2('/api/payments/filter', { method: 'POST', body: filter })
// File download with auth
const blob = await $api2downloader('/api/export/xlsx', { ... })File-Based Routing
Routes are defined by the file structure under pages/. unplugin-vue-router generates typed route definitions at build time (typed-router.d.ts — gitignored, never commit).
Navigation Config
Vertical nav and settings nav are defined in: packages/shared/src/navigation/vertical/settings/tenant-admin.ts
Development
cd blitz-ui/src
bun run devui # Start UI dev server at http://localhost:5173Build & Deploy
Images are built via GitHub Actions (build-push-ui.yml in blitz-ui) and pushed to GHCR. Railway deploys the latest image on trigger.
Git Workflow
mainis protected — all changes via PR- Never commit auto-generated files:
auto-imports.d.ts,components.d.ts,typed-router.d.ts