feat(dashboard): Complete dashboard desktop cleanup and improvements
User Stories Completed: - US-001: Eliminare SolduriCompactCard de pe Desktop - US-002: Eliminare Icoane din Header-ul CollapsibleCard - US-003: Reorganizare TreasuryDualCard - Text Înainte de Grafice - US-004: Reorganizare ClientiBalanceCard - Text Înainte de Grafice - US-005: Reorganizare FurnizoriBalanceCard - Text Înainte de Grafice - US-006: Grafice Colapsabile în TreasuryDualCard - US-007: Grafice Colapsabile în ClientiBalanceCard - US-008: Grafice Colapsabile în FurnizoriBalanceCard - US-009: Grafice Colapsabile în CashFlowMetricCard Additional Improvements: - Add cache metadata display (CacheFooter component) for all dashboard cards - Add @cached decorators to get_monthly_flows and get_indicators_with_sparklines - Fix financial indicators calculations and sparkline sync - Add state reset on company change to prevent stale data - New shared components: CacheFooter.vue, authRedirect.js - Enhanced FinancialIndicatorsCard with sparklines and period selection Squashed from branch: ralph/dashboard-desktop-cleanup (11 commits) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
253
.claude/plans/immutable-chasing-flute.md
Normal file
253
.claude/plans/immutable-chasing-flute.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Plan: Curățare Cod ROA2WEB (High ROI, Low Risk)
|
||||
|
||||
## Sumar Executiv
|
||||
|
||||
Arhitectura ROA2WEB este **solidă** pentru o echipă de 1-2 developeri. Nu necesită schimbări arhitecturale.
|
||||
|
||||
Acest plan conține doar **optimizări tactice sigure** cu randament mare.
|
||||
|
||||
---
|
||||
|
||||
## Acțiuni de Curățare (Prioritizate)
|
||||
|
||||
### 1. Elimină Store Duplication ✅
|
||||
**ROI**: ⭐⭐⭐⭐⭐ | **Risc**: Foarte Scăzut | **Efort**: 15 min
|
||||
|
||||
**Problema**: `sharedStores.js` identic în 2 module (42 linii duplicate)
|
||||
|
||||
**Fișiere de șters**:
|
||||
- `src/modules/reports/stores/sharedStores.js`
|
||||
- `src/modules/data-entry/stores/sharedStores.js`
|
||||
|
||||
**Soluție**: Instantiază stores direct în `App.vue`
|
||||
|
||||
```javascript
|
||||
// App.vue
|
||||
import { createAuthStore } from '@shared/stores/auth'
|
||||
import { createCompaniesStore } from '@shared/stores/companies'
|
||||
import { createAccountingPeriodStore } from '@shared/stores/accountingPeriod'
|
||||
import authApi from '@shared/services/authApi'
|
||||
|
||||
const useAuthStore = createAuthStore(authApi)
|
||||
const useCompanyStore = createCompaniesStore(authApi, useAuthStore)
|
||||
const useAccountingPeriodStore = createAccountingPeriodStore(authApi)
|
||||
```
|
||||
|
||||
**Verificare**: App funcționează normal, stores disponibile în componente
|
||||
|
||||
---
|
||||
|
||||
### 2. Factory pentru API Services ✅
|
||||
**ROI**: ⭐⭐⭐⭐ | **Risc**: Scăzut | **Efort**: 30 min
|
||||
|
||||
**Problema**: `api.js` duplicat în module (70% cod identic, 156 linii total)
|
||||
|
||||
**Fișier de creat**: `src/shared/services/createApiService.js`
|
||||
|
||||
```javascript
|
||||
// createApiService.js (~50 linii)
|
||||
import axios from 'axios'
|
||||
|
||||
export function createApiService(basePath, options = {}) {
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.BASE_URL + `api/${basePath}`,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
})
|
||||
|
||||
api.interceptors.request.use(config => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`
|
||||
|
||||
if (options.injectCompany) {
|
||||
// Logic pentru X-Selected-Company header
|
||||
}
|
||||
|
||||
if (config.data instanceof FormData) {
|
||||
delete config.headers['Content-Type']
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
api.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('access_token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
return api
|
||||
}
|
||||
```
|
||||
|
||||
**Fișiere simplificate** (5 linii fiecare):
|
||||
```javascript
|
||||
// src/modules/reports/services/api.js
|
||||
import { createApiService } from '@shared/services/createApiService'
|
||||
export default createApiService('reports')
|
||||
|
||||
// src/modules/data-entry/services/api.js
|
||||
import { createApiService } from '@shared/services/createApiService'
|
||||
export default createApiService('data-entry', { injectCompany: true })
|
||||
```
|
||||
|
||||
**Verificare**: Login funcționează, API calls returnează date
|
||||
|
||||
---
|
||||
|
||||
### 3. Dependențe OCR Opționale (Lazy Loading) ✅
|
||||
**ROI**: ⭐⭐⭐⭐ | **Risc**: Foarte Scăzut | **Efort**: 30 min
|
||||
|
||||
**Problema**: PaddleOCR + pytesseract se instalează și încarcă mereu, chiar dacă nu sunt folosite
|
||||
|
||||
**Soluție**: Fă-le opționale via `.env` și lazy loading
|
||||
|
||||
**Fișier `.env`** - adaugă:
|
||||
```env
|
||||
# OCR Engines (true = instalează și încarcă, false = skip)
|
||||
OCR_ENABLE_PADDLEOCR=false
|
||||
OCR_ENABLE_TESSERACT=false
|
||||
```
|
||||
|
||||
**Fișier `backend/requirements.txt`** - mută în secțiune opțională:
|
||||
```
|
||||
# Required OCR
|
||||
python-doctr[torch]>=0.8.0
|
||||
|
||||
# Optional OCR (install only if needed)
|
||||
# paddleocr>=2.7.0 # Uncomment if OCR_ENABLE_PADDLEOCR=true
|
||||
# paddlepaddle>=2.5.0 # Uncomment if OCR_ENABLE_PADDLEOCR=true
|
||||
# pytesseract>=0.3.10 # Uncomment if OCR_ENABLE_TESSERACT=true
|
||||
```
|
||||
|
||||
**SAU** creează `requirements-ocr-optional.txt`:
|
||||
```
|
||||
paddleocr>=2.7.0
|
||||
paddlepaddle>=2.5.0
|
||||
pytesseract>=0.3.10
|
||||
```
|
||||
|
||||
**Fișier OCR service** - lazy import:
|
||||
```python
|
||||
# backend/modules/data_entry/services/ocr_service.py
|
||||
import os
|
||||
from functools import lru_cache
|
||||
|
||||
@lru_cache()
|
||||
def get_paddleocr():
|
||||
if os.getenv('OCR_ENABLE_PADDLEOCR', 'false').lower() == 'true':
|
||||
from paddleocr import PaddleOCR
|
||||
return PaddleOCR(use_angle_cls=True, lang='ro')
|
||||
return None
|
||||
|
||||
@lru_cache()
|
||||
def get_tesseract():
|
||||
if os.getenv('OCR_ENABLE_TESSERACT', 'false').lower() == 'true':
|
||||
import pytesseract
|
||||
return pytesseract
|
||||
return None
|
||||
```
|
||||
|
||||
**Beneficiu**:
|
||||
- Păstrează alternativele pentru viitor
|
||||
- Nu se instalează/încarcă dacă nu sunt necesare
|
||||
- Startup mai rapid când sunt dezactivate
|
||||
- ~500MB saved când `false`
|
||||
|
||||
**Verificare**:
|
||||
- Cu `false`: App pornește fără PaddleOCR/Tesseract instalate
|
||||
- Cu `true`: OCR fallback funcționează
|
||||
|
||||
---
|
||||
|
||||
### 4. Consolidare Design Tokens CSS ✅
|
||||
**ROI**: ⭐⭐⭐ | **Risc**: Scăzut | **Efort**: 1 oră
|
||||
|
||||
**Problema**: 3 fișiere cu tokens overlap: `variables.css`, `tokens.css`, `md3-tokens.css`
|
||||
|
||||
**Soluție**: Consolidează în `src/assets/css/core/design-tokens.css`
|
||||
|
||||
**Pași**:
|
||||
1. Creează `design-tokens.css` unificat
|
||||
2. Migrează variabilele din cele 3 fișiere
|
||||
3. Actualizează importurile în `main.css`
|
||||
4. Șterge fișierele vechi
|
||||
|
||||
**Verificare**: Testează light mode ȘI dark mode pe toate paginile
|
||||
|
||||
---
|
||||
|
||||
## Acțiuni Opționale (Dacă Ai Timp)
|
||||
|
||||
### 5. Split receiptStore (Opțional)
|
||||
**ROI**: ⭐⭐⭐ | **Risc**: Mediu | **Efort**: 2-3 ore
|
||||
|
||||
**Problema**: 606 linii într-un singur store
|
||||
|
||||
**Soluție**: Split în 3 stores (receipts, workflow, nomenclatures)
|
||||
|
||||
**De făcut doar dacă**: Lucrezi frecvent pe data-entry module
|
||||
|
||||
---
|
||||
|
||||
### 6. Simplificare Cache (Opțional)
|
||||
**ROI**: ⭐⭐ | **Risc**: Mediu | **Efort**: 2-3 ore
|
||||
|
||||
**Problema**: 7 fișiere cache, unele nefolosite
|
||||
|
||||
**Soluție**: Reduce la 3 fișiere
|
||||
|
||||
**De făcut doar dacă**: Ai nevoie să modifici cache logic
|
||||
|
||||
---
|
||||
|
||||
## Impact Total (Acțiuni 1-4)
|
||||
|
||||
| Metrică | Înainte | După | Diferență |
|
||||
|---------|---------|------|-----------|
|
||||
| Linii cod duplicat | ~200 | ~50 | **-150** |
|
||||
| OCR deps obligatorii | 3 | 0 | **opționale via .env** |
|
||||
| Fișiere CSS tokens | 3 | 1 | **-2** |
|
||||
| Store duplication | 42 linii | 0 | **-42** |
|
||||
| Startup time (OCR off) | ~5s | ~2s | **-3s** |
|
||||
| **Timp total** | - | - | **~2.5 ore** |
|
||||
|
||||
---
|
||||
|
||||
## Ordinea Recomandată
|
||||
|
||||
1. **[15 min]** Elimină store duplication
|
||||
2. **[30 min]** Factory pentru API services
|
||||
3. **[30 min]** OCR dependencies opționale (lazy loading)
|
||||
4. **[1 oră]** Consolidare CSS tokens
|
||||
|
||||
**Total**: ~2.5 ore pentru toate 4 acțiunile principale
|
||||
|
||||
---
|
||||
|
||||
## Verificare Finală
|
||||
|
||||
După fiecare acțiune:
|
||||
- [ ] App pornește fără erori
|
||||
- [ ] Login funcționează
|
||||
- [ ] Un raport se încarcă
|
||||
- [ ] O chitanță se creează
|
||||
- [ ] Dark mode arată corect
|
||||
|
||||
---
|
||||
|
||||
## Ce NU Schimbăm
|
||||
|
||||
- ✅ Arhitectura Layered - potrivită pentru echipa de 1-2 devs
|
||||
- ✅ Module isolation (reports, data-entry, telegram)
|
||||
- ✅ Router/Store Factory patterns
|
||||
- ✅ Auth middleware
|
||||
- ✅ Cache decorator `@cached`
|
||||
- ✅ Oracle pool singleton
|
||||
|
||||
---
|
||||
|
||||
*Plan simplificat: doar curățare cod cu randament mare și risc minim.*
|
||||
Reference in New Issue
Block a user