export const CHART_ENDPOINTS = { costiRicavi: '/dashboard/costi-ricavi', costiRicaviCum: '/dashboard/costi-ricavi', fatturatoCliente: '/dashboard/fatturato-cliente', aperteChiuseCliente: '/dashboard/aperte-chiuse-cliente', aperteChiuseFornitore: '/dashboard/aperte-chiuse-fornitore', fatturatoSoa: '/dashboard/fatturato-soa', apertoCliente: '/dashboard/partite-cliente', chiusoCliente: '/dashboard/partite-cliente', marginalitaMediaSoa: '/dashboard/marginalita-media-soa', marginalitaCategoriaSoa: '/dashboard/aggregati-soa', pesoFatturatoSoa: '/dashboard/aggregati-soa' }; export const CHART_DATA_KEY: Record = { costiRicaviCum: 'costiRicavi', apertoCliente: 'partiteCliente', chiusoCliente: 'partiteCliente', marginalitaCategoriaSoa: 'aggregatiSoa', pesoFatturatoSoa: 'aggregatiSoa' }; export const cumulate = (arr: number[]): number[] => { let sum = 0; return (arr || []).map(v => (sum += (Number(v) || 0))); }; export const formatEuro = (val: string | number): string => { const num = parseFloat(val as string); return (isNaN(num) ? 0 : num).toLocaleString('it-IT', { style: 'currency', currency: 'EUR' }); }; const TOOLTIP_BASE = { renderMode: 'richText', confine: true, textStyle: { fontSize: 10 } }; const wrapText = (text: string, maxChars = 30): string => { const words = String(text).split(' '); const lines = []; let line = ''; words.forEach(word => { if (line && (line.length + 1 + word.length) > maxChars) { lines.push(line); line = word; } else { line = line ? `${line} ${word}` : word; } }); if (line) { lines.push(line); } return lines.join('\n'); }; export const buildPieOption = (data: any) => ({ legend: { type: 'plain', top: 190, left: 'center', itemGap: 8, itemWidth: 14, itemHeight: 10, textStyle: { fontSize: 12 } }, series: [ { name: 'Fatturato', type: 'pie', radius: [45, 80], center: ['50%', 95], avoidLabelOverlap: false, itemStyle: { borderRadius: 5, borderColor: '#fff', borderWidth: 2 }, label: { show: false, position: 'center' }, emphasis: { label: { show: true, fontSize: 11, fontWeight: 'bold', formatter: (params: any) => { const val = (typeof params.value === 'number') ? params.value : 0; return `${params.name}\n${val.toLocaleString('it-IT', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 })}`; } } }, labelLine: { show: false }, data: data } ] }); export const buildSoaBarOption = (rows: any[], { color, valueLabel, extraLines }: any) => { const labels = rows.map(r => `${r.code} - ${r.name}`); return { grid: { left: 8, right: 60, top: 10, bottom: 10, containLabel: true }, xAxis: { type: 'value', axisLabel: { formatter: '{value}%', fontSize: 10 } }, yAxis: { type: 'category', data: rows.map(r => r.code), inverse: true, axisLabel: { interval: 0, fontSize: 10 } }, tooltip: { ...TOOLTIP_BASE, trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: (params: any) => { const p = params[0]; const row = rows[p.dataIndex]; const extra = extraLines ? extraLines(row) : []; return [ wrapText(labels[p.dataIndex]), `${valueLabel}: ${Number(p.value).toFixed(2)}%`, ...extra ].join('\n'); } }, series: [ { name: valueLabel, type: 'bar', data: rows.map(r => r.value), label: { show: true, position: 'right', formatter: (p: any) => `${Number(p.value).toFixed(2)}%`, fontSize: 10 }, itemStyle: { color: (p: any) => (p.value >= 0 ? color : '#ee6666'), borderRadius: [0, 4, 4, 0] } } ] }; }; export const commesseLine = (r: any) => [`Commesse: ${r.n_commesse}`]; export const buildAperteChiuseOption = (d: any) => ({ tooltip: { ...TOOLTIP_BASE, trigger: 'axis', valueFormatter: formatEuro }, legend: { bottom: 0, itemGap: 10, itemWidth: 14, itemHeight: 10, textStyle: { fontSize: 12 }, data: [ `Aperto(${d.current_year})`, `Chiuso(${d.current_year})`, `Aperto(${d.prev_year})`, `Chiuso(${d.prev_year})` ], selected: { [`Aperto(${d.prev_year})`]: false, [`Chiuso(${d.prev_year})`]: false }, }, grid: { left: '3%', right: '4%', top: '10%', bottom: 75, containLabel: true }, xAxis: { type: 'category', data: d.mesi }, yAxis: { type: 'value' }, series: [ { name: `Aperto(${d.current_year})`, type: 'bar', stack: 'one', data: d.aperto }, { name: `Chiuso(${d.current_year})`, type: 'bar', stack: 'one', data: d.chiuso }, { name: `Aperto(${d.prev_year})`, type: 'bar', stack: 'one', itemStyle: { color: '#b3b3b3' }, data: d.aperto_prev }, { name: `Chiuso(${d.prev_year})`, type: 'bar', stack: 'one', itemStyle: { color: '#d9d9d9' }, data: d.chiuso_prev } ] });