Skip to content

Frontend

The frontend lives in the blitz-ui repo — a multi-app monorepo using Vite Module Federation.

Tech Stack

LayerTechnology
FrameworkVue 3.5 (Composition API, <script setup>)
LanguageTypeScript
UI LibraryVuetify 3.8
Data Grid / ComponentsSyncfusion EJ2 (wrapped as Fin* components)
State ManagementPinia
RoutingVue Router + unplugin-vue-router (file-based)
BuildVite 7, Bun runtime
HTTP Client$api2 (ofetch with automatic Bearer token auth)
Rich TextTipTap editor
Module FederationVite 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 stores

Module Federation Architecture

The frontend is split into a host app and 5 remote apps:

AppDev PortRole
ui10000 / 5173Main application shell (host) — loads remotes
closehub5001Close Hub (remote)
invoice5005Invoice management (remote)
journal5003Journal entries (remote)
recon5004Reconciliation (remote)
collections5002Collections 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, export
  • FinExportPanel.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):

typescript
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).

Vertical nav and settings nav are defined in: packages/shared/src/navigation/vertical/settings/tenant-admin.ts

Development

bash
cd blitz-ui/src
bun run devui     # Start UI dev server at http://localhost:5173

Build & 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

  • main is protected — all changes via PR
  • Never commit auto-generated files: auto-imports.d.ts, components.d.ts, typed-router.d.ts