Enhance data processing: add utility functions for year extraction and graph data population

This commit is contained in:
Giuliano Paschoalino 2026-01-19 17:14:50 -03:00
parent 76ad6d53ad
commit 43a686bb3b
11 changed files with 428 additions and 53 deletions

View File

@ -79,6 +79,8 @@ export function CativoXLivreChart({ title, subtitle, chartData, label, dataset1,
const options: any = config(miniature) const options: any = config(miniature)
const hasEstimated = chartData?.some((value) => value.dad_estimado)
const data: any = { const data: any = {
labels, labels,
datasets: chartData?.map(value => value.dad_estimado)?.includes(true) ? [ datasets: chartData?.map(value => value.dad_estimado)?.includes(true) ? [
@ -184,7 +186,7 @@ export function CativoXLivreChart({ title, subtitle, chartData, label, dataset1,
return ( return (
<CativoXLivreChartView> <CativoXLivreChartView>
<ChartTitle title={title} subtitle={subtitle}/> {/* <ChartTitle title={title} subtitle={subtitle}/> */}
<div> <div>
<Chart ref={chartRef} type='bar' options={options} data={data} /> <Chart ref={chartRef} type='bar' options={options} data={data} />
</div> </div>

View File

@ -80,7 +80,7 @@ export default function CostIndicatorChart({ title, data1, data2, label, subtitl
return ( return (
<CostIndicatorChartView> <CostIndicatorChartView>
<ChartTitle title={title} subtitle={subtitle} /> {/* <ChartTitle title={title} subtitle={subtitle} /> */}
<Bar <Bar
options={options} options={options}
data={data} data={data}

View File

@ -1,10 +1,9 @@
import { BarElement, CategoryScale, Chart as ChartJS, layouts, Legend, LinearScale, Title, Tooltip } from 'chart.js'; import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, Title, Tooltip } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels'; import ChartDataLabels from 'chartjs-plugin-datalabels';
import 'chartjs-plugin-style'; import 'chartjs-plugin-style';
import { draw } from 'patternomaly'; import { draw } from 'patternomaly';
import { Chart } from 'react-chartjs-2'; import { Chart } from 'react-chartjs-2';
import ChartTitle from '../ChartTitle';
import { GrossAnualChartView } from './GrossAnualChartView'; import { GrossAnualChartView } from './GrossAnualChartView';
ChartJS.register( ChartJS.register(
@ -40,7 +39,7 @@ export function GrossAnualChart({ title, subtitle, dataProps = [], label, datase
// maintainAspectRatio: false, // maintainAspectRatio: false,
layout: { layout: {
padding: { padding: {
top: 50, top: 0,
} }
}, },
scales: { scales: {
@ -56,7 +55,7 @@ export function GrossAnualChart({ title, subtitle, dataProps = [], label, datase
}, },
}, },
y: { y: {
stacked: false, stacked: true,
//max: Number.parseInt(dataProps.reduce((prev, current) => prev.economia_acumulada < current.economia_acumulada ? prev.economia_acumulada : current.economia_acumulada, 0)) + 350, //max: Number.parseInt(dataProps.reduce((prev, current) => prev.economia_acumulada < current.economia_acumulada ? prev.economia_acumulada : current.economia_acumulada, 0)) + 350,
min: 0, min: 0,
grid: { grid: {
@ -79,26 +78,28 @@ export function GrossAnualChart({ title, subtitle, dataProps = [], label, datase
}, },
plugins: { plugins: {
datalabels: { datalabels: {
display: true,
color: '#255488',
clip: false,
formatter: (value, ctx) => { formatter: (value, ctx) => {
const percentage = (dataProps[ctx.dataIndex]?.econ_percentual * 100).toFixed(0) + "%"; const percentage = (dataProps[ctx.dataIndex]?.econ_percentual * 100).toFixed(0) + "%";
const result = `${spacement(parseInt(value).toLocaleString('pt-br'))}${percentage}\n${parseInt(value).toLocaleString('pt-br')}${spacement(parseInt(value).toLocaleString('pt-br'))}` const result = `${spacement(parseInt(value).toLocaleString('pt-br'))}${percentage}\n${parseInt(value).toLocaleString('pt-br')}${spacement(parseInt(value).toLocaleString('pt-br'))}`
return value == null ? null : result return value == null ? null : result
}, },
display: true, anchor: 'end',
anchor: "end", align: 'end',
align: "end", offset: 5,
font: { font: {
weight: 'bold', weight: 'bold',
size: !miniature ? window.innerWidth / 80 : window.innerWidth / 125, size: !miniature ? window.innerWidth / 80 : window.innerWidth / 125,
}, },
color: '#255488',
}, },
legend: { legend: {
position: 'bottom' as const, position: 'bottom' as const,
}, },
title: { title: {
display: false, display: true,
text: '', text: '',
}, },
}, },
@ -126,7 +127,7 @@ export function GrossAnualChart({ title, subtitle, dataProps = [], label, datase
{ {
type: 'bar', type: 'bar',
label: dataset, label: dataset,
stacked: true, // stacked: true,
data: consolidatedData, data: consolidatedData,
datalabels: { datalabels: {
// backgroundColor: '#255488', // backgroundColor: '#255488',
@ -134,19 +135,19 @@ export function GrossAnualChart({ title, subtitle, dataProps = [], label, datase
// opacity: .8, // opacity: .8,
display: (ctx) => ctx.dataIndex === 0, // Exibe apenas o primeiro display: (ctx) => ctx.dataIndex === 0, // Exibe apenas o primeiro
}, },
borderRadius: 10, skipNull: true,
borderRadius: 8,
backgroundColor: '#255488', backgroundColor: '#255488',
}, },
{ {
type: 'bar', type: 'bar',
stacked: true,
label: 'Estimado', label: 'Estimado',
spanGaps: true,
datalabels: { datalabels: {
// keep the previous behaviour of offsetting the second estimated bar if needed // keep the previous behaviour of offsetting the second estimated bar if needed
}, },
data: estimatedData, data: estimatedData,
borderRadius: 10, skipNull: true,
borderRadius: 8,
backgroundColor: draw('diagonal-right-left', '#C2d5fb'), backgroundColor: draw('diagonal-right-left', '#C2d5fb'),
}, },
], ],
@ -154,8 +155,7 @@ export function GrossAnualChart({ title, subtitle, dataProps = [], label, datase
return ( return (
<GrossAnualChartView> <GrossAnualChartView>
<Chart options={options} data={data} type='bar' height={'156'} />
<Chart options={options} data={data} type='bar' height={150} />
</GrossAnualChartView> </GrossAnualChartView>
) )
} }

View File

@ -3,21 +3,23 @@ import styled from "styled-components"
export const GrossAnualChartView = styled.div` export const GrossAnualChartView = styled.div`
width: 90%; width: 90%;
transform: translateY(-25px);
@media (max-width: 900px) { @media (max-width: 900px) {
min-width: 20rem min-width: 20rem
} }
` `
export const ChartTitleView = styled.div` // export const ChartTitleView = styled.div`
display: flex; // display: flex;
justify-content: center; // justify-content: center;
align-items: center; // align-items: center;
margin-top: 30px; // margin-top: 30px;
flex-direction: column; // flex-direction: column;
* { // * {
margin: 0; // margin: 0;
} // }
` // `

View File

@ -88,7 +88,7 @@ export default function GrossMensalChart({
datalabels: { datalabels: {
display: true, display: true,
color: '#255488', color: '#255488',
clip: true, clip: false,
formatter: (value, ctx) => { formatter: (value, ctx) => {
let sum = 0 let sum = 0
const dataArr = ctx.chart.data.datasets[0].data const dataArr = ctx.chart.data.datasets[0].data
@ -152,7 +152,7 @@ export default function GrossMensalChart({
return ( return (
<GrossMensalChartView> <GrossMensalChartView>
<ChartTitle title={title} subtitle={subtitle} /> {/* <ChartTitle title={title} subtitle={subtitle} /> */}
<ChartJs options={options} data={data} type={'bar'} height={'156'} /> <ChartJs options={options} data={data} type={'bar'} height={'156'} />
</GrossMensalChartView> </GrossMensalChartView>
) )

View File

@ -1,7 +1,7 @@
import { GetServerSideProps } from 'next' import { GetServerSideProps } from 'next'
import Head from 'next/head' import Head from 'next/head'
import { parseCookies } from 'nookies' import { parseCookies } from 'nookies'
import { useEffect, useState } from 'react'
// import Chart2 from '../../components/graph/Chart2' // import Chart2 from '../../components/graph/Chart2'
import GrossMensalChart from '../../components/graph/grossMensalChart/GrossMensalChart' import GrossMensalChart from '../../components/graph/grossMensalChart/GrossMensalChart'
@ -10,6 +10,7 @@ import PageTitle from '../../components/pageTitle/PageTitle'
import getAPIClient from '../../services/ssrApi' import getAPIClient from '../../services/ssrApi'
import { AccumulatedSavingsView } from '../../styles/layouts/economy/accumulatedSavings/AccumulatedSavingsView' import { AccumulatedSavingsView } from '../../styles/layouts/economy/accumulatedSavings/AccumulatedSavingsView'
import { getLastConsolidatedYear, populateGraphDataForYear } from '../../utils/dataProcessing'
export default function AccumulatedSavings({graphData, years, userName}: any) { export default function AccumulatedSavings({graphData, years, userName}: any) {
const months = [ const months = [
@ -27,6 +28,19 @@ export default function AccumulatedSavings({graphData, years, userName}: any) {
'Dez' 'Dez'
] ]
const [processedData, setProcessedData] = useState(graphData)
const [lastConsolidatedYear, setLastConsolidatedYear] = useState<number | null>(null)
useEffect(() => {
// Calculate the last consolidated year
const lastYear = getLastConsolidatedYear(graphData, true)
setLastConsolidatedYear(lastYear)
// Populate graph data with consolidated and estimated data for that year
const populatedData = populateGraphDataForYear(graphData, lastYear)
setProcessedData(populatedData)
}, [graphData])
return ( return (
<AccumulatedSavingsView> <AccumulatedSavingsView>
<Head> <Head>
@ -37,8 +51,8 @@ export default function AccumulatedSavings({graphData, years, userName}: any) {
</Header> </Header>
<section> <section>
<GrossMensalChart title='' subtitle='' <GrossMensalChart title='' subtitle=''
data1={graphData} data1={processedData}
data2={graphData} data2={processedData}
label={months} label={months}
/> />
{/* <SingleBar title='' subtitle='' dataset='Consolidada' {/* <SingleBar title='' subtitle='' dataset='Consolidada'

View File

@ -15,6 +15,7 @@ import PageTitle from '../../components/pageTitle/PageTitle'
import getAPIClient from '../../services/ssrApi' import getAPIClient from '../../services/ssrApi'
import { CostIndicatorView } from '../../styles/layouts/economy/costIndicator/CostIndicatorView' import { CostIndicatorView } from '../../styles/layouts/economy/costIndicator/CostIndicatorView'
import { api } from '../../services/api'; import { api } from '../../services/api';
import { getLastConsolidatedYear, populateGraphDataForYear } from '../../utils/dataProcessing'
export default function CostIndicator({graphData, userName, clients}: any) { export default function CostIndicator({graphData, userName, clients}: any) {
const [unity, setUnity] = useState(''); const [unity, setUnity] = useState('');
@ -35,6 +36,18 @@ export default function CostIndicator({graphData, userName, clients}: any) {
] ]
const [graphDataState, setGraphDataState] = useState([]); const [graphDataState, setGraphDataState] = useState([]);
const [processedGraphData, setProcessedGraphData] = useState(graphData)
const [lastConsolidatedYear, setLastConsolidatedYear] = useState<number | null>(null)
useEffect(() => {
// Calculate the last consolidated year
const lastYear = getLastConsolidatedYear(graphData, true)
setLastConsolidatedYear(lastYear)
// Populate graph data with consolidated and estimated data for that year
const populatedData = populateGraphDataForYear(graphData, lastYear)
setProcessedGraphData(populatedData)
}, [graphData])
useEffect(() => { useEffect(() => {
api.post('/economy/estimates', unity!==''?{ api.post('/economy/estimates', unity!==''?{
@ -42,7 +55,14 @@ export default function CostIndicator({graphData, userName, clients}: any) {
{"type" : "=", "field":"dados_cadastrais.cod_smart_unidade", "value": unity} {"type" : "=", "field":"dados_cadastrais.cod_smart_unidade", "value": unity}
] ]
}:{}).then(res => { }:{}).then(res => {
// Apply data processing to filtered result
if (res.data.data && res.data.data.length > 0) {
const lastYear = getLastConsolidatedYear(res.data.data, true)
const populatedData = populateGraphDataForYear(res.data.data, lastYear)
setGraphDataState(populatedData)
} else {
setGraphDataState(res.data.data) setGraphDataState(res.data.data)
}
}) })
}, [unity]) }, [unity])
@ -75,12 +95,16 @@ export default function CostIndicator({graphData, userName, clients}: any) {
</FormControl> </FormControl>
<section> <section>
<CostIndicatorChart title='' subtitle='' <CostIndicatorChart title='' subtitle=''
data1={unity!==''? graphDataState.filter((value, index) => value.mes.slice(4, 8).includes('2021')) data1={unity!==''? graphDataState.filter((value, index) => value.mes.slice(0, 4).includes(lastConsolidatedYear?.toString() || ''))
.map(value => value?.custo_unit && !!parseInt(value?.custo_unit) ? value.custo_unit : null)
: :
graphData.filter((value, index) => value.mes.slice(4, 8).includes('2021'))} processedGraphData.filter((value, index) => value.mes.slice(0, 4).includes(lastConsolidatedYear?.toString() || ''))
data2={unity!==''? graphDataState.filter((value, index) => value.mes.slice(4, 8).includes('2022')) .map(value => value?.custo_unit && !!parseInt(value?.custo_unit) ? value.custo_unit : null)}
data2={unity!==''? graphDataState.filter((value, index) => value.mes.slice(0, 4).includes(lastConsolidatedYear?.toString() || ''))
.map(value => value?.custo_unit && !!parseInt(value?.custo_unit) ? value.custo_unit : null)
: :
graphData.filter((value, index) => value.mes.slice(4, 8).includes('2022'))} processedGraphData.filter((value, index) => value.mes.slice(0, 4).includes(lastConsolidatedYear?.toString() || ''))
.map(value => value?.custo_unit && !!parseInt(value?.custo_unit) ? value.custo_unit : null)}
label={months} label={months}
/> />
</section> </section>

View File

@ -18,6 +18,7 @@ import CostIndicatorChart from '../../components/graph/costIndicatorChart'
import { GrossAnualChart } from '../../components/graph/grossAnualChart/GrossAnualChart' import { GrossAnualChart } from '../../components/graph/grossAnualChart/GrossAnualChart'
import GrossMensalChart from '../../components/graph/grossMensalChart/GrossMensalChart' import GrossMensalChart from '../../components/graph/grossMensalChart/GrossMensalChart'
import getAPIClient from '../../services/ssrApi' import getAPIClient from '../../services/ssrApi'
import { getLastConsolidatedYear, populateGraphDataForYear } from '../../utils/dataProcessing'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import Modal from '@mui/material/Modal' import Modal from '@mui/material/Modal'
@ -66,30 +67,41 @@ export default function Dashboard({ grossAnualGraph, grossAnualYears, grossMensa
const [lastDataBrutaMensalS, setLastDataBrutaMensal] = useState('') const [lastDataBrutaMensalS, setLastDataBrutaMensal] = useState('')
const [lastDataBrutaAnualS, setLastDataBrutaAnual] = useState('') const [lastDataBrutaAnualS, setLastDataBrutaAnual] = useState('')
const [processedMensalData, setProcessedMensalData] = useState(grossMensalGraph)
const [lastConsolidatedYear, setLastConsolidatedYear] = useState<number | null>(null)
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const handleOpen = () => setOpen(true); const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false); const handleClose = () => setOpen(false);
useEffect(() => { useEffect(() => {
// Calculate the last consolidated year
const lastYear = getLastConsolidatedYear(grossMensalGraph, true)
setLastConsolidatedYear(lastYear)
// Populate graph data with consolidated and estimated data for that year
const populatedData = populateGraphDataForYear(grossMensalGraph, lastYear)
setProcessedMensalData(populatedData)
// Calculate last data values
let lastDataMensal = '0' let lastDataMensal = '0'
let lastDataAnual = '0' let lastDataAnual = '0'
let index = 0 let index = 0
while (index < grossMensalGraph.length) { while (index < populatedData.length) {
if (!grossMensalGraph[index].dad_estimado && grossMensalGraph[index].economia_acumulada !== null) if (!populatedData[index].dad_estimado && populatedData[index].economia_acumulada !== null)
lastDataMensal = grossMensalGraph[index].economia_acumulada lastDataMensal = String(populatedData[index].economia_acumulada)
index++ index++
} }
setLastDataBrutaMensal(`${parseFloat(lastDataMensal).toFixed(3)}`) setLastDataBrutaMensal(`${parseFloat(lastDataMensal).toFixed(3)}`)
index = 0 index = 0
while (index < grossAnualGraph.length) { while (index < grossAnualGraph.length) {
if (!grossAnualGraph[index].dad_estimado) if (!grossAnualGraph[index].dad_estimado)
lastDataAnual = grossAnualGraph[index].economia_acumulada lastDataAnual = String(grossAnualGraph[index].economia_acumulada)
index++ index++
} }
setLastDataBrutaAnual(`${parseFloat(lastDataAnual).toFixed(3)}`) setLastDataBrutaAnual(`${parseFloat(lastDataAnual).toFixed(3)}`)
}, []) }, [grossMensalGraph, grossAnualGraph])
return ( return (
<DashboardView> <DashboardView>
@ -126,8 +138,8 @@ export default function Dashboard({ grossAnualGraph, grossAnualYears, grossMensa
<GraphCard title='Economia Mensal' subtitle='Economia Bruta Estimada e Acumulada Mensal - Valores em R$ x mil'> <GraphCard title='Economia Mensal' subtitle='Economia Bruta Estimada e Acumulada Mensal - Valores em R$ x mil'>
<AccumulatedEconomyTitle value={lastDataBrutaMensalS} /> <AccumulatedEconomyTitle value={lastDataBrutaMensalS} />
<GrossMensalChart title='' subtitle='' <GrossMensalChart title='' subtitle=''
data1={grossMensalGraph} data1={processedMensalData}
data2={grossMensalGraph} data2={processedMensalData}
label={months} label={months}
miniature miniature
/> />

View File

@ -13,6 +13,7 @@ import { api } from '../../services/api';
import getAPIClient from '../../services/ssrApi'; import getAPIClient from '../../services/ssrApi';
import { TableHeader } from '../../styles/layouts/pld/PldView'; import { TableHeader } from '../../styles/layouts/pld/PldView';
import RenderIf from '../../utils/renderIf'; import RenderIf from '../../utils/renderIf';
import { getLastConsolidatedYear, populateGraphDataForYear } from '../../utils/dataProcessing';
import Tab from '@mui/material/Tab'; import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs'; import Tabs from '@mui/material/Tabs';
@ -32,6 +33,8 @@ export default function economy({ userName, anual, years, brutaMensal, catLiv, c
const [catLivDataState, setCatLivDataState] = useState(null); const [catLivDataState, setCatLivDataState] = useState(null);
const [indicatorDataState, setIndicatorDataState] = useState(null); const [indicatorDataState, setIndicatorDataState] = useState(null);
const [processedBrutaMensal, setProcessedBrutaMensal] = useState(brutaMensal)
const [lastConsolidatedYear, setLastConsolidatedYear] = useState<number | null>(null)
const currentYear = new Date().getUTCFullYear() const currentYear = new Date().getUTCFullYear()
const previousYear = new Date().getUTCFullYear() - 1 const previousYear = new Date().getUTCFullYear() - 1
@ -52,13 +55,24 @@ export default function economy({ userName, anual, years, brutaMensal, catLiv, c
] ]
const [lastDataBruta, setLastDataBruta] = useState('') const [lastDataBruta, setLastDataBruta] = useState('')
useEffect(() => {
// Calculate the last consolidated year
const lastYear = getLastConsolidatedYear(brutaMensal, true)
setLastConsolidatedYear(lastYear)
// Populate graph data with consolidated and estimated data for that year
const populatedData = populateGraphDataForYear(brutaMensal, lastYear)
setProcessedBrutaMensal(populatedData)
}, [brutaMensal])
useEffect(() => { useEffect(() => {
let lastData = '0' let lastData = '0'
let index = 0 let index = 0
if (economyMenu) { if (economyMenu) {
while (index < brutaMensal.length) { while (index < processedBrutaMensal.length) {
if (!brutaMensal[index].dad_estimado) if (!processedBrutaMensal[index].dad_estimado)
lastData = brutaMensal[index].economia_acumulada lastData = processedBrutaMensal[index].economia_acumulada
index++ index++
} }
} else { } else {
@ -69,7 +83,7 @@ export default function economy({ userName, anual, years, brutaMensal, catLiv, c
} }
} }
setLastDataBruta(`${parseFloat(lastData).toFixed(3)}`) setLastDataBruta(`${parseFloat(lastData).toFixed(3)}`)
}, [economyMenu]) }, [economyMenu, processedBrutaMensal])
useEffect(() => { useEffect(() => {
console.log(indicatorDataState) console.log(indicatorDataState)
}, [indicatorDataState]) }, [indicatorDataState])
@ -148,8 +162,8 @@ export default function economy({ userName, anual, years, brutaMensal, catLiv, c
<RenderIf isTrue={economyMenu === 1}> <RenderIf isTrue={economyMenu === 1}>
<section> <section>
<GrossMensalChart title='' subtitle='' <GrossMensalChart title='' subtitle=''
data1={brutaMensal} data1={processedBrutaMensal}
data2={brutaMensal} data2={processedBrutaMensal}
label={months} label={months}
/> />
</section> </section>

View File

@ -16,11 +16,24 @@ import getAPIClient from '../../services/ssrApi'
import { EstimatedCostView } from '../../styles/layouts/economy/estimatedCost/EstimatedCostView' import { EstimatedCostView } from '../../styles/layouts/economy/estimatedCost/EstimatedCostView'
import { api } from '../../services/api' import { api } from '../../services/api'
import { getLastConsolidatedYear, populateGraphDataForYear } from '../../utils/dataProcessing'
export default function EstimatedCost({graphData, userName, clients}: any) { export default function EstimatedCost({graphData, userName, clients}: any) {
const [unity, setUnity] = useState<string>(null); const [unity, setUnity] = useState<string>(null);
const [graphDataState, setGraphDataState] = useState(null); const [graphDataState, setGraphDataState] = useState(null);
const [processedGraphData, setProcessedGraphData] = useState(graphData)
const [lastConsolidatedYear, setLastConsolidatedYear] = useState<number | null>(null)
useEffect(() => {
// Calculate the last consolidated year
const lastYear = getLastConsolidatedYear(graphData, true)
setLastConsolidatedYear(lastYear)
// Populate graph data with consolidated and estimated data for that year
const populatedData = populateGraphDataForYear(graphData, lastYear)
setProcessedGraphData(populatedData)
}, [graphData])
useEffect(() => { useEffect(() => {
api.post('/economy/estimates', unity!==''?{ api.post('/economy/estimates', unity!==''?{
@ -28,7 +41,14 @@ export default function EstimatedCost({graphData, userName, clients}: any) {
{"type" : "=", "field":"dados_cadastrais.cod_smart_unidade", "value": unity} {"type" : "=", "field":"dados_cadastrais.cod_smart_unidade", "value": unity}
] ]
}:{}).then(res => { }:{}).then(res => {
// Apply data processing to filtered result
if (res.data.data && res.data.data.length > 0) {
const lastYear = getLastConsolidatedYear(res.data.data, true)
const populatedData = populateGraphDataForYear(res.data.data, lastYear)
setGraphDataState(populatedData)
} else {
setGraphDataState(res.data.data) setGraphDataState(res.data.data)
}
}) })
}, [unity]) }, [unity])
@ -60,7 +80,7 @@ export default function EstimatedCost({graphData, userName, clients}: any) {
</Select> </Select>
</FormControl> </FormControl>
<section> <section>
<CativoXLivreChart chartData={unity!==null? graphDataState : graphData} <CativoXLivreChart chartData={unity!==null? graphDataState : processedGraphData}
dataset1="Economia (R$)" dataset2='Est. Cativo' dataset3='Est. Livre' dataset1="Economia (R$)" dataset2='Est. Cativo' dataset3='Est. Livre'
label={['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']} title='' subtitle='' barLabel hashurado/> label={['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']} title='' subtitle='' barLabel hashurado/>
</section> </section>

287
src/utils/dataProcessing.ts Normal file
View File

@ -0,0 +1,287 @@
/**
* Utility functions to process economy data from API
* Calculates the year with the last consolidated data and populates graphs accordingly
*/
interface EconomyData {
ano?: string | number;
mes?: string;
economia_acumulada?: number;
economia_mensal?: number;
dad_estimado: boolean;
[key: string]: any;
}
/**
* Extract year from mes field supporting multiple formats
* Supports: "YYYY-MM-DD", "MMM/YYYY", "MM/YYYY"
* @param mesValue - The mes field value
* @returns Year as number
*/
function extractYearFromMes(mesValue: string): number {
const mesStr = mesValue.toString();
// Handle "YYYY-MM-DD" format
if (mesStr.includes('-')) {
const yearStr = mesStr.split('-')[0];
return parseInt(yearStr);
}
// Handle "MMM/YYYY" or "MM/YYYY" format
if (mesStr.includes('/')) {
const yearStr = mesStr.split('/')[1];
return parseInt(yearStr);
}
// Fallback: try to parse as number if it's just a year
const parsed = parseInt(mesStr);
return !isNaN(parsed) ? parsed : new Date().getFullYear();
}
/**
* Find the year with the last consolidated (non-estimated) data
* @param data - Array of economy data
* @param isMonthly - If true, extract year from 'mes' field; if false, use 'ano' field
* @returns The year with the last consolidated data
*/
export function getLastConsolidatedYear(data: EconomyData[], isMonthly: boolean = false): number {
if (!data || data.length === 0) {
return new Date().getFullYear();
}
// Filter only consolidated data (dad_estimado === false)
const consolidatedData = data.filter(item => !item.dad_estimado);
// Extract all years present in the dataset (consolidated + estimated)
const allYears = data
.map(item => {
if (isMonthly && item.mes) {
return extractYearFromMes(item.mes);
}
return parseInt(item.ano?.toString() || new Date().getFullYear().toString());
})
.filter(year => !Number.isNaN(year));
// Extract years from consolidated records
const consolidatedYears = consolidatedData
.map(item => {
if (isMonthly && item.mes) {
return extractYearFromMes(item.mes);
}
return parseInt(item.ano?.toString() || new Date().getFullYear().toString());
})
.filter(year => !Number.isNaN(year));
// Prefer the latest consolidated year; if none, fall back to the latest year available
if (consolidatedYears.length > 0) {
return Math.max(...consolidatedYears);
}
if (allYears.length > 0) {
return Math.max(...allYears);
}
return new Date().getFullYear();
}
/**
* Filter and prepare graph data for a specific year
* Uses consolidated data for that year and estimated data for remaining months
* @param data - Array of economy data (monthly)
* @param targetYear - The year to filter by
* @returns Processed array with consolidated and estimated data
*/
export function populateGraphDataForYear(data: EconomyData[], targetYear: number | string): EconomyData[] {
if (!data || data.length === 0) {
return [];
}
const year = parseInt(targetYear.toString());
const currentMonth = new Date().getMonth() + 1; // 1-12
const currentYear = new Date().getFullYear();
// Filter data for the target year
const yearData = data.filter(item => {
if (item.mes) {
const itemYear = extractYearFromMes(item.mes);
return itemYear === year;
}
return false;
});
// If no data for the year, try to use the latest year available to avoid empty charts
if (yearData.length === 0) {
const fallbackYear = data
.map(item => (item.mes ? extractYearFromMes(item.mes) : parseInt(item.ano?.toString() || '0')))
.filter(year => !Number.isNaN(year))
.sort((a, b) => b - a)[0];
if (!fallbackYear) {
return [];
}
return populateGraphDataForYear(data, fallbackYear);
}
// Create a map of months for quick lookup
const dataMap = new Map<number, EconomyData>();
yearData.forEach(item => {
if (item.mes) {
const mesStr = item.mes.toString();
let month = 0;
// Extract month from different formats
if (mesStr.includes('-')) {
// YYYY-MM-DD format
month = parseInt(mesStr.split('-')[1]);
} else if (mesStr.includes('/')) {
// MMM/YYYY or MM/YYYY format
const parts = mesStr.split('/');
const monthPart = parts[0];
// Try to parse as number first
month = parseInt(monthPart);
// If it's NaN, it's a month name, so we need to convert it
if (isNaN(month)) {
const monthNames = ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'];
month = monthNames.indexOf(monthPart.toLowerCase()) + 1;
}
}
if (month > 0) {
dataMap.set(month, item);
}
}
});
// Build complete year data (1-12 months)
const completeYearData: EconomyData[] = [];
for (let month = 1; month <= 12; month++) {
if (dataMap.has(month)) {
completeYearData.push(dataMap.get(month)!);
} else {
// Fill missing months with estimated data
// Find the last consolidated data or use the last available data as base
const lastConsolidated = Array.from(dataMap.values())
.filter(item => !item.dad_estimado)
.sort((a, b) => {
const mesA = a.mes?.toString() || '';
const mesB = b.mes?.toString() || '';
let monthA = 0, monthB = 0;
if (mesA.includes('-')) {
monthA = parseInt(mesA.split('-')[1]);
} else if (mesA.includes('/')) {
const part = mesA.split('/')[0];
monthA = parseInt(part) || getMonthNumber(part);
}
if (mesB.includes('-')) {
monthB = parseInt(mesB.split('-')[1]);
} else if (mesB.includes('/')) {
const part = mesB.split('/')[0];
monthB = parseInt(part) || getMonthNumber(part);
}
return monthB - monthA;
})[0];
if (lastConsolidated) {
const lastConsolidatedMonth = extractMonthFromMes(lastConsolidated.mes || '');
if (month > lastConsolidatedMonth) {
// Create estimated data entry for future months
completeYearData.push({
...lastConsolidated,
mes: `${month < 10 ? '0' : ''}${month}/${year}`,
dad_estimado: true
});
} else if (dataMap.size > 0) {
// Use the first available data as template for earlier months
const firstData = Array.from(dataMap.values())[0];
completeYearData.push({
...firstData,
mes: `${month < 10 ? '0' : ''}${month}/${year}`,
dad_estimado: true
});
}
} else if (dataMap.size > 0) {
// Use the first available data as template
const firstData = Array.from(dataMap.values())[0];
completeYearData.push({
...firstData,
mes: `${month < 10 ? '0' : ''}${month}/${year}`,
dad_estimado: true
});
}
}
}
return completeYearData.sort((a, b) => {
const monthA = extractMonthFromMes(a.mes || '');
const monthB = extractMonthFromMes(b.mes || '');
return monthA - monthB;
});
}
/**
* Extract month number from mes field
* @param mesValue - The mes field value
* @returns Month number (1-12)
*/
function extractMonthFromMes(mesValue: string): number {
const mesStr = mesValue.toString();
// Handle "YYYY-MM-DD" format
if (mesStr.includes('-')) {
return parseInt(mesStr.split('-')[1]);
}
// Handle "MMM/YYYY" or "MM/YYYY" format
if (mesStr.includes('/')) {
const monthPart = mesStr.split('/')[0];
const parsed = parseInt(monthPart);
if (!isNaN(parsed)) {
return parsed;
}
return getMonthNumber(monthPart);
}
return 1; // Default to January
}
/**
* Convert month name to month number
* @param monthName - Month name (Jan, Feb, etc. or jan, fev, etc.)
* @returns Month number (1-12)
*/
function getMonthNumber(monthName: string): number {
const months = ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'];
return months.indexOf(monthName.toLowerCase()) + 1 || 1;
}
/**
* Get consolidated and estimated data split for a specific year
* Returns both consolidated and estimated datasets separately
* @param data - Array of economy data
* @param targetYear - The year to filter by
* @returns Object with consolidated and estimated arrays
*/
export function getConsolidatedAndEstimatedData(
data: EconomyData[],
targetYear: number | string
): { consolidated: EconomyData[]; estimated: EconomyData[] } {
const year = parseInt(targetYear.toString());
const yearData = data.filter(item => {
if (item.mes) {
const itemYear = extractYearFromMes(item.mes);
return itemYear === year;
}
return false;
});
const consolidated = yearData.filter(item => !item.dad_estimado);
const estimated = yearData.filter(item => item.dad_estimado);
return { consolidated, estimated };
}