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>
1175 lines
35 KiB
Markdown
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
|