Files
roa2web-service-auto/unified-app-README.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

17 KiB

ROA2WEB - Unified Application

Single-Page Application combining Reports and Data Entry modules into one unified frontend.

📋 Overview

This is the unified frontend for the ROA2WEB ERP system, consolidating two previously separate applications:

  • Reports Module - Read-only financial reports from Oracle database
  • Data Entry Module - Fiscal receipt management with approval workflow

Key Features

  • Single Build & Deployment - One IIS site instead of two
  • Module Isolation - Error boundaries prevent crashes from propagating
  • Lazy Loading - Modules load on-demand for optimal performance
  • Unified Navigation - Seamless switching between Reports and Data Entry
  • Shared Authentication - Single login for both modules
  • Feature Flags - Enable/disable modules via configuration

Technology Stack

  • Frontend: Vue 3 (Composition API), Vite
  • UI Library: PrimeVue (saga-blue theme)
  • State Management: Pinia
  • Routing: Vue Router (with lazy loading)
  • HTTP Client: Axios
  • Charts: Chart.js, vue-chartjs
  • Export: jsPDF, xlsx

🚀 Quick Start

Prerequisites

  • Node.js: 16+ (18+ recommended)
  • npm: 8+
  • Backend Services:
    • Reports API running on http://localhost:8001
    • Data Entry API running on http://localhost:8003

Installation

# Install dependencies
npm install

Development

# Start all services (backends + frontend) in DEV mode
./start-dev.sh

# Or for TEST environment
./start-test.sh

This will start:

  • Reports Backend on port 8001 (with auto-reload in dev mode)
  • Data Entry Backend on port 8003 (with auto-reload in dev mode)
  • Telegram Bot on port 8002
  • Unified Frontend on port 3000
  • SSH Tunnel for Oracle database

Option 2: Start Services Manually

# Terminal 1 - Reports Backend
cd reports-app/backend
source venv/bin/activate
uvicorn app.main:app --reload --port 8001

# Terminal 2 - Data Entry Backend
cd data-entry-app/backend
source venv/bin/activate
uvicorn app.main:app --reload --port 8003

# Terminal 3 - Unified Frontend
npm run dev

Access the app at: http://localhost:3000

Production Build

# Build for production
npm run build

# Preview production build locally
npm run preview

Build output will be in the dist/ directory.


🏗️ Architecture

Directory Structure

src/
├── main.js                    # App entry point
├── App.vue                    # Root component with unified menu
│
├── router/
│   └── index.js              # Unified router with lazy loading
│
├── config/
│   ├── menu.js               # Menu configuration
│   └── features.js           # Feature flags
│
├── modules/
│   ├── reports/              # REPORTS MODULE (isolated)
│   │   ├── ReportsLayout.vue # Error boundary wrapper
│   │   ├── views/            # Dashboard, Invoices, Bank/Cash, etc.
│   │   ├── components/       # Reports-specific components
│   │   ├── stores/           # Module stores + sharedStores.js
│   │   └── services/
│   │       └── api.js        # API client (/api/reports)
│   │
│   └── data-entry/           # DATA ENTRY MODULE (isolated)
│       ├── DataEntryLayout.vue # Error boundary wrapper
│       ├── views/receipts/   # Receipts List, Create
│       ├── components/ocr/   # OCR components
│       ├── stores/           # Module stores + sharedStores.js
│       └── services/
│           └── api.js        # API client (/api/data-entry)
│
├── shared/                   # SHARED ACROSS MODULES
│   ├── components/           # LoginView, Selectors, Layout
│   │   ├── LoginView.vue
│   │   ├── CompanySelector.vue
│   │   ├── PeriodSelector.vue
│   │   ├── ErrorBoundary.vue
│   │   └── layout/
│   │       ├── AppHeader.vue
│   │       └── SlideMenu.vue
│   ├── stores/               # Shared store factories
│   │   ├── auth.js
│   │   ├── companies.js
│   │   └── accountingPeriod.js
│   └── styles/               # Shared CSS
│
└── assets/
    └── css/                  # Global CSS design system
        ├── core/             # Design tokens
        ├── components/       # Component patterns
        ├── patterns/         # Interactive patterns
        ├── layout/           # Page structure
        ├── utilities/        # Utility classes
        └── vendor/           # PrimeVue overrides

Module Isolation

Each module is wrapped in an ErrorBoundary component:

<!-- ReportsLayout.vue -->
<template>
  <ErrorBoundary module-name="Rapoarte">
    <router-view />
  </ErrorBoundary>
</template>

Benefits:

  • Errors in one module don't crash the entire app
  • Other modules continue to function normally
  • User-friendly error messages with recovery options

Lazy Loading Strategy

Modules are loaded on-demand using dynamic imports:

// Reports module loaded only when user navigates to /reports/*
{
  path: '/reports',
  component: () => import('@/modules/reports/ReportsLayout.vue'),
  children: [
    {
      path: 'dashboard',
      component: () => import('@reports/views/DashboardView.vue')
    }
  ]
}

Shared Store Pattern

Shared stores use a factory pattern to work with both modules:

// src/shared/stores/auth.js
export function createAuthStore(apiService) {
  return defineStore('auth', () => {
    // Store implementation using provided apiService
  })
}

// src/modules/reports/stores/sharedStores.js
import { createAuthStore } from '@shared/stores/auth'
import api from '@reports/services/api'

export const useAuthStore = createAuthStore(api) // Binds to Reports API

Each module instantiates shared stores with its own API service.


🔗 URL Structure

Public Routes

  • /login - Login page

Reports Module

  • /reports/dashboard - Financial dashboard
  • /reports/invoices - Invoices view
  • /reports/bank-cash - Bank & cash register
  • /reports/trial-balance - Trial balance
  • /reports/telegram - Telegram bot management
  • /reports/cache-stats - Cache statistics

Data Entry Module

  • /data-entry - Receipts list
  • /data-entry/create - Create new receipt
  • /data-entry/:id - View receipt details
  • /data-entry/:id/edit - Edit receipt

Redirects

  • //reports/dashboard (default)

🚢 Deployment

IIS Configuration (Windows Production)

1. Build the Application

npm run build

2. Deploy to IIS

Copy the dist/ folder contents to your IIS site directory (e.g., C:\inetpub\wwwroot\roa2web\).

3. Configure URL Rewriting

The public/web.config file (copied to dist/) contains the necessary IIS rewrite rules:

  • API Proxies:

    • /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

4. Start Backend Services

Ensure both backend services are running:

  • Reports API (port 8001) - via NSSM or similar
  • Data Entry API (port 8003) - via NSSM or similar

5. IIS Application Pool Settings

  • .NET CLR Version: No Managed Code
  • Pipeline Mode: Integrated
  • Identity: ApplicationPoolIdentity (or custom service account)

Nginx Configuration (Linux Production)

server {
    listen 80;
    server_name your-domain.com;

    root /var/www/roa2web;
    index index.html;

    # API Proxies
    location /api/reports/ {
        proxy_pass http://localhost:8001/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /api/data-entry/ {
        proxy_pass http://localhost:8003/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /uploads/ {
        proxy_pass http://localhost:8003/uploads/;
    }

    # SPA Fallback
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

🎛️ Feature Flags

Feature flags allow enabling/disabling modules without redeployment.

Configuration

Edit src/config/features.js:

export const features = {
  reports: {
    enabled: true,
    modules: {
      dashboard: true,
      invoices: true,
      bankCash: true,
      trialBalance: true,
      telegram: true,
      cacheStats: true
    }
  },
  dataEntry: {
    enabled: true,
    modules: {
      receipts: true,
      ocr: true
    }
  }
}

Environment Variables

Override via .env file:

# Disable Data Entry module
VITE_FEATURE_DATA_ENTRY=false

# Disable Reports module
VITE_FEATURE_REPORTS=false

Usage

The menu automatically filters disabled modules:

import { isFeatureEnabled } from '@/config/features'

if (isFeatureEnabled('reports')) {
  // Show Reports menu items
}

🛠️ Development Guide

Adding a New Route

1. Create the View Component

# For Reports module
src/modules/reports/views/NewView.vue

# For Data Entry module
src/modules/data-entry/views/NewView.vue

2. Register in Router

Edit src/router/index.js:

{
  path: '/reports/new-feature',
  name: 'NewFeature',
  component: () => import('@reports/views/NewView.vue'),
  meta: { requiresAuth: true, title: 'New Feature - ROA2WEB' }
}

3. Add to Menu

Edit src/config/menu.js:

{
  to: '/reports/new-feature',
  icon: 'pi pi-star',
  label: 'New Feature'
}

Adding a New Component

Module-Specific Component

# Reports module
src/modules/reports/components/MyComponent.vue

# Data Entry module
src/modules/data-entry/components/MyComponent.vue

Import using module alias:

import MyComponent from '@reports/components/MyComponent.vue'

Shared Component

src/shared/components/MySharedComponent.vue

Import using shared alias:

import MySharedComponent from '@shared/components/MySharedComponent.vue'

Using the CSS Design System

The unified app uses the Reports App CSS architecture. See docs/ONBOARDING_CSS.md for a complete guide.

Quick Reference

Design Tokens (src/assets/css/core/tokens.css):

var(--color-primary)      /* #2563eb - Primary blue */
var(--color-success)      /* #16a34a - Success green */
var(--color-danger)       /* #dc2626 - Danger red */
var(--spacing-md)         /* 1rem - Medium spacing */
var(--radius-md)          /* 0.5rem - Medium border radius */

Component Patterns (src/assets/css/components/):

<!-- Card pattern -->
<div class="roa-card">
  <div class="card-header">Title</div>
  <div class="card-body">Content</div>
</div>

<!-- Badge pattern -->
<span class="roa-badge-success">Active</span>
<span class="roa-badge-warning">Pending</span>

<!-- Stats pattern -->
<div class="roa-metric">
  <div class="metric-value">1,234</div>
  <div class="metric-label">Total</div>
</div>

Best Practices:

  • Use global patterns from src/assets/css/
  • Use design tokens for colors, spacing, typography
  • Don't create new CSS files without checking existing patterns
  • Don't use <style scoped> in components (use global classes)
  • Don't hardcode colors or spacing values

Adding a Module Store

1. Create Store File

// src/modules/reports/stores/myStore.js
import { defineStore } from 'pinia'
import api from '@reports/services/api'

export const useMyStore = defineStore('my-store', () => {
  const data = ref([])

  const fetchData = async () => {
    const response = await api.get('/endpoint')
    data.value = response.data
  }

  return { data, fetchData }
})

2. Use in Component

<script setup>
import { useMyStore } from '@reports/stores/myStore'

const myStore = useMyStore()
await myStore.fetchData()
</script>

Working with Shared Stores

Import from the module's sharedStores.js:

// ✅ CORRECT
import { useAuthStore } from '@reports/stores/sharedStores'

// ❌ WRONG - Don't import directly from @shared
import { createAuthStore } from '@shared/stores/auth'

This ensures the store is bound to the correct API service.


🧪 Testing

Manual Testing Checklist

See Task 28 in the implementation plan for the complete E2E testing checklist.

Quick Smoke Test

  1. Start dev server: npm run dev
  2. Login: Navigate to /login and authenticate
  3. Reports: Click through all Reports menu items
  4. Data Entry: Navigate to Data Entry module
  5. Module Switching: Switch between modules - verify state preserved
  6. Logout: Verify redirect and state cleared

E2E Tests (Playwright)

# Run E2E tests
npm run test:e2e

Tests are located in tests/e2e/ (to be added).


📦 Build Output

Bundle Splitting

Production build creates optimized chunks:

dist/assets/
  vendor-core.[hash].js         # ~150KB - Vue, Router, Pinia
  vendor-primevue.[hash].js     # ~200KB - PrimeVue components
  vendor-utils.[hash].js        # ~80KB  - Axios, date-fns
  vendor-charts.[hash].js       # ~150KB - Chart.js (lazy)
  vendor-export.[hash].js       # ~200KB - XLSX, jsPDF (lazy)
  reports.[hash].js             # ~150KB - Reports module (lazy)
  data-entry.[hash].js          # ~100KB - Data Entry module (lazy)
  main.[hash].js                # ~50KB  - App shell
  main.[hash].css               # ~80KB  - Global CSS

Total initial load: ~500KB (gzipped: ~200KB)

  • Only loads core vendors + app shell
  • Modules load on-demand when navigated to

Performance Targets

  • Initial load: < 2 seconds (3G connection)
  • Module switching: < 500ms (cached) / < 1s (first load)
  • Lighthouse Performance Score: ≥ 90

🔧 Configuration Files

vite.config.js

  • Aliases: @, @shared, @reports, @data-entry
  • Proxy: Dual backend proxy for development
  • Build: Manual chunk splitting, source maps, cache busting

.env.example

Template for environment variables:

# API URLs (development - default values)
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

Copy to .env and customize as needed.

package.json

Merged dependencies from both original apps with de-duplication.


🐛 Troubleshooting

Dev Server Won't Start

Problem: npm run dev fails or shows EADDRINUSE error.

Solutions:

  • Check if port 3000 is already in use: netstat -ano | findstr :3000 (Windows) or lsof -i :3000 (Linux)
  • Kill the process or change port in vite.config.js

API Calls Fail (404 or CORS)

Problem: API requests return 404 or CORS errors.

Solutions:

  • Verify both backends are running (localhost:8001 and localhost:8003)
  • Check proxy configuration in vite.config.js
  • Verify API base URLs in module services (/api/reports, /api/data-entry)

Module Not Loading (Blank Screen)

Problem: Navigating to a route shows blank screen or loading spinner indefinitely.

Solutions:

  • Check browser console for errors
  • Verify route is defined in src/router/index.js
  • Check component imports use correct alias (@reports, @data-entry)
  • Check ErrorBoundary is not showing an error (inspect DOM)

Build Fails

Problem: npm run build fails with import errors.

Solutions:

  • Run npm install to ensure dependencies are up-to-date
  • Check for circular dependencies in imports
  • Verify all import paths use configured aliases
  • Check for missing files referenced in imports

Styles Not Applied

Problem: Components render but styles are missing or wrong.

Solutions:

  • Verify src/assets/css/main.css is imported in main.js
  • Check CSS files exist in src/assets/css/
  • Clear browser cache (Ctrl+Shift+R / Cmd+Shift+R)
  • Check PrimeVue theme is imported (primevue/resources/themes/saga-blue/theme.css)

📚 Additional Documentation

For more detailed information, see:

  • CLAUDE.md - Complete project overview and development guide
  • docs/ONBOARDING_CSS.md - CSS system quick start (5 minutes)
  • docs/CSS_PATTERNS.md - Complete CSS patterns library
  • docs/ARCHITECTURE_SCHEMA.md - Architecture diagrams and schemas
  • .auto-build/specs/unified-app/spec.md - Feature specification
  • .auto-build/specs/unified-app/plan.md - Implementation plan

📄 License

Internal use only - ROA2WEB ERP System


📞 Support

For issues or questions, contact the development team or check project documentation in docs/.


Version: 1.0 Last Updated: 2025-12-22 Build Status: Production Ready