Consolidate Reports and Data Entry apps into a single Vue.js SPA with: Architecture: - Module-based structure with lazy-loaded routes (@reports, @data-entry) - Error boundaries per module to prevent cascade failures - Dual API proxy in Vite for microservices (reports:8001, data-entry:8003) - Pinia store factories for shared auth, company, and period stores - Vite path aliases for clear module boundaries (@shared, @reports, @data-entry) Service Management: - Granular service control scripts (backend-reports.sh, backend-data-entry.sh, bot.sh, frontend.sh) - 87% faster frontend restart: 7s vs 53s full restart - 38% faster full startup: 33s vs 53s via parallel backend initialization - Enhanced start-dev.sh with proper service timeouts (OCR: 30s, Vite: 15s, Bot: 10s) - status.sh for comprehensive health checks Features: - Auto-select first company on login with period auto-load - Hamburger menu with feature toggle support - JWT token auto-injection via axios interceptors - Unified header with company/period selectors - IIS web.config for production deployment with multi-API routing UX Improvements: - Vue watchers for reactive company/period loading - Lazy store initialization with graceful error handling - Period persistence per user+company in localStorage - Feature flags for optional modules Deployment: - Single IIS site serves unified frontend with API proxy rules - Maintains separate backend processes for microservices - Windows line ending fixes (.env CRLF → LF conversion) Stats: 112 files changed, 38,342 insertions(+), 2,342 deletions(-) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
35 KiB
Implementation Plan: unified-app
Overview
This plan consolidates the Reports App and Data Entry App frontends into a single unified SPA. The implementation follows a pragmatic monolith approach with module isolation through error boundaries and lazy loading.
Key principles:
- Single build, single IIS site deployment
- Module isolation via error boundaries (errors in one module don't crash the other)
- Lazy loading for each module (only load what the user navigates to)
- Shared components from
src/shared/(auth, companies, period selectors) - CSS system from reports-app preserved and enhanced
Worktree location: /mnt/e/proiecte/ab-worktrees/roa2web-unified-app
Phase 1: Project Setup (0.5 days)
Task 1: Create Root Directory Structure
Objective: Set up the foundational directory structure for the unified app at the repository root.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/config/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/router/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/assets/(create directory)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/public/(create directory)
Description: Create the following directory structure at the repository root (not inside reports-app or data-entry-app):
src/
modules/
reports/
views/
stores/
services/
data-entry/
views/
receipts/
components/
ocr/
stores/
services/
shared/
components/
layout/
stores/
styles/
layout/
config/
router/
assets/
css/
core/
components/
patterns/
layout/
utilities/
vendor/
public/
Dependencies: None
Completion Criteria:
- All directories created
- Directory structure matches specification
Task 2: Create package.json with Merged Dependencies
Objective: Create unified package.json combining dependencies from both apps.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/package.json(create)
Description:
Merge dependencies from both reports-app/frontend/package.json and data-entry-app/frontend/package.json:
Dependencies to include:
vue: ^3.4.0vue-router: ^4.2.5pinia: ^2.1.7axios: ^1.6.5 (use higher version)primevue: ^3.48.0 (use higher version)primeicons: ^6.0.1chart.js: ^4.5.0 (reports only)vue-chartjs: ^5.3.2 (reports only)date-fns: ^2.30.0jspdf: ^3.0.1jspdf-autotable: ^5.0.2xlsx: ^0.18.5qrcode.vue: ^3.6.0
DevDependencies:
@vitejs/plugin-vue: ^5.0.0vite: ^5.0.10@playwright/test: ^1.54.2eslint: ^8.56.0eslint-plugin-vue: ^9.20.0prettier: ^3.1.1
Scripts:
dev: vitebuild: vite buildpreview: vite previewlint: eslint src/ --ext .vue,.js --fixtest:e2e: playwright test
Dependencies: Task 1
Completion Criteria:
- package.json created with all dependencies
- Scripts defined correctly
npm installsucceeds
Task 3: Create vite.config.js with Dual Proxy and Lazy Loading
Objective: Configure Vite for unified app with proxies to both backends.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/vite.config.js(create)
Description: Create Vite configuration with:
-
Dual proxy configuration:
/api/reports/*->http://localhost:8001/api/*/api/data-entry/*->http://localhost:8003/api/*/uploads->http://localhost:8003
-
Aliases:
@->./src@shared->./src/shared@reports->./src/modules/reports@data-entry->./src/modules/data-entry
-
Build configuration:
base:/(single site, no subdirectory)- Manual chunks for vendors (vue, primevue, charts, exports)
- Source maps enabled
- Cache busting with hashes
-
dedupe for Vue, vue-router, pinia, primevue
-
WSL2 file watching (usePolling: true)
Reference: reports-app/frontend/vite.config.js for htmlTimestampPlugin and build settings.
Dependencies: Task 1
Completion Criteria:
- Vite config created with dual proxy
- All aliases defined
- Manual chunks configured
npm run devstarts successfully
Task 4: Copy CSS System from Reports App
Objective: Migrate the complete CSS design system to the unified app.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/assets/css/(copy entire directory)
Description:
Copy the entire CSS directory from reports-app/frontend/src/assets/css/ to src/assets/css/:
core/- Design tokens (tokens.css)components/- Component patterns (cards.css, badges.css, stats.css, etc.)patterns/- Interactive patternslayout/- Page structureutilities/- Utility classesvendor/- PrimeVue overridesmain.css- Main import fileglobal.css- Global stylesmobile.css- Mobile responsive styles
This is the authoritative CSS system that both modules will use.
Dependencies: Task 1
Completion Criteria:
- All CSS files copied
- Directory structure preserved
main.cssimports all other CSS files correctly
Task 5: Copy Shared Frontend Components and Stores
Objective: Migrate shared components to unified app's shared directory.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/LoginView.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/CompanySelector.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/PeriodSelector.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/layout/AppHeader.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/layout/SlideMenu.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/stores/auth.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/stores/companies.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/stores/accountingPeriod.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/styles/login.css(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/styles/layout/header.css(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/styles/layout/navigation.css(copy)
Description:
Copy from shared/frontend/:
- Components:
LoginView.vue,CompanySelector.vue,PeriodSelector.vue,layout/AppHeader.vue,layout/SlideMenu.vue - Stores:
auth.js,companies.js,accountingPeriod.js - Styles:
login.css,layout/header.css,layout/navigation.css
Update import paths in components to use relative paths within src/shared/.
Dependencies: Task 1
Completion Criteria:
- All shared components copied
- All shared stores copied
- All shared styles copied
- Import paths updated to work from new location
Task 6: Create .env.example and public/index.html
Objective: Create environment template and HTML entry point.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/.env.example(create)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/public/index.html(create)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/public/favicon.ico(copy from reports-app if exists)
Description:
- Create
.env.example:
# API URLs (development)
VITE_REPORTS_API_URL=http://localhost:8001/api
VITE_DATA_ENTRY_API_URL=http://localhost:8003/api
# Feature flags
VITE_FEATURE_REPORTS=true
VITE_FEATURE_DATA_ENTRY=true
- Create
public/index.html(or use Vite's default index.html in root):
<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ROA2WEB - Unified App</title>
<link rel="icon" href="/favicon.ico" />
<!-- BUILD_TIMESTAMP placeholder for cache busting -->
<meta name="build-time" content="BUILD_TIMESTAMP" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
Dependencies: Task 1
Completion Criteria:
- .env.example created
- index.html created with proper meta tags
- Build timestamp placeholder present
Phase 2: Module Migration (1 day)
Task 7: Migrate Reports Module Views
Objective: Copy and adapt Reports views to the modules directory.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/views/DashboardView.vue(copy and adapt)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/views/InvoicesView.vue(copy and adapt)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/views/BankCashRegisterView.vue(copy and adapt)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/views/TrialBalanceView.vue(copy and adapt)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/views/TelegramView.vue(copy and adapt)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/views/CacheStatsView.vue(copy and adapt)
Description:
Copy views from reports-app/frontend/src/views/ to src/modules/reports/views/:
- DashboardView.vue
- InvoicesView.vue
- BankCashRegisterView.vue
- TrialBalanceView.vue
- TelegramView.vue
- CacheStatsView.vue
DO NOT copy LoginView.vue - use shared LoginView.
Update imports:
- Change
@/stores/xxxto@reports/stores/xxxfor module-specific stores - Change
@/stores/authto@shared/stores/auth - Change
@/stores/companiesto@shared/stores/companies - Change
@/stores/accountingPeriodto@shared/stores/accountingPeriod
Dependencies: Task 4, Task 5
Completion Criteria:
- All 6 views copied
- Import paths updated
- No references to old shared path (
../../../shared/)
Task 8: Migrate Reports Module Stores
Objective: Copy and adapt Reports stores to the modules directory.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/stores/dashboard.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/stores/invoices.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/stores/treasury.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/stores/trialBalance.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/stores/cacheStore.js(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/stores/index.js(copy and adapt)
Description:
Copy module-specific stores from reports-app/frontend/src/stores/:
- dashboard.js
- invoices.js
- treasury.js
- trialBalance.js
- cacheStore.js
- index.js (barrel export)
DO NOT copy auth.js, companies.js, accountingPeriod.js - these are in shared.
Update any internal imports to use module paths.
Dependencies: Task 5
Completion Criteria:
- All 6 store files copied
- No references to shared stores (auth, companies, period)
- index.js exports all module stores
Task 9: Create Reports Module API Service
Objective: Create API service for Reports module with /api/reports/ prefix.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/services/api.js(create/adapt)
Description:
Create or adapt the API service from reports-app/frontend/src/services/api.js:
- Base URL:
/api/reports(proxied to port 8001) - Include auth token interceptor
- Include response error handler
- Export configured axios instance
Example structure:
import axios from 'axios'
const api = axios.create({
baseURL: '/api/reports',
headers: { 'Content-Type': 'application/json' }
})
// Request interceptor for auth token
api.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
export default api
Dependencies: Task 1
Completion Criteria:
- API service created with
/api/reportsbase URL - Auth token interceptor configured
- Error handling interceptor configured
Task 10: Migrate Data Entry Module Views
Objective: Copy and adapt Data Entry views to the modules directory.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/views/receipts/ReceiptsListView.vue(copy and adapt)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/views/receipts/ReceiptCreateView.vue(copy and adapt)
Description:
Copy views from data-entry-app/frontend/src/views/receipts/:
- ReceiptsListView.vue
- ReceiptCreateView.vue
DO NOT copy LoginView.vue - use shared LoginView.
Update imports:
- Change
@/stores/xxxto@data-entry/stores/xxxfor module-specific stores - Change
@/stores/authto@shared/stores/auth - Change
@/stores/companiesto@shared/stores/companies - Change
@/stores/accountingPeriodto@shared/stores/accountingPeriod - Change
@/components/xxxto@data-entry/components/xxx - Change
@/services/apito@data-entry/services/api
Dependencies: Task 4, Task 5
Completion Criteria:
- Both receipt views copied
- Import paths updated
- No references to old shared path
Task 11: Migrate Data Entry Module Components
Objective: Copy Data Entry specific components (OCR).
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/components/ocr/OCRUploadZone.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/components/ocr/OCRPreview.vue(copy)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/components/ocr/OCRConfidenceIndicator.vue(copy)
Description:
Copy OCR components from data-entry-app/frontend/src/components/ocr/:
- OCRUploadZone.vue
- OCRPreview.vue
- OCRConfidenceIndicator.vue
Update any imports to use the new module paths.
Dependencies: Task 1
Completion Criteria:
- All 3 OCR components copied
- Import paths updated
- Components work independently
Task 12: Migrate Data Entry Module Stores
Objective: Copy Data Entry specific stores.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/stores/receiptsStore.js(copy and adapt)
Description:
Copy from data-entry-app/frontend/src/stores/:
- receiptsStore.js
DO NOT copy auth.js, companies.js, accountingPeriod.js - these are in shared.
Update imports:
- API service import to
@data-entry/services/api
Dependencies: Task 5
Completion Criteria:
- receiptsStore.js copied
- Import paths updated
- No duplicate shared stores
Task 13: Create Data Entry Module API Service
Objective: Create API service for Data Entry module with /api/data-entry/ prefix.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/services/api.js(create/adapt)
Description:
Create or adapt the API service from data-entry-app/frontend/src/services/api.js:
- Base URL:
/api/data-entry(proxied to port 8003) - Include auth token interceptor
- Include
X-Selected-Companyheader injection - Include response error handler
- Export configured axios instance
Reference the existing data-entry-app/frontend/src/services/api.js for company header logic.
Dependencies: Task 1
Completion Criteria:
- API service created with
/api/data-entrybase URL - Auth token interceptor configured
- Company header interceptor configured
Task 14: Merge and Adapt CSS from Data Entry
Objective: Integrate any unique Data Entry CSS into the unified CSS system.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/assets/css/modules/data-entry.css(create if needed)
Description:
- Review
data-entry-app/frontend/src/assets/css/main.cssfor unique styles - Extract any styles not already covered by the reports-app CSS system
- Add to a new
modules/data-entry.cssfile if needed - Import in main.css
Note: Data Entry currently uses lara-light-blue theme while Reports uses saga-blue.
Decision: Use saga-blue (reports-app theme) for consistency as per spec.
Dependencies: Task 4
Completion Criteria:
- Data Entry unique styles identified
- Styles merged without conflicts
- PrimeVue theme standardized to saga-blue
Phase 3: Routing & Navigation (0.5 days)
Task 15: Create Unified Router Configuration
Objective: Create unified Vue Router with lazy loading for both modules.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/router/index.js(create)
Description: Create unified router with:
- Login route (not lazy loaded - immediate access needed):
{
path: '/login',
name: 'Login',
component: () => import('@shared/components/LoginView.vue'),
meta: { requiresAuth: false, title: 'Autentificare - ROA2WEB' }
}
- Reports module routes (lazy loaded):
{
path: '/reports',
component: () => import('@/modules/reports/ReportsLayout.vue'),
children: [
{ path: 'dashboard', name: 'Dashboard', component: () => import('@reports/views/DashboardView.vue') },
{ path: 'invoices', name: 'Invoices', component: () => import('@reports/views/InvoicesView.vue') },
{ path: 'bank-cash', name: 'BankCash', component: () => import('@reports/views/BankCashRegisterView.vue') },
{ path: 'trial-balance', name: 'TrialBalance', component: () => import('@reports/views/TrialBalanceView.vue') },
{ path: 'telegram', name: 'Telegram', component: () => import('@reports/views/TelegramView.vue') },
{ path: 'cache-stats', name: 'CacheStats', component: () => import('@reports/views/CacheStatsView.vue') },
]
}
- Data Entry module routes (lazy loaded):
{
path: '/data-entry',
component: () => import('@/modules/data-entry/DataEntryLayout.vue'),
children: [
{ path: '', name: 'ReceiptsList', component: () => import('@data-entry/views/receipts/ReceiptsListView.vue') },
{ path: 'create', name: 'ReceiptCreate', component: () => import('@data-entry/views/receipts/ReceiptCreateView.vue') },
{ path: ':id', name: 'ReceiptDetail', component: () => import('@data-entry/views/receipts/ReceiptCreateView.vue') },
{ path: ':id/edit', name: 'ReceiptEdit', component: () => import('@data-entry/views/receipts/ReceiptCreateView.vue') },
]
}
- Redirects:
/->/reports/dashboard/:pathMatch(.*)*->/reports/dashboard
- Navigation guards:
- Check authentication before protected routes
- Set page title from route meta
- Scroll to top after navigation
Dependencies: Task 7, Task 10
Completion Criteria:
- All routes defined with lazy loading
- Navigation guards implemented
- Redirects configured
- Page titles set from meta
Task 16: Create Menu Configuration
Objective: Create unified menu configuration for all modules.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/config/menu.js(create)
Description: Create menu configuration with sections:
export const menuSections = [
{
title: 'Rapoarte',
items: [
{ to: '/reports/dashboard', icon: 'pi pi-home', label: 'Dashboard' },
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi' },
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Casa si Banca' },
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanta de Verificare' }
]
},
{
title: 'Introduceri Date',
items: [
{ to: '/data-entry', icon: 'pi pi-list', label: 'Lista Bonuri' },
{ to: '/data-entry/create', icon: 'pi pi-plus', label: 'Bon Nou' }
]
},
{
title: 'Sistem',
items: [
{ to: '/reports/telegram', icon: 'pi pi-telegram', label: 'Telegram Bot' },
{ to: '/reports/cache-stats', icon: 'pi pi-chart-bar', label: 'Statistici Cache' }
]
}
]
Dependencies: None
Completion Criteria:
- Menu configuration created
- All routes represented
- Icons assigned correctly
Task 17: Create Feature Flags Configuration
Objective: Create feature flags for module enable/disable.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/config/features.js(create)
Description: Create feature flags configuration:
export const features = {
reports: {
enabled: import.meta.env.VITE_FEATURE_REPORTS !== 'false',
modules: {
dashboard: true,
invoices: true,
bankCash: true,
trialBalance: true,
telegram: true,
cacheStats: true
}
},
dataEntry: {
enabled: import.meta.env.VITE_FEATURE_DATA_ENTRY !== 'false',
modules: {
receipts: true,
ocr: true
}
}
}
export function isFeatureEnabled(module, subModule = null) {
if (!features[module]?.enabled) return false
if (subModule && !features[module]?.modules?.[subModule]) return false
return true
}
export function getEnabledMenuSections(menuSections) {
return menuSections.filter(section => {
if (section.title === 'Rapoarte') return features.reports.enabled
if (section.title === 'Introduceri Date') return features.dataEntry.enabled
return true // System section always visible
})
}
Dependencies: Task 16
Completion Criteria:
- Feature flags created
- Environment variable support
- Helper functions for filtering menu
Task 18: Create App.vue Root Component
Objective: Create unified App.vue with menu integration.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/App.vue(create)
Description: Create App.vue that:
- Shows AppHeader and SlideMenu when authenticated
- Uses unified menu configuration from
config/menu.js - Filters menu based on feature flags
- Handles company/period changes
- Handles user logout (clears all stores)
- Uses Toast and ConfirmDialog globally
Reference reports-app/frontend/src/App.vue for structure but adapt for:
- Using
@shared/components - Using unified menu
- Using shared stores via aliases
Dependencies: Task 5, Task 15, Task 16, Task 17
Completion Criteria:
- App.vue created
- Header and SlideMenu integrated
- Menu sections from config
- Company/period handlers work
- Logout clears all stores
Task 19: Create main.js Entry Point
Objective: Create unified main.js with PrimeVue setup.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/main.js(create)
Description: Create main.js that:
- Creates Vue app
- Sets up Pinia store
- Sets up Vue Router
- Configures PrimeVue with
saga-bluetheme - Registers ToastService, ConfirmationService
- Registers common PrimeVue components globally
- Imports unified CSS (
./assets/css/main.css)
Merge component registrations from both apps:
- From reports: Button, InputText, Password, DataTable, Column, Card, Toast, ConfirmDialog, Menu, Menubar, Badge, Tag, Dropdown, AutoComplete, Calendar, ProgressSpinner, Dialog
- From data-entry: InputNumber, Textarea, FileUpload, Image, TabView, TabPanel, Checkbox, RadioButton, Toolbar, Divider, Message
Dependencies: Task 2, Task 4, Task 15
Completion Criteria:
- main.js created
- All PrimeVue components registered
- PrimeVue theme set to saga-blue
- CSS imports correct
- App mounts successfully
Phase 4: Error Boundaries & Resilience (0.25 days)
Task 20: Create ErrorBoundary Component
Objective: Create reusable error boundary component.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/ErrorBoundary.vue(create)
Description: Create ErrorBoundary.vue component that:
- Uses
onErrorCapturedto catch child component errors - Displays user-friendly error message in Romanian
- Provides "Retry" button (reloads page)
- Provides "Go to Dashboard" button
- Logs error details to console
- Returns
falsefrom onErrorCaptured to prevent propagation
Template structure:
<template>
<div v-if="error" class="module-error">
<div class="error-icon">
<i class="pi pi-exclamation-triangle"></i>
</div>
<h3>{{ moduleName }} a intampinat o eroare</h3>
<p class="error-message">{{ error.message }}</p>
<div class="error-actions">
<Button label="Reincearca" icon="pi pi-refresh" @click="retry" />
<Button label="Mergi la Dashboard" icon="pi pi-home" severity="secondary" @click="goHome" />
</div>
</div>
<slot v-else />
</template>
Dependencies: Task 5
Completion Criteria:
- ErrorBoundary component created
- Catches errors from children
- Shows user-friendly message
- Retry and navigation buttons work
Task 21: Create ReportsLayout Module Wrapper
Objective: Create layout wrapper with error boundary for Reports module.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/reports/ReportsLayout.vue(create)
Description: Create ReportsLayout.vue that wraps module routes with ErrorBoundary:
<template>
<ErrorBoundary module-name="Rapoarte">
<router-view />
</ErrorBoundary>
</template>
<script setup>
import ErrorBoundary from '@shared/components/ErrorBoundary.vue'
</script>
This ensures errors in Reports views don't crash the entire app.
Dependencies: Task 20
Completion Criteria:
- ReportsLayout created
- ErrorBoundary wraps router-view
- Module name set correctly
Task 22: Create DataEntryLayout Module Wrapper
Objective: Create layout wrapper with error boundary for Data Entry module.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/modules/data-entry/DataEntryLayout.vue(create)
Description: Create DataEntryLayout.vue that wraps module routes with ErrorBoundary:
<template>
<ErrorBoundary module-name="Introduceri Date">
<router-view />
</ErrorBoundary>
</template>
<script setup>
import ErrorBoundary from '@shared/components/ErrorBoundary.vue'
</script>
Dependencies: Task 20
Completion Criteria:
- DataEntryLayout created
- ErrorBoundary wraps router-view
- Module name set correctly
Task 23: Add Loading States for Lazy Routes
Objective: Add loading indicators during module loading.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/components/ModuleLoading.vue(create)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/router/index.js(update)
Description:
- Create ModuleLoading.vue component with ProgressSpinner
- Configure router to show loading state during async component loading
Using Vue Router's built-in async component handling:
component: defineAsyncComponent({
loader: () => import('@reports/views/DashboardView.vue'),
loadingComponent: ModuleLoading,
delay: 200,
timeout: 10000
})
Or use Suspense in layout components.
Dependencies: Task 15, Task 20
Completion Criteria:
- Loading component created
- Shown during module lazy loading
- Reasonable delay before showing
Phase 5: Build & Deployment (0.25 days)
Task 24: Create IIS web.config for Production
Objective: Create web.config with URL rewrite rules for IIS deployment.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/public/web.config(create)
Description: Create web.config with:
-
URL Rewrite Rules:
/api/reports/*->http://localhost:8001/api/*/api/data-entry/*->http://localhost:8003/api/*/uploads/*->http://localhost:8003/uploads/*- SPA fallback: all other routes ->
/index.html
-
Static file handling
-
Cache headers for assets
-
MIME types for modern files
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Proxy Reports API" stopProcessing="true">
<match url="^api/reports/(.*)" />
<action type="Rewrite" url="http://localhost:8001/api/{R:1}" />
</rule>
<rule name="Proxy Data Entry API" stopProcessing="true">
<match url="^api/data-entry/(.*)" />
<action type="Rewrite" url="http://localhost:8003/api/{R:1}" />
</rule>
<rule name="Proxy Uploads" stopProcessing="true">
<match url="^uploads/(.*)" />
<action type="Rewrite" url="http://localhost:8003/uploads/{R:1}" />
</rule>
<rule name="SPA Fallback" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
<staticContent>
<mimeMap fileExtension=".webmanifest" mimeType="application/manifest+json" />
</staticContent>
</system.webServer>
</configuration>
Dependencies: None
Completion Criteria:
- web.config created
- API proxy rules correct
- SPA fallback configured
Task 25: Verify Build and Bundle Splitting
Objective: Run production build and verify output.
Files:
- (verification task, no file modifications)
Description:
-
Run
npm run build -
Verify
dist/output:assets/vendor-core.[hash].js(vue, router, pinia)assets/vendor-primevue.[hash].js(primevue components)assets/vendor-utils.[hash].js(axios, date-fns)assets/vendor-charts.[hash].js(chart.js - lazy)assets/vendor-export.[hash].js(xlsx, jspdf - lazy)- Module chunks for reports and data-entry
- CSS chunk
-
Check total bundle size <= sum of old apps
-
Verify source maps generated
Dependencies: Tasks 1-23
Completion Criteria:
- Build completes without errors
- Expected chunks generated
- Bundle size acceptable
- Source maps present
Task 26: Create README Documentation
Objective: Create README for the unified app.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/unified-app-README.md(create, will be moved to README.md later)
Description: Create documentation covering:
- Overview: Unified SPA combining Reports and Data Entry
- Quick Start:
npm installnpm run dev(requires both backends running)npm run build
- Architecture:
- Module structure
- Shared components
- Error boundaries
- URL Structure
- Deployment:
- IIS configuration
- Proxy setup
- Feature Flags
- Development Guide:
- Adding new routes
- Adding new components
- CSS system reference
Dependencies: None
Completion Criteria:
- README covers all sections
- Quick start instructions work
- Architecture explained clearly
Task 27: Update Shared Stores for Dual API Support
Objective: Ensure shared stores work with both API endpoints.
Files:
/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/stores/auth.js(update)/mnt/e/proiecte/ab-worktrees/roa2web-unified-app/src/shared/stores/companies.js(update)
Description: The shared stores (auth, companies) need to work with both backends. Since authentication is handled by the Reports backend (port 8001), update:
- auth.js: Use
/api/reports/auth/for login/logout - companies.js: Use
/api/reports/companies/for company list
Ensure the API base URL is correctly configured for the unified app's proxy setup.
Dependencies: Task 5, Task 3
Completion Criteria:
- Auth store uses correct API path
- Companies store uses correct API path
- Login/logout flow works
Task 28: End-to-End Integration Test
Objective: Verify the complete unified app works.
Files:
- (verification task, manual testing)
Description: Manual testing checklist:
-
Dev Server:
npm run devstarts on port 3000- No console errors on load
-
Login Flow:
- Navigate to
/login - Login with valid credentials
- Redirected to
/reports/dashboard
- Navigate to
-
Reports Module:
- Dashboard loads with data
- Navigate to Invoices
- Navigate to Bank/Cash
- Navigate to Trial Balance
- Navigate to Telegram
- Navigate to Cache Stats
-
Data Entry Module:
- Navigate to
/data-entry(Receipts List) - Navigate to
/data-entry/create(Create Receipt) - Company selector preserved from Reports
- Navigate to
-
Module Switching:
- Switch from Reports to Data Entry
- Switch from Data Entry to Reports
- Selected company/period preserved
-
Error Boundary:
- Intentionally break a component
- Error shows in that module only
- Other module still works
-
Logout:
- Logout works
- Redirected to login
- All stores cleared
Dependencies: All previous tasks
Completion Criteria:
- All manual tests pass
- No console errors
- No visual regressions
Testing Strategy
During Implementation
- After each task, verify no import/build errors
- Test changed components individually
- Check browser console for errors
Final Validation
- Run
npm run build- must succeed - Run
npm run preview- test production build - Test on different screen sizes (mobile, tablet, desktop)
- Verify lazy loading with Network tab (modules load on demand)
Risk Mitigation
| Risk | Detection | Response |
|---|---|---|
| CSS conflicts between modules | Visual testing shows wrong styles | Use scoped styles or module-specific CSS files |
| Import path errors | Build fails or runtime errors | Check all import aliases in vite.config.js |
| PrimeVue theme inconsistency | Different component styles | Ensure only saga-blue theme imported |
| Shared store contamination | Data appears in wrong module | Keep module stores separate, only use shared for auth/company/period |
| Lazy loading not working | Large initial bundle | Verify dynamic imports in router |
| Error boundary not catching | App crashes on error | Test with intentional errors |
Notes for Implementation
- Always use absolute paths from worktree root when creating files
- Test incrementally - don't wait until the end to test
- Preserve existing functionality - this is a migration, not a rewrite
- Reference existing files - use reports-app as the primary reference
- Use saga-blue theme consistently (reports-app theme)
- Login always uses Reports API (port 8001) for authentication
- Keep module stores isolated - no cross-module store dependencies