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:
Claude Agent
2026-01-22 07:27:27 +00:00
parent 69683b2d65
commit 1b9ebf1d8f
23 changed files with 4034 additions and 1396 deletions

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from typing import Optional
# import sys # Removed - no longer needed
import os
from shared.auth.dependencies import get_current_user
@@ -290,6 +289,7 @@ async def get_maturity_analysis(
@router.get("/monthly-flows")
async def get_monthly_flows(
request: Request,
company: int = Query(..., description="ID-ul firmei"),
luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"),
an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"),
@@ -301,15 +301,31 @@ async def get_monthly_flows(
- Necesită autentificare JWT
- Returnează date pentru analiza fluxurilor lunare
- luna/an: perioada contabilă de referință (dacă nu sunt specificate, folosește ultima perioadă)
- Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header)
"""
try:
# Verifică dacă utilizatorul are acces la firma specificată
if str(company) not in current_user.companies:
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
result = await DashboardService.get_monthly_flows(company, luna=luna, an=an)
return result
# Apelăm serviciul cu request pentru cache metadata
result = await DashboardService.get_monthly_flows(company, luna=luna, an=an, request=request)
# Convert to dict if needed
result_dict = result.dict() if hasattr(result, 'dict') else result
# Add cache metadata if requested (for Telegram Bot / Dashboard)
include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true'
if include_metadata:
cache_hit = getattr(request.state, 'cache_hit', False)
response_time = getattr(request.state, 'response_time_ms', 0)
cache_source = getattr(request.state, 'cache_source', None)
result_dict['cache_hit'] = cache_hit
result_dict['response_time_ms'] = response_time
result_dict['cache_source'] = cache_source
return result_dict
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
@@ -435,7 +451,6 @@ async def get_current_period(
@router.get(
"/financial-indicators",
response_model=FinancialIndicatorsResponse,
tags=["dashboard"]
)
async def get_financial_indicators(
@@ -445,7 +460,7 @@ async def get_financial_indicators(
an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"),
include_sparklines: bool = Query(True, description="Include date istorice pentru sparklines (12 luni)"),
current_user: CurrentUser = Depends(get_current_user)
) -> FinancialIndicatorsResponse:
):
"""
Returnează toți indicatorii financiari calculați pentru firma selectată.
@@ -504,15 +519,25 @@ async def get_financial_indicators(
# Dacă include_sparklines este True, folosim metoda care include datele istorice
if include_sparklines:
response = await FinancialIndicatorsService.get_indicators_with_sparklines(
company, resolved_luna, resolved_an, months=12
company, resolved_luna, resolved_an, months=12, request=request
)
logger.info(
f"Financial indicators with sparklines for company {company}, "
f"luna={resolved_luna}, an={resolved_an}: "
f"Z-Score={response.altman_zscore.zscore.value} ({response.altman_zscore.zscore.status})"
f"Z-Score={response.altman_zscore.zscore.value} ({response.altman_zscore.zscore.status}), "
f"cache_hit={getattr(request.state, 'cache_hit', False)}, "
f"response_time={getattr(request.state, 'response_time_ms', 0):.1f}ms"
)
# Add cache metadata if requested (for Telegram Bot / Dashboard)
include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true'
if include_metadata:
result_dict = response.dict() if hasattr(response, 'dict') else response
result_dict['cache_hit'] = getattr(request.state, 'cache_hit', False)
result_dict['response_time_ms'] = getattr(request.state, 'response_time_ms', 0)
result_dict['cache_source'] = getattr(request.state, 'cache_source', None)
return result_dict
return response
# Dacă include_sparklines este False, calculăm doar indicatorii curenți
@@ -537,6 +562,12 @@ async def get_financial_indicators(
altman_task = FinancialIndicatorsService.calculate_altman_zscore(
company, resolved_luna, resolved_an
)
profitabilitate_task = FinancialIndicatorsService.calculate_profitability_indicators(
company, resolved_luna, resolved_an
)
solvabilitate_task = FinancialIndicatorsService.calculate_solvability_indicators(
company, resolved_luna, resolved_an
)
# Executăm toate calculele în paralel pentru performanță
(
@@ -545,14 +576,18 @@ async def get_financial_indicators(
risc,
cash_flow,
dinamica,
altman_zscore
altman_zscore,
profitabilitate,
solvabilitate
) = await asyncio.gather(
lichiditate_task,
eficienta_task,
risc_task,
cash_flow_task,
dinamica_task,
altman_task
altman_task,
profitabilitate_task,
solvabilitate_task
)
# Construim răspunsul
@@ -562,7 +597,9 @@ async def get_financial_indicators(
risc=risc,
cash_flow=cash_flow,
dinamica=dinamica,
altman_zscore=altman_zscore
altman_zscore=altman_zscore,
profitabilitate=profitabilitate,
solvabilitate=solvabilitate
)
logger.info(
@@ -570,6 +607,14 @@ async def get_financial_indicators(
f"Z-Score={altman_zscore.zscore.value} ({altman_zscore.zscore.status})"
)
# Add cache metadata if requested (for Telegram Bot / Dashboard)
include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true'
if include_metadata:
result_dict = response.dict() if hasattr(response, 'dict') else response
result_dict['cache_hit'] = getattr(request.state, 'cache_hit', False)
result_dict['response_time_ms'] = getattr(request.state, 'response_time_ms', 0)
result_dict['cache_source'] = getattr(request.state, 'cache_source', None)
return result_dict
return response
except ValueError as e: