Architecture Guide
This guide covers the application architecture, design patterns, and organizational structure of Waldur HomePort.
Frontend Stack
- React with TypeScript for component development
- Vite for build tooling and development server
- Redux for legacy state management
- UI Router React for navigation (state-based routing)
- React Bootstrap (Bootstrap 5) for UI components
- React Final Form for modern form handling
- ECharts for data visualization
- Leaflet with React Leaflet for mapping
- TanStack React Query for server state management
Check Current Versions
Check all major dependencies
yarn list react typescript vite redux @uirouter/react react-bootstrap react-final-form echarts leaflet @tanstack/react-query
Check specific package version
yarn info
Key Architectural Patterns
Module Organization
The codebase follows a feature-based folder structure under src/:
- Each domain (customer, project, marketplace, etc.) has its own folder
- Components are co-located with their specific business logic
- Shared utilities are in
core/andtable/ - Legacy API interactions might use Redux patterns
State Management
Modern Patterns (Use for New Development)
- TanStack React Query: Server state management and caching for API calls
- Autonomous Table Filters: Redux-based state management for table filters using
@/tablecomponents (StringFilter, SelectFilter, etc.) - React Final Form: Local form state management (use for data entry forms, not table filters)
- Local Component State: useState and useReducer for UI state
- Custom Hooks: Reusable state logic and business operations
Legacy Patterns (Maintenance Only - Do Not Extend)
- Redux Store: Global state with dynamic reducer injection (legacy - avoid for new features, except for the Table Store)
- React Final Form for Filters: Old pattern of wrapping tables in
<Form>(legacy - migrate to autonomous filters)
Navigation & Routing
The application uses UI-Router for React with state-based routing. Routes are defined in module-specific routes.ts files.
Route Definition Structure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Dynamic Parameters (Preventing Full Reloads)
When a query parameter controls nested tabs or filters within a page, mark it as dynamic: true to prevent full component reloads:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Nested Tabs Pattern
For tabs within a page section that need URL synchronization:
- Add the parameter to the route URL with
dynamic: true:
1 2 3 4 5 6 7 | |
- Use router hooks in the component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Main Page Tabs (usePageTabsTransmitter)
For main page-level tabs, use the usePageTabsTransmitter hook which automatically handles URL synchronization:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Route Best Practices
- Use
dynamic: truefor any parameter that controls UI state within a page (subtabs, filters, panel states) - Keep routes hierarchical - child routes inherit parent's URL prefix
- Use abstract routes for shared layouts and data fetching
- Lazy load components with
lazyComponent()for code splitting - Define query params in URL - e.g.,
url: 'page/?tab&filter'makes params explicit
Data Fetching
Modern Approach (Use for New Development)
- TanStack React Query: Preferred for server state management and caching
- Custom Hooks: Reusable data fetching logic with React Query
- Waldur JS Client: TypeScript API client integration
- Automatic Caching: 5-minute stale time with background refetching
Legacy Approach (Maintenance Only)
- Redux Actions: Centralized API calls (legacy - use React Query instead)
- Table Store: Standardized data loading patterns (legacy pattern)
- Periodic Polling: Real-time updates via legacy polling (use React Query polling instead)
Component Architecture
- Container Components: Handle data fetching and state management
- Presentation Components: Pure UI components with props
- Form Components: Specialized forms using React Final Form
- Table Components: Reusable table infrastructure with filtering, sorting, pagination
- Button Components: Unified button system built on Tailwind/shadcn for consistent UX
Button Component Architecture
The application uses a unified button system to ensure consistent styling, behavior, and accessibility. Direct Bootstrap Button imports and raw Bootstrap button markup are forbidden or deprecated — use BaseButton (waldur-ui) or the appropriate specialized wrapper component instead.
BaseButton (waldur-ui) is a from-scratch Tailwind/shadcn component, not a Bootstrap wrapper. It natively provides tooltips (with tooltipSide), loading states (pending), icon-only layout/padding resets, responsive sizing (sm, md, lg), and design token variants. The specialized wrappers in src/ (SubmitButton, CloseDialogButton, SaveButton, etc.) compose BaseButton directly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Button Selection Guide
| Use Case | Component |
|---|---|
| Form submit | SubmitButton (size="lg" default, size="sm" for compact) |
| Table row action | BaseButton (size="lg") |
| Inline action (small) | BaseButton (size="sm") |
| Modal cancel/close | CloseDialogButton |
| Icon-only button with tooltip | BaseButton (iconNode, tooltip) |
| Table toolbar (refresh, export, filter) | BaseButton (variant="tertiary", size="lg") |
| Edit field in settings row | CompactEditButton |
| Edit in card header | BaseButton (iconNode, label) |
| Create with dialog | CreateModalButton |
| Delete with confirmation | DeleteButton |
ESLint Enforcement
Two mechanisms cover the two ways Bootstrap button styling reaches the tree.
A plain no-restricted-imports entry (error, eslint.config.js's
RESTRICTED_IMPORTS array — the same mechanism already used to ban
Badge/Tooltip/OverlayTrigger/Popover/Alert from react-bootstrap)
catches direct imports — import { Button } from 'react-bootstrap' — for
both Button and DropdownButton. It replaced two near-identical custom
rules (no-direct-bootstrap-button, no-direct-bootstrap-dropdown-button)
once their ALLOWED_FILES lists had shrunk to nothing: the migration's
closing pass converted every remaining real <Button>/<DropdownButton>
usage in the app, leaving zero call sites for either. A genuinely new
exception belongs on the offending line as a standard
// eslint-disable-next-line no-restricted-imports -- <reason>.
waldur-custom/no-bootstrap-button-markup (error) catches the hand-rolled
form — <button className="btn btn-danger"> — which carries no import and so was
invisible to the import rule. The tree has no remaining instances and the rule
has no allowlist: Link builds its button classes from buttonVariants() via
its buttonVariant prop, and LoginButton and AssistantComposer's Send button
are plain BaseButtons (BaseButton is forwardRef, so it works under
assistant-ui's asChild).
src/table/ActionsDropdown.tsx's row-action dropdown
toggles were the last remaining hand-rolled usage in the shared component
layer (as opposed to the scattered app-level instances above) — Phase F3
converted their toggle buttons onto buttonVariants() from waldur-ui, so
they no longer trigger this rule at all. The menu panel and rows have since
moved off Bootstrap's .dropdown-* classes too, onto waldur-ui's Menu in
the actions look (ActionsMenu).
Key Directories
src/core/- Shared utilities, API clients, and base componentssrc/table/- Reusable table components and data managementsrc/form/- Form components and field implementationssrc/marketplace/- Service marketplace and offering management (largest module)src/customer/- Organization management and billingsrc/project/- Project management and resourcessrc/auth/- Authentication and identity provider integrationsrc/administration/- Admin panel functionalitysrc/booking/- Resource booking systemsrc/broadcasts/- System announcementssrc/dashboard/- Dashboard componentssrc/navigation/- Navigation and layout componentssrc/proposals/- Proposal managementsrc/quotas/- Resource quotas managementsrc/theme/- Theme management (dark/light mode)src/user/- User managementsrc/metronic/- UI framework integration
Backend Integration
Integrates with Waldur MasterMind REST API requiring CORS configuration on the backend for local development.
API Client
- Waldur JS Client - Custom API client for Waldur MasterMind
- Auto-generated client with TypeScript support
- Request/response interceptors for authentication and error handling
- Token-based authentication with auto-refresh capabilities
Version Management
Check current version
grep "waldur-js-client" package.json
Check latest available version
yarn info waldur-js-client version
Update to latest version in package.json, then install
yarn install
Build System & Performance
Modern Build Configuration
- Vite 7.0 with ES modules support
- Node.js v23.7.0 (latest LTS) compatibility
- Code splitting with lazy loading for all major features
- Optimized bundle sizes and asset processing
- Source maps for development and production debugging
Performance Optimizations
- Lazy component loading with
lazyComponentutility - Dynamic reducer injection for Redux store
- Automatic code splitting by route and feature
- Optimized asset loading (images, fonts, SVG)
- Bundle analysis and optimization tools
Cold-start budget
Everything statically reachable from src/index.tsx is downloaded before the
first paint, so the entry graph is kept deliberately small:
public/boot-redirect.jsruns before the module preloads and sends anonymous visitors of/and/login/to/api-auth/default/init/, where the backend resolvesDEFAULT_IDPitself. It probes that endpoint withprobe=1first and navigates only on the204a probe-aware backend answers, so a backend without a default provider, without the route, or unreachable leaves the visitor on the application's own login page. Whether a session exists is decided from web storage in the script, since only the browser can see it. The script publishes its decision aswindow.waldurBootRedirect, andsrc/index.tsxdoes not render while the visitor is being sent away: Firefox cancels the page's in-flight requests when a navigation starts, and an app booting alongside the redirect would see its lazy chunks fail and reload, cancelling the redirect (src/core/bootGuard.ts).yarn build:check(scripts/bundle-budget.mjs, run by theCheck cold-path budgetCI job on every MR) fails when the entry script plus itsmodulepreloads exceed the gzip budget, or when echarts, matrix-js-sdk, livekit-client, monaco-editor, @mdxeditor/editor or mermaid appear in that set. Each of those is loaded lazily on purpose:reporting/screens.tskeeps the route flags chart-free,MatrixRootlazy-loadsMatrixProviders,MatrixCallHostlazy-loads the call view, the@/formbarrel lazy-loadsMonacoGroup/MarkdownGroup, and the assistant registry lazy-loads the mermaid block.docker/nginx-tpl.confservesassets/as immutable for a year with build-time precompressed gzip;index.htmlis never cached.
Asset Management
- SVG files processed through SVGR 8.1.0 plugin for React components
- Images and static assets in
src/images/ - Font files managed through Vite's asset pipeline
- Markdown content processed through vite-plugin-markdown
- Monaco Editor 0.52.2 for code editing capabilities
- Sass 1.85.0 for SCSS preprocessing
Environment Variables
VITE_API_URL- Backend API endpoint (defaults to http://localhost:8000/)
Project Overview
Waldur HomePort is a React-based web frontend for the Waldur MasterMind cloud orchestrator. It's a TypeScript application built with Vite that provides a comprehensive management interface for cloud resources, organizations, projects, and marketplace offerings.