Files
roa2web-service-auto/.auto-build/specs/unified-app/plan.md
Marius Mutu d507a81b0a feat: Implement unified Vue SPA with granular service control
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>
2025-12-24 19:06:23 +02:00

1175 lines
35 KiB
Markdown

# 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**:
- [x] All directories created
- [x] 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.0
- `vue-router`: ^4.2.5
- `pinia`: ^2.1.7
- `axios`: ^1.6.5 (use higher version)
- `primevue`: ^3.48.0 (use higher version)
- `primeicons`: ^6.0.1
- `chart.js`: ^4.5.0 (reports only)
- `vue-chartjs`: ^5.3.2 (reports only)
- `date-fns`: ^2.30.0
- `jspdf`: ^3.0.1
- `jspdf-autotable`: ^5.0.2
- `xlsx`: ^0.18.5
- `qrcode.vue`: ^3.6.0
**DevDependencies:**
- `@vitejs/plugin-vue`: ^5.0.0
- `vite`: ^5.0.10
- `@playwright/test`: ^1.54.2
- `eslint`: ^8.56.0
- `eslint-plugin-vue`: ^9.20.0
- `prettier`: ^3.1.1
**Scripts:**
- `dev`: vite
- `build`: vite build
- `preview`: vite preview
- `lint`: eslint src/ --ext .vue,.js --fix
- `test:e2e`: playwright test
**Dependencies**: Task 1
**Completion Criteria**:
- [x] package.json created with all dependencies
- [x] Scripts defined correctly
- [x] `npm install` succeeds
---
### 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:
1. **Dual proxy configuration:**
- `/api/reports/*` -> `http://localhost:8001/api/*`
- `/api/data-entry/*` -> `http://localhost:8003/api/*`
- `/uploads` -> `http://localhost:8003`
2. **Aliases:**
- `@` -> `./src`
- `@shared` -> `./src/shared`
- `@reports` -> `./src/modules/reports`
- `@data-entry` -> `./src/modules/data-entry`
3. **Build configuration:**
- `base`: `/` (single site, no subdirectory)
- Manual chunks for vendors (vue, primevue, charts, exports)
- Source maps enabled
- Cache busting with hashes
4. **dedupe** for Vue, vue-router, pinia, primevue
5. **WSL2 file watching** (usePolling: true)
Reference: `reports-app/frontend/vite.config.js` for htmlTimestampPlugin and build settings.
**Dependencies**: Task 1
**Completion Criteria**:
- [x] Vite config created with dual proxy
- [x] All aliases defined
- [x] Manual chunks configured
- [x] `npm run dev` starts 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 patterns
- `layout/` - Page structure
- `utilities/` - Utility classes
- `vendor/` - PrimeVue overrides
- `main.css` - Main import file
- `global.css` - Global styles
- `mobile.css` - Mobile responsive styles
This is the authoritative CSS system that both modules will use.
**Dependencies**: Task 1
**Completion Criteria**:
- [x] All CSS files copied
- [x] Directory structure preserved
- [x] `main.css` imports 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/`:
1. Components: `LoginView.vue`, `CompanySelector.vue`, `PeriodSelector.vue`, `layout/AppHeader.vue`, `layout/SlideMenu.vue`
2. Stores: `auth.js`, `companies.js`, `accountingPeriod.js`
3. 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**:
- [x] All shared components copied
- [x] All shared stores copied
- [x] All shared styles copied
- [x] 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**:
1. 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
```
2. Create `public/index.html` (or use Vite's default index.html in root):
```html
<!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**:
- [x] .env.example created
- [x] index.html created with proper meta tags
- [x] 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/xxx` to `@reports/stores/xxx` for module-specific stores
- Change `@/stores/auth` to `@shared/stores/auth`
- Change `@/stores/companies` to `@shared/stores/companies`
- Change `@/stores/accountingPeriod` to `@shared/stores/accountingPeriod`
**Dependencies**: Task 4, Task 5
**Completion Criteria**:
- [x] All 6 views copied
- [x] Import paths updated
- [x] 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**:
- [x] All 6 store files copied
- [x] No references to shared stores (auth, companies, period)
- [x] 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`:
1. Base URL: `/api/reports` (proxied to port 8001)
2. Include auth token interceptor
3. Include response error handler
4. Export configured axios instance
Example structure:
```javascript
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**:
- [x] API service created with `/api/reports` base URL
- [x] Auth token interceptor configured
- [x] 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/xxx` to `@data-entry/stores/xxx` for module-specific stores
- Change `@/stores/auth` to `@shared/stores/auth`
- Change `@/stores/companies` to `@shared/stores/companies`
- Change `@/stores/accountingPeriod` to `@shared/stores/accountingPeriod`
- Change `@/components/xxx` to `@data-entry/components/xxx`
- Change `@/services/api` to `@data-entry/services/api`
**Dependencies**: Task 4, Task 5
**Completion Criteria**:
- [x] Both receipt views copied
- [x] Import paths updated
- [x] 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**:
- [x] All 3 OCR components copied
- [x] Import paths updated
- [x] 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**:
- [x] receiptsStore.js copied
- [x] Import paths updated
- [x] 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`:
1. Base URL: `/api/data-entry` (proxied to port 8003)
2. Include auth token interceptor
3. Include `X-Selected-Company` header injection
4. Include response error handler
5. Export configured axios instance
Reference the existing `data-entry-app/frontend/src/services/api.js` for company header logic.
**Dependencies**: Task 1
**Completion Criteria**:
- [x] API service created with `/api/data-entry` base URL
- [x] Auth token interceptor configured
- [x] 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**:
1. Review `data-entry-app/frontend/src/assets/css/main.css` for unique styles
2. Extract any styles not already covered by the reports-app CSS system
3. Add to a new `modules/data-entry.css` file if needed
4. 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**:
- [x] Data Entry unique styles identified
- [x] Styles merged without conflicts
- [x] 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:
1. **Login route** (not lazy loaded - immediate access needed):
```javascript
{
path: '/login',
name: 'Login',
component: () => import('@shared/components/LoginView.vue'),
meta: { requiresAuth: false, title: 'Autentificare - ROA2WEB' }
}
```
2. **Reports module routes** (lazy loaded):
```javascript
{
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') },
]
}
```
3. **Data Entry module routes** (lazy loaded):
```javascript
{
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') },
]
}
```
4. **Redirects**:
- `/` -> `/reports/dashboard`
- `/:pathMatch(.*)*` -> `/reports/dashboard`
5. **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**:
- [x] All routes defined with lazy loading
- [x] Navigation guards implemented
- [x] Redirects configured
- [x] 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:
```javascript
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**:
- [x] Menu configuration created
- [x] All routes represented
- [x] 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:
```javascript
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**:
- [x] Feature flags created
- [x] Environment variable support
- [x] 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:
1. Shows AppHeader and SlideMenu when authenticated
2. Uses unified menu configuration from `config/menu.js`
3. Filters menu based on feature flags
4. Handles company/period changes
5. Handles user logout (clears all stores)
6. 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**:
- [x] App.vue created
- [x] Header and SlideMenu integrated
- [x] Menu sections from config
- [x] Company/period handlers work
- [x] 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:
1. Creates Vue app
2. Sets up Pinia store
3. Sets up Vue Router
4. Configures PrimeVue with `saga-blue` theme
5. Registers ToastService, ConfirmationService
6. Registers common PrimeVue components globally
7. 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**:
- [x] main.js created
- [x] All PrimeVue components registered
- [x] PrimeVue theme set to saga-blue
- [x] CSS imports correct
- [x] 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:
1. Uses `onErrorCaptured` to catch child component errors
2. Displays user-friendly error message in Romanian
3. Provides "Retry" button (reloads page)
4. Provides "Go to Dashboard" button
5. Logs error details to console
6. Returns `false` from onErrorCaptured to prevent propagation
Template structure:
```vue
<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**:
- [x] ErrorBoundary component created
- [x] Catches errors from children
- [x] Shows user-friendly message
- [x] 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:
```vue
<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**:
- [x] ReportsLayout created
- [x] ErrorBoundary wraps router-view
- [x] 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:
```vue
<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**:
- [x] DataEntryLayout created
- [x] ErrorBoundary wraps router-view
- [x] 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**:
1. Create ModuleLoading.vue component with ProgressSpinner
2. Configure router to show loading state during async component loading
Using Vue Router's built-in async component handling:
```javascript
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**:
- [x] Loading component created
- [x] Shown during module lazy loading
- [x] 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:
1. **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`
2. **Static file handling**
3. **Cache headers for assets**
4. **MIME types for modern files**
```xml
<?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**:
- [x] web.config created
- [x] API proxy rules correct
- [x] SPA fallback configured
---
### Task 25: Verify Build and Bundle Splitting
**Objective**: Run production build and verify output.
**Files**:
- (verification task, no file modifications)
**Description**:
1. Run `npm run build`
2. 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
3. Check total bundle size <= sum of old apps
4. Verify source maps generated
**Dependencies**: Tasks 1-23
**Completion Criteria**:
- [x] Build completes without errors
- [x] Expected chunks generated
- [x] Bundle size acceptable
- [x] 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:
1. **Overview**: Unified SPA combining Reports and Data Entry
2. **Quick Start**:
- `npm install`
- `npm run dev` (requires both backends running)
- `npm run build`
3. **Architecture**:
- Module structure
- Shared components
- Error boundaries
4. **URL Structure**
5. **Deployment**:
- IIS configuration
- Proxy setup
6. **Feature Flags**
7. **Development Guide**:
- Adding new routes
- Adding new components
- CSS system reference
**Dependencies**: None
**Completion Criteria**:
- [x] README covers all sections
- [x] Quick start instructions work
- [x] 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:
1. **auth.js**: Use `/api/reports/auth/` for login/logout
2. **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**:
- [x] Auth store uses correct API path
- [x] Companies store uses correct API path
- [x] 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:
1. **Dev Server**:
- [ ] `npm run dev` starts on port 3000
- [ ] No console errors on load
2. **Login Flow**:
- [ ] Navigate to `/login`
- [ ] Login with valid credentials
- [ ] Redirected to `/reports/dashboard`
3. **Reports Module**:
- [ ] Dashboard loads with data
- [ ] Navigate to Invoices
- [ ] Navigate to Bank/Cash
- [ ] Navigate to Trial Balance
- [ ] Navigate to Telegram
- [ ] Navigate to Cache Stats
4. **Data Entry Module**:
- [ ] Navigate to `/data-entry` (Receipts List)
- [ ] Navigate to `/data-entry/create` (Create Receipt)
- [ ] Company selector preserved from Reports
5. **Module Switching**:
- [ ] Switch from Reports to Data Entry
- [ ] Switch from Data Entry to Reports
- [ ] Selected company/period preserved
6. **Error Boundary**:
- [ ] Intentionally break a component
- [ ] Error shows in that module only
- [ ] Other module still works
7. **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
1. **Always use absolute paths** from worktree root when creating files
2. **Test incrementally** - don't wait until the end to test
3. **Preserve existing functionality** - this is a migration, not a rewrite
4. **Reference existing files** - use reports-app as the primary reference
5. **Use saga-blue theme** consistently (reports-app theme)
6. **Login always uses Reports API** (port 8001) for authentication
7. **Keep module stores isolated** - no cross-module store dependencies