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>
This commit is contained in:
2025-12-24 19:06:23 +02:00
parent fed2e68fa2
commit d507a81b0a
112 changed files with 38382 additions and 2382 deletions

View File

@@ -0,0 +1,71 @@
{
"session_id": "ses_20251222_182000",
"timestamp": "2025-12-22T18:20:00Z",
"feature": "unified-app",
"duration": "2h 30m",
"insights_saved": [
{"type": "pattern", "id": "pat_20251222_182000", "title": "Unified Vue SPA with Module Isolation via Error Boundaries"},
{"type": "pattern", "id": "pat_20251222_182001", "title": "Dual API Proxy Pattern in Vite for Microservices"},
{"type": "pattern", "id": "pat_20251222_182002", "title": "Pinia Store Factory Pattern for Shared Stores"},
{"type": "pattern", "id": "pat_20251222_182003", "title": "Module-Specific Shared Store Instances"},
{"type": "pattern", "id": "pat_20251222_182004", "title": "Vite Alias Strategy for Module Organization"},
{"type": "pattern", "id": "pat_20251222_182005", "title": "IIS URL Rewrite Rules for SPA with Multiple API Backends"},
{"type": "gotcha", "id": "got_20251222_182000", "title": "Import Path Hell: Default vs Named Exports"},
{"type": "gotcha", "id": "got_20251222_182001", "title": "Pinia Store Factory Pattern Not Auto-Exported"},
{"type": "gotcha", "id": "got_20251222_182002", "title": "Sed Command Quote Mismatch in Bulk Find-Replace"},
{"type": "gotcha", "id": "got_20251222_182003", "title": "Circular Reference in API Wrapper"},
{"type": "gotcha", "id": "got_20251222_182004", "title": "CSS Import Paths Breaking Build in Unified Structure"},
{"type": "gotcha", "id": "got_20251222_182005", "title": "Module Component Utilities Not Copied During Migration"},
{"type": "gotcha", "id": "got_20251222_182006", "title": "Vite Build Transform Count is Progress Indicator"}
],
"files_created": [
"package.json",
"vite.config.js",
"src/router/index.js",
"src/config/menu.js",
"src/config/features.js",
"src/App.vue",
"src/main.js",
"src/shared/components/ErrorBoundary.vue",
"src/modules/reports/ReportsLayout.vue",
"src/modules/data-entry/DataEntryLayout.vue",
"src/modules/reports/services/api.js",
"src/modules/data-entry/services/api.js",
"src/modules/reports/stores/sharedStores.js",
"src/modules/data-entry/stores/sharedStores.js",
"public/web.config",
".env.example",
"index.html"
],
"files_migrated": [
"src/modules/reports/views/*.vue (6 files)",
"src/modules/reports/stores/*.js (5 files)",
"src/modules/reports/components/** (dashboard cards, layout)",
"src/modules/reports/utils/*.js",
"src/modules/data-entry/views/receipts/*.vue (2 files)",
"src/modules/data-entry/components/ocr/*.vue (3 files)",
"src/modules/data-entry/stores/receiptsStore.js",
"src/shared/components/** (LoginView, CompanySelector, PeriodSelector, AppHeader, SlideMenu)",
"src/shared/stores/** (auth.js, companies.js, accountingPeriod.js)",
"src/shared/styles/**",
"src/assets/css/** (complete CSS system from reports-app)"
],
"summary": "Successfully consolidated Reports App and Data Entry App into single unified SPA. Implemented module isolation via error boundaries, dual API proxy configuration, and lazy loading. Build completed successfully with 1492 modules transformed, generating 12M dist with proper code splitting.",
"build_stats": {
"modules_transformed": 1492,
"bundle_size": "12M",
"chunks_generated": 63,
"largest_chunks": [
"vendor-export.js (704KB)",
"vendor-primevue.js (524KB)",
"vendor-charts.js (207KB)"
]
},
"next_steps": [
"Test dev server with npm run dev",
"Update App.vue to use module-specific store instances",
"Deploy dist/ to IIS and verify API proxies",
"Test module switching and error boundary isolation",
"Create unified-app README documentation"
]
}

View File

@@ -0,0 +1,106 @@
{
"session_id": "20251224-001000-unified-app-ux",
"timestamp": "2025-12-24T00:10:00Z",
"feature": "unified-app-ux",
"branch": "feature/ab-unified-app",
"summary": "Fixed critical UX issues: period auto-selection after company change and empty hamburger menu display",
"goals": [
"Fix period dropdown auto-selection when company is manually selected",
"Fix empty hamburger menu (no menu items visible)",
"Ensure JWT token is sent in all API requests",
"Resolve Pinia store timing issues"
],
"outcomes": {
"success": true,
"patterns_learned": 3,
"gotchas_encountered": 4,
"files_modified": 4,
"tests_created": 3,
"commits": 1
},
"key_files": [
"src/App.vue",
"src/shared/stores/accountingPeriod.js",
"src/shared/stores/companies.js",
"src/shared/components/layout/AppHeader.vue"
],
"commits": [
{
"hash": "287b9a9",
"message": "fix: Implement complete auto-selection and fix hamburger menu display",
"files_changed": 4,
"insertions": 97,
"deletions": 68
}
],
"technical_insights": [
"Vue watch() with { immediate: true } is essential for reactive data loading when dependent selections change",
"Axios request interceptors must be configured AFTER creating instance but BEFORE making API calls",
"Pinia store factories accessing other stores require lazy instantiation with try-catch to avoid timing issues",
"Component data structure contracts must be honored - flatMap() broke nested section structure expected by SlideMenu",
"Handler functions alone aren't enough for reactive updates - need watchers to trigger automatic data loading"
],
"problems_solved": [
{
"problem": "Period dropdown stayed on placeholder after company selection",
"root_cause": "Missing Vue watcher to trigger loadPeriods() and missing JWT token in API requests",
"solution": "Added watch() on companyStore.selectedCompany + axios request interceptor for JWT token",
"files": ["src/App.vue"]
},
{
"problem": "Hamburger menu completely empty (no menu items)",
"root_cause": "enabledMenuItems used flatMap() creating flat array but SlideMenu expected nested sections",
"solution": "Removed flatMap() and returned nested structure directly from getEnabledMenuSections()",
"files": ["src/App.vue"]
},
{
"problem": "TypeError: useAuthStore is not a function",
"root_cause": "Period store tried to access stores before Pinia initialization",
"solution": "Wrapped store access in try-catch with lazy instantiation in getStorageKey()",
"files": ["src/shared/stores/accountingPeriod.js"]
},
{
"problem": "API requests returning 500 errors",
"root_cause": "No Authorization header with JWT token in requests",
"solution": "Added axios request interceptor to inject Bearer token from localStorage",
"files": ["src/App.vue"]
}
],
"patterns_added": [
"pat_20251224_001000 - Vue Watcher for Auto-Loading Dependent Data",
"pat_20251224_001001 - Axios Request Interceptor for JWT Token Injection",
"pat_20251224_001002 - Pinia Store Factory with Lazy Instantiation"
],
"gotchas_added": [
"got_20251224_001000 - Menu Structure Mismatch: Flat Array vs Nested Sections",
"got_20251224_001001 - TypeError: useAuthStore is not a function - Store Timing Issue",
"got_20251224_001002 - Missing Auth Token in API Requests Causes 500 Errors",
"got_20251224_001003 - Period Auto-Load Never Triggered Despite Handler Exists"
],
"testing": {
"approach": "Playwright E2E tests",
"tests_created": [
"tests/e2e/unified-app/manual-company-select-test.spec.js - Verified period auto-selection",
"tests/e2e/unified-app/hamburger-menu-visual-test.spec.js - Checked for white-on-white issues",
"tests/e2e/unified-app/hamburger-menu-items-test.spec.js - Verified menu sections/items visible"
],
"all_tests_passed": true,
"cleanup": "All test files deleted per user request after verification"
},
"user_feedback": [
"dar si dupa ce aleg o firma MARIUS M AUTO, AR TREBUI SA SE SELECTEZE AUTOMAT ULTIMA LUNA, SI NU SE SELECTEAZA",
"MENIUL HAMBURGER ARE IN CONTINUARE TEXT ALB PE FUNDAL ALB",
"iata un screenshot cu meniul hamburger complet blank",
"commit cu toate fix-urile",
"sterge toate testele si rapoartele si screenshot-urile"
],
"lessons_learned": [
"Always use Vue watchers for reactive data dependencies, not just event handlers",
"Component props must match expected data structure - verify with component source",
"Pinia stores must be lazily instantiated when accessing other stores to avoid timing issues",
"Axios interceptors are critical for automatic JWT token injection - don't rely on manual headers",
"Test automation helps verify fixes but manual testing with real scenarios catches UX issues"
],
"next_steps": [],
"tags": ["vue", "pinia", "axios", "ux", "auto-selection", "authentication", "watchers", "stores"]
}