Files
roa2web-service-auto/reports-app/frontend/src/stores/dashboard.js
Marius Mutu 12ac2b671e feat: Frontend CSS refactoring and test improvements
Frontend:
- Refactored CSS architecture with new utility classes
- Updated dashboard components styling
- Improved responsive grid system
- Enhanced typography and variables
- Updated E2E and integration tests

Added:
- Claude Code slash commands for validation
- SSH tunnel and start test scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 21:08:47 +02:00

509 lines
14 KiB
JavaScript

import { defineStore } from "pinia";
import { ref } from "vue";
import { apiService } from "../services/api";
export const useDashboardStore = defineStore("dashboard", () => {
// State existent
const summary = ref(null);
const trends = ref(null);
const isLoading = ref(false);
const error = ref(null);
// State nou pentru carduri
const performanceData = ref({});
const cashflowData = ref({});
const maturityData = ref({});
const currentPeriod = ref(null);
// State pentru detailed data pagination
const detailedDataTotal = ref(0);
// Cache pentru date
const dataCache = new Map();
const loadDashboardSummary = async (companyId) => {
isLoading.value = true;
error.value = null;
try {
const response = await apiService.get("/dashboard/summary", {
params: { company: companyId },
});
summary.value = response.data;
return { success: true };
} catch (err) {
error.value = err.response?.data?.detail || "Failed to load dashboard";
console.error("Failed to load dashboard:", err);
return { success: false, error: error.value };
} finally {
isLoading.value = false;
}
};
const loadTrendData = async (
companyId,
period = "12m",
chartType = "line",
) => {
isLoading.value = true;
error.value = null;
try {
console.log(
`Loading trend data for company ${companyId}, period: ${period}`,
);
const response = await apiService.get("/dashboard/trends", {
params: {
company: companyId,
period: period,
},
});
// Validate response structure
if (!response.data) {
throw new Error("Empty response from trends API");
}
console.log("Raw trends response:", response.data);
// Transform backend response to Chart.js format
const backendData = response.data;
const transformedData = transformTrendsData(backendData);
if (!transformedData) {
throw new Error("Failed to transform trends data - invalid format");
}
trends.value = transformedData;
console.log("Transformed trends data:", transformedData);
return { success: true, data: transformedData };
} catch (err) {
const errorMessage =
err.response?.data?.detail ||
err.message ||
"Failed to load trend data";
error.value = errorMessage;
console.error("Failed to load trend data:", err);
console.error("Error details:", {
status: err.response?.status,
statusText: err.response?.statusText,
data: err.response?.data,
});
// Clear trends data and return error - no more mock data
trends.value = null;
return { success: false, error: error.value };
} finally {
isLoading.value = false;
}
};
// Transform backend trends data to Chart.js format AND preserve raw data
const transformTrendsData = (backendData) => {
if (
!backendData ||
!backendData.periods ||
!Array.isArray(backendData.periods) ||
backendData.periods.length === 0
) {
console.warn("Invalid trends data received:", backendData);
return null;
}
// Validate that we have all required data
const requiredFields = [
"trezorerie_sold",
"clienti_sold",
"furnizori_sold",
"clienti_incasat",
"furnizori_achitat",
];
for (const field of requiredFields) {
if (!backendData[field] || !Array.isArray(backendData[field])) {
console.warn(`Missing ${field} data`);
return null;
}
}
// Data is already in ASC order from backend
const periods = [...backendData.periods];
// Format labels for monthly data (YYYY-MM -> MM/YYYY)
const formattedPeriods = periods.map((period) => {
const [year, month] = period.split("-");
const date = new Date(year, month - 1);
return date.toLocaleDateString("ro-RO", {
month: "2-digit",
year: "numeric",
});
});
// Preserve all raw data from backend for card calculations
return {
labels: formattedPeriods,
raw: {
// Current period data
periods: backendData.periods,
clienti_facturat: backendData.clienti_facturat || [],
clienti_incasat: backendData.clienti_incasat || [],
clienti_sold: backendData.clienti_sold || [],
furnizori_facturat: backendData.furnizori_facturat || [],
furnizori_achitat: backendData.furnizori_achitat || [],
furnizori_sold: backendData.furnizori_sold || [],
trezorerie_sold: backendData.trezorerie_sold || [],
// Previous period data (year-over-year comparison)
previous_periods: backendData.previous_periods || [],
clienti_facturat_prev: backendData.clienti_facturat_prev || [],
clienti_incasat_prev: backendData.clienti_incasat_prev || [],
clienti_sold_prev: backendData.clienti_sold_prev || [],
furnizori_facturat_prev: backendData.furnizori_facturat_prev || [],
furnizori_achitat_prev: backendData.furnizori_achitat_prev || [],
furnizori_sold_prev: backendData.furnizori_sold_prev || [],
trezorerie_sold_prev: backendData.trezorerie_sold_prev || [],
},
datasets: [
{
label: "Trezorerie - Sold Net",
data: [...backendData.trezorerie_sold].map((val) => Number(val) || 0),
borderColor: "rgb(59, 130, 246)",
backgroundColor: "rgba(59, 130, 246, 0.1)",
tension: 0.4,
fill: false,
pointBackgroundColor: "rgb(59, 130, 246)",
pointBorderColor: "#ffffff",
pointBorderWidth: 2,
pointRadius: 4,
pointHoverRadius: 6,
},
],
};
};
const loadDetailedData = async (
dataType,
companyId,
page = 1,
pageSize = 25,
search = "",
) => {
isLoading.value = true;
error.value = null;
try {
const response = await apiService.get("/dashboard/detailed-data", {
params: {
company: companyId,
data_type: dataType,
page: page,
page_size: pageSize,
search: search,
},
});
// Store total for pagination
detailedDataTotal.value = response.data.total || 0;
return {
success: true,
data: response.data.data || [], // Backend returns 'data' not 'items'
total: response.data.total || 0,
page: response.data.page || 1,
};
} catch (err) {
error.value =
err.response?.data?.detail || "Failed to load detailed data";
console.error("Failed to load detailed data:", err);
// Return mock data structure for testing
const mockData = generateMockDetailedData(dataType);
detailedDataTotal.value = mockData.length;
return {
success: false,
error: error.value,
data: mockData,
total: mockData.length,
page: 1,
};
} finally {
isLoading.value = false;
}
};
// Generate mock data for testing until backend endpoint is implemented
const generateMockDetailedData = (dataType) => {
switch (dataType) {
case "clients":
return [
{
id: 1,
client: "SC ALPHA SRL",
facturat: 15000,
incasat: 12000,
sold: 3000,
status: "Activ",
},
{
id: 2,
client: "SC BETA SRL",
facturat: 8500,
incasat: 8500,
sold: 0,
status: "Activ",
},
{
id: 3,
client: "SC GAMMA SRL",
facturat: 22000,
incasat: 15000,
sold: 7000,
status: "Activ",
},
{
id: 4,
client: "SC DELTA SRL",
facturat: 5500,
incasat: 2000,
sold: 3500,
status: "Întârziere",
},
{
id: 5,
client: "SC EPSILON SRL",
facturat: 18000,
incasat: 18000,
sold: 0,
status: "Activ",
},
];
case "suppliers":
return [
{
id: 1,
furnizor: "SC SUPPLIER A SRL",
facturat: 12000,
achitat: 10000,
sold: 2000,
status: "Activ",
},
{
id: 2,
furnizor: "SC SUPPLIER B SRL",
facturat: 7500,
achitat: 7500,
sold: 0,
status: "Activ",
},
{
id: 3,
furnizor: "SC SUPPLIER C SRL",
facturat: 19000,
achitat: 12000,
sold: 7000,
status: "Pendente",
},
{
id: 4,
furnizor: "SC SUPPLIER D SRL",
facturat: 4200,
achitat: 4200,
sold: 0,
status: "Activ",
},
{
id: 5,
furnizor: "SC SUPPLIER E SRL",
facturat: 16800,
achitat: 8000,
sold: 8800,
status: "Pendente",
},
];
case "treasury":
return [
{
id: 1,
cont: "5121",
nume_cont: "Cont curent BCR",
sold: 45000,
valuta: "RON",
tip: "Bancă",
},
{
id: 2,
cont: "5311",
nume_cont: "Casa RON",
sold: 2500,
valuta: "RON",
tip: "Numerar",
},
{
id: 3,
cont: "5124",
nume_cont: "Cont curent BRD EUR",
sold: 8500,
valuta: "EUR",
tip: "Bancă",
},
{
id: 4,
cont: "5125",
nume_cont: "Cont economii ING",
sold: 125000,
valuta: "RON",
tip: "Economii",
},
{
id: 5,
cont: "5312",
nume_cont: "Casa valută",
sold: 500,
valuta: "EUR",
tip: "Numerar",
},
];
default:
return [];
}
};
// Funcții noi pentru carduri
const loadPerformanceData = async (companyId, period = "7d") => {
const cacheKey = `performance-${companyId}-${period}`;
// Check cache
if (dataCache.has(cacheKey)) {
performanceData.value[period] = dataCache.get(cacheKey);
return { success: true, data: dataCache.get(cacheKey) };
}
try {
const response = await apiService.get("/dashboard/performance", {
params: { company: companyId, period },
});
performanceData.value[period] = response.data;
dataCache.set(cacheKey, response.data);
return { success: true, data: response.data };
} catch (err) {
console.error("Failed to load performance data:", err);
return { success: false, error: err.message };
}
};
const loadCashFlowData = async (companyId, period = "7d") => {
const cacheKey = `cashflow-${companyId}-${period}`;
if (dataCache.has(cacheKey)) {
cashflowData.value[period] = dataCache.get(cacheKey);
return { success: true, data: dataCache.get(cacheKey) };
}
try {
const response = await apiService.get("/dashboard/cashflow", {
params: { company: companyId, period },
});
cashflowData.value[period] = response.data;
dataCache.set(cacheKey, response.data);
return { success: true, data: response.data };
} catch (err) {
console.error("Failed to load cashflow data:", err);
return { success: false, error: err.message };
}
};
const loadMaturityData = async (companyId, period = "7d") => {
const cacheKey = `maturity-${companyId}-${period}`;
if (dataCache.has(cacheKey)) {
maturityData.value[period] = dataCache.get(cacheKey);
return { success: true, data: dataCache.get(cacheKey) };
}
try {
const response = await apiService.get("/dashboard/maturity", {
params: { company: companyId, period },
});
maturityData.value[period] = response.data;
dataCache.set(cacheKey, response.data);
return { success: true, data: response.data };
} catch (err) {
console.error("Failed to load maturity data:", err);
return { success: false, error: err.message };
}
};
const loadCurrentPeriod = async (companyId) => {
try {
const response = await apiService.get("/dashboard/current-period", {
params: { company: companyId },
});
currentPeriod.value = response.data;
return { success: true, data: response.data };
} catch (err) {
console.error("Failed to load current period:", err);
// Fallback to current date if API fails
const now = new Date();
const fallbackPeriod = {
year: now.getFullYear(),
month: now.getMonth() + 1,
period: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`,
};
currentPeriod.value = fallbackPeriod;
return { success: false, error: err.message, data: fallbackPeriod };
}
};
// Clear cache
const clearCache = () => {
dataCache.clear();
};
const reset = () => {
summary.value = null;
trends.value = null;
isLoading.value = false;
error.value = null;
// Clear new data as well
performanceData.value = {};
cashflowData.value = {};
maturityData.value = {};
currentPeriod.value = null;
clearCache();
};
return {
// Existing
summary,
trends,
isLoading,
error,
loadDashboardSummary,
loadTrendData,
loadDetailedData,
reset,
// New
performanceData,
cashflowData,
maturityData,
currentPeriod,
loadPerformanceData,
loadCashFlowData,
loadMaturityData,
loadCurrentPeriod,
clearCache,
// Detailed data pagination
detailedDataTotal,
};
});