From 5a91da9ddfecf87a5bebe3132d74f5abeeff6789 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Tue, 27 Aug 2024 14:15:05 -0300 Subject: [PATCH 01/31] feat: adicionando componente Mensagem para a Home --- front/src/app/page.tsx | 12 ++++++----- front/src/components/Mensagem.tsx | 36 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 front/src/components/Mensagem.tsx diff --git a/front/src/app/page.tsx b/front/src/app/page.tsx index 861a558..282eaaf 100644 --- a/front/src/app/page.tsx +++ b/front/src/app/page.tsx @@ -1,25 +1,27 @@ "use client"; -import { Search, MapPin, CalendarClock, MoveRight } from "lucide-react"; +import { Search, MapPin, CalendarClock, MoveRight } from "lucide-react"; import Busca from "@/components/Busca"; import dynamic from 'next/dynamic'; import Slider from "@/components/Slider"; import Pilares from "@/components/Pilares"; +import Mensagem from "@/components/Mensagem"; const Grafico = dynamic(() => import('@/components/Grafico'), { ssr: false }); export default function Home() { return (
-
- - -
+
{/* Conteúdo adicional pode ir aqui */}
+
+ + +
); } diff --git a/front/src/components/Mensagem.tsx b/front/src/components/Mensagem.tsx new file mode 100644 index 0000000..8acd00f --- /dev/null +++ b/front/src/components/Mensagem.tsx @@ -0,0 +1,36 @@ +import { useState, useEffect } from "react"; + +const Mensagem = () => { + const [currentText, setCurrentText] = useState(0); + + const texts = [ + "Simplificando o acesso aos investimentos culturais pelo estado", + "Facilitando a visualização de dados culturais", + "Promovendo a transparência nos gastos culturais", + "Aprimorando o acesso à cultura em Minas Gerais" + ]; + + useEffect(() => { + const interval = setInterval(() => { + setCurrentText((prevText) => (prevText + 1) % texts.length); + }, 3000); + + return () => clearInterval(interval); + }, [texts.length]); + + return ( +
+
+

+ Dados oficiais das despesas da Secretaria de Estado de Cultura de Minas Gerais +

+
+
+
+

{texts[currentText]}

+
+
+ ); +} + +export default Mensagem; From fd99d0e963753ef056af6b6fb4a74e9b6267b614 Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Thu, 29 Aug 2024 16:34:24 -0300 Subject: [PATCH 02/31] feat: Adicionando o novo modelo de grafico --- front/src/app/Pesquisa/page.tsx | 2 + front/src/components/Dashboard.tsx | 237 +++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 front/src/components/Dashboard.tsx diff --git a/front/src/app/Pesquisa/page.tsx b/front/src/app/Pesquisa/page.tsx index 89760b4..5936172 100644 --- a/front/src/app/Pesquisa/page.tsx +++ b/front/src/app/Pesquisa/page.tsx @@ -1,9 +1,11 @@ import Filtro from "@/components/Filtro"; +import Dashboard from '@/components/Dashboard'; export default function Pesquisa() { return (
+
) } \ No newline at end of file diff --git a/front/src/components/Dashboard.tsx b/front/src/components/Dashboard.tsx new file mode 100644 index 0000000..7b97829 --- /dev/null +++ b/front/src/components/Dashboard.tsx @@ -0,0 +1,237 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import ApexCharts from 'react-apexcharts'; +import { ApexOptions } from 'apexcharts'; +import { CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; +import { ptBR } from 'date-fns/locale'; + +interface Dados { + id: number; + committed_value: number; + liquidated_value: number; + paid_value: number; + year: number; + month: number; +} + +const DashboardWithFilter: React.FC = () => { + const [data, setData] = useState([]); + const [lineChartSeries, setLineChartSeries] = useState([]); + const [pieChartSeries, setPieChartSeries] = useState([]); + const [totalSales, setTotalSales] = useState(0); + const [totalRevenue, setTotalRevenue] = useState(0); + const [totalUsers, setTotalUsers] = useState(0); + const [startDate, setStartDate] = useState(null); + const [endDate, setEndDate] = useState(null); + const [errorMessage, setErrorMessage] = useState(null); + + const fetchData = async (startDate: Date | null, endDate: Date | null) => { + if (!startDate || !endDate) return; + + try { + const startMonth = (startDate.getMonth() + 1).toString().padStart(2, '0'); + const startYear = startDate.getFullYear().toString(); + const endMonth = (endDate.getMonth() + 1).toString().padStart(2, '0'); + const endYear = endDate.getFullYear().toString(); + + const response = await fetch(`http://localhost:5000/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`); + const data: Dados[] = await response.json(); + setData(data); + + const committedValues = data.map(item => item.committed_value); + const liquidatedValues = data.map(item => item.liquidated_value); + const paidValues = data.map(item => item.paid_value); + + setLineChartSeries([ + { name: 'Valor Empenhado', data: committedValues }, + { name: 'Valor Liquidado', data: liquidatedValues }, + { name: 'Valor Pago', data: paidValues } + ]); + + const totalCommitted = data.reduce((acc, item) => acc + item.committed_value, 0); + const totalLiquidated = data.reduce((acc, item) => acc + item.liquidated_value, 0); + const totalPaid = data.reduce((acc, item) => acc + item.paid_value, 0); + + setPieChartSeries([totalCommitted, totalLiquidated, totalPaid]); + setTotalSales(totalCommitted); + setTotalRevenue(totalLiquidated); + setTotalUsers(totalPaid); + + setErrorMessage(null); + } catch (error) { + console.error('Erro ao buscar dados:', error); + setErrorMessage('Erro ao buscar dados.'); + } + }; + + useEffect(() => { + fetchData(startDate, endDate); + }, [startDate, endDate]); + + const renderCustomHeader = ({ + date, + decreaseYear, + increaseYear, + }: { + date: Date; + decreaseYear: () => void; + increaseYear: () => void; + }) => { + const months = [ + 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', + 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' + ]; + return ( +
+ + + {months[date.getMonth()]} de {date.getFullYear()} + + +
+ ); + }; + + const handleStartDateChange = (date: Date | null) => { + if (date && endDate && date > endDate) { + setEndDate(date); + } + setStartDate(date); + }; + + const handleEndDateChange = (date: Date | null) => { + if (date && startDate && date < startDate) { + setEndDate(startDate); + } else { + setEndDate(date); + } + }; + + const lineChartOptions: ApexOptions = { + chart: { + type: 'line', + }, + title: { + text: 'Valores', + align: 'center' + }, + xaxis: { + categories: data.map(item => `${item.year}-${String(item.month).padStart(2, '0')}`) + }, + yaxis: { + title: { + text: 'Valores' + }, + labels: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` + } + }, + colors: ['#ED1C24', '#F19C28', '#2FB551'] + }; + + const pieChartOptions: ApexOptions = { + chart: { + type: 'pie', + }, + title: { + text: 'Distribuição de valores', + align: 'center' + }, + labels: ['Valor Empenhado', 'Valor Liquidado', 'Valor Pago'], + tooltip: { + y: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` + } + }, + colors: ['#ED1C24', '#F19C28', '#2FB551'], + legend: { + position: 'bottom', + } + }; + + return ( +
+
+
+

+ Pesquise por período +

+
    +
  • + + + + +
    +
  • +
+
+ + + +
+
+

Total Empenhado

+

R$ {totalSales.toLocaleString()}

+
+
+

Total Liquidado

+

R$ {totalRevenue.toLocaleString()}

+
+
+

Total Pago

+

R$ {totalUsers.toLocaleString()}

+
+
+
+
+ +
+
+ +
+
+
+
+ ); +}; + +export default DashboardWithFilter; \ No newline at end of file From cf877ad13d8803732a44a0acedb7ca517dcffec8 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 18:40:53 -0300 Subject: [PATCH 03/31] =?UTF-8?q?feat:=20adicionando=20requisi=C3=A7=C3=A3?= =?UTF-8?q?o=20=C3=A0=20API=20para=20obter=20despesas=20anuais?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/package-lock.json | 7 ++++--- front/src/app/page.tsx | 6 +----- front/src/services/api.ts | 32 ++++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/front/package-lock.json b/front/package-lock.json index f8a3487..bc5159d 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -10542,9 +10542,10 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" diff --git a/front/src/app/page.tsx b/front/src/app/page.tsx index 282eaaf..f5cdae4 100644 --- a/front/src/app/page.tsx +++ b/front/src/app/page.tsx @@ -6,16 +6,12 @@ import dynamic from 'next/dynamic'; import Slider from "@/components/Slider"; import Pilares from "@/components/Pilares"; import Mensagem from "@/components/Mensagem"; - -const Grafico = dynamic(() => import('@/components/Grafico'), { ssr: false }); +import Grafico from '@/components/Grafico'; export default function Home() { return (
-
- {/* Conteúdo adicional pode ir aqui */} -
diff --git a/front/src/services/api.ts b/front/src/services/api.ts index 596babf..6891b7a 100644 --- a/front/src/services/api.ts +++ b/front/src/services/api.ts @@ -14,7 +14,7 @@ export const fetchCities = async () => { const response = await axios.get('http://localhost:5000/cities'); return response.data; } catch (error) { - console.error('Erro ao buscar cidades:', error); + console.error('Error fetching cities:', error); throw error; } }; @@ -24,13 +24,37 @@ export const fetchUnits = async () => { const response = await axios.get('http://localhost:5000/units'); return response.data; } catch (error) { - console.error('Erro ao buscar cidades:', error); + console.error('Error fetching units:', error); throw error; } }; +export const fetchYearlyTendersData = async () => { + try { + const requests = []; + for (let year = 2002; year <= 2023; year++) { + requests.push(axios.get(`http://localhost:5000/tenders/year?year=${year}`)); + } + const responses = await Promise.all(requests); + return responses.flatMap((response) => response.data); + } catch (error) { + console.error('Erro ao buscar dados anuais:', error); + return []; + } +}; + +export const fetchYearTender = async (year: number) => { + try { + const response = await axios.get(`http://localhost:5000/tenders/year?year=${year}`); + return response.data; + } catch (error) { + console.error('Error fetching year tender data:', error); + return []; + } +} + export const searchLicitacoes = async (params: SearchParams) => { - const { startYear, startMonth, endYear, endMonth, cityId, unitId} = params; + const { startYear, startMonth, endYear, endMonth, cityId, unitId } = params; const url = `http://localhost:5000/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}&city=${cityId}`; @@ -38,7 +62,7 @@ export const searchLicitacoes = async (params: SearchParams) => { const response = await axios.get(url); return response.data; } catch (error) { - console.error('Erro ao buscar licitações:', error); + console.error('Error searching tenders:', error); throw error; } }; From 051747e32fbc32c30a907bd21fd0459ef5210480 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 18:41:50 -0300 Subject: [PATCH 04/31] chore: Adicionando acessibilidade ao componente mensagem e diminuindo o tempo de clock --- front/src/components/Mensagem.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/front/src/components/Mensagem.tsx b/front/src/components/Mensagem.tsx index 8acd00f..38843e5 100644 --- a/front/src/components/Mensagem.tsx +++ b/front/src/components/Mensagem.tsx @@ -13,7 +13,7 @@ const Mensagem = () => { useEffect(() => { const interval = setInterval(() => { setCurrentText((prevText) => (prevText + 1) % texts.length); - }, 3000); + }, 2000); return () => clearInterval(interval); }, [texts.length]); @@ -21,13 +21,13 @@ const Mensagem = () => { return (
-

+

Dados oficiais das despesas da Secretaria de Estado de Cultura de Minas Gerais

-
+
-

{texts[currentText]}

+

{texts[currentText]}

); From 75f0343d61c5b3743fd76a7f784d8aae4e5df879 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 18:45:05 -0300 Subject: [PATCH 05/31] =?UTF-8?q?feat:=20adicionando=20gr=C3=A1fico=20de?= =?UTF-8?q?=20linha=20para=20despesas=20de=202002=20a=202023=20com=20dados?= =?UTF-8?q?=20da=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/Grafico.tsx | 290 +++++++++++++++---------------- 1 file changed, 138 insertions(+), 152 deletions(-) diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index 99a3bc5..8d80b01 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -1,165 +1,151 @@ -"use client"; - -import React, { useEffect, useState } from 'react'; +import React, { useState, useEffect } from 'react'; import dynamic from 'next/dynamic'; +import { fetchYearlyTendersData } from '../services/api'; import { ApexOptions } from 'apexcharts'; -const ApexCharts = dynamic(() => import('react-apexcharts'), { ssr: false }); +const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); + +interface Dados { + year: number; + committed_value: number; + liquidated_value: number; + paid_value: number; +} const Grafico: React.FC = () => { - const [isClient, setIsClient] = useState(false); - const isHighContrastMode = document.documentElement.classList.contains('high-contrast'); - const [chartOptions, setChartOptions] = useState({ - chart: { - type: 'bar', - height: 500, - background: '#ffffff', - foreColor: '#000000', - }, - plotOptions: { - bar: { - horizontal: false, - distributed: true, - barHeight: '100%', - colors: { - ranges: [{ - from: 0, - to: 500000, - color: '#ED1C24' - }], - } - } - }, - xaxis: { - categories: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], - type: 'category', - labels: { - style: { - colors: '#000000' - } - } - }, - yaxis: { - title: { - text: 'Valor', - style: { - color: '#000000' - } - }, - labels: { - style: { - colors: '#000000' - } - } - }, - legend: { - show: false, - }, - responsive: [ - { - breakpoint: 1025, - options: { - chart: { - height: 424, - width: 800, - } - } - }, - { - breakpoint: 640, - options: { - chart: { - height: 200, - width: 310, - } - } - }, - { - breakpoint: 769, - options: { - chart: { - height: 424, - width: 700 - } - } - } - ], - theme: { - mode: 'light', // Modo padrão inicial - palette: 'palette1', - }, - }); + const [series, setSeries] = useState([]); + const [errorMessage, setErrorMessage] = useState(null); - const series = [{ - name: 'Valor gasto', - data: [20000, 300000, 65000, 100000, 200000, 200000, 20000, 30000, 20000, 75000, 300000, 200000] - }]; + const fetchData = async () => { + try { + const data = await fetchYearlyTendersData(); + console.log('API Response:', data); + + if (Array.isArray(data) && data.length > 0) { + const series = [ + { + name: 'Valor Empenhado', + data: data.map((item) => ({ + x: new Date(item.year, 0, 1).toISOString(), + y: item.committed_value || 0 + })), + }, + { + name: 'Valor Liquidado', + data: data.map((item) => ({ + x: new Date(item.year, 0, 1).toISOString(), + y: item.liquidated_value || 0 + })), + }, + { + name: 'Valor Pago', + data: data.map((item) => ({ + x: new Date(item.year, 0, 1).toISOString(), + y: item.paid_value || 0 + })), + }, + ]; - useEffect(() => { - setIsClient(true); + console.log('Series Data Transformada:', series); - const root = document.documentElement; - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.attributeName === "class") { - const isDarkMode = root.classList.contains('dark'); - const isHighContrastMode = root.classList.contains('high-contrast'); // Verifica o modo de alto contraste - setChartOptions((prevOptions) => ({ - ...prevOptions, - chart: { - ...prevOptions.chart, - background: isHighContrastMode ? '#000000' : isDarkMode ? '#1f1f1f' : '#ffffff', - foreColor: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - }, - xaxis: { - ...prevOptions.xaxis, - labels: { - style: { - colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000' - } - } - }, - yaxis: { - ...prevOptions.yaxis, - labels: { - style: { - colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000' - } - }, - title: { - style: { - color: isDarkMode ? '#e5e7eb' : '#000000' - } - } - }, - theme: { - mode: isDarkMode ? 'dark' : 'light', - } - })); - } - }); - }); + setSeries(series); + setErrorMessage(null); + } else { + console.error('Dados inválidos retornados pela API.'); + setErrorMessage('Dados inválidos retornados pela API.'); + } + } catch (error) { + console.error('Erro ao buscar dados para o gráfico:', error); + setErrorMessage('Erro ao buscar dados para o gráfico.'); + } + }; - observer.observe(root, { attributes: true }); + useEffect(() => { + fetchData(); + }, []); - return () => { - observer.disconnect(); - }; - }, []); + const options: ApexOptions = { + chart: { + type: 'line', + height: 600, + zoom: { + enabled: true, + type: 'x', + zoomedArea: { + fill: { + color: '#90CAF9', + opacity: 0.4 + } + } + }, + toolbar: { + tools: { + zoomin: true, + zoomout: true, + pan: true, + reset: true + }, + autoSelected: 'pan' // Garantir que a ferramenta 'pan' seja selecionada inicialmente + } + }, + colors: ['#F19C28', '#ED1C24', '#2FB551'], + dataLabels: { + enabled: false + }, + stroke: { + curve: 'smooth' + }, + fill: { + type: 'gradient', + gradient: { + opacityFrom: 0.6, + opacityTo: 0.8, + } + }, + legend: { + position: 'top', + horizontalAlign: 'center' + }, + xaxis: { + type: 'datetime', + labels: { + format: 'yyyy', + }, + tickAmount: 10, + min: new Date(2014, 0, 1).getTime(), + max: new Date(2023, 0, 1).getTime(), + }, + yaxis: { + labels: { + style: { + fontSize: '14px', + }, + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` + }, + tickAmount: 4, + min: 5000000, // Define o valor mínimo do eixo y como 25 milhões + }, + tooltip: { + x: { + format: 'yyyy' + }, + }, + }; - return ( - <> - {isClient && ( - - )} - - ); -} + return ( +
+

+ Despesas em Cultura em Minas Gerais ao Longo dos Anos (2002-2023) +

+ {errorMessage &&

{errorMessage}

} + +
+ ); +}; export default Grafico; From dfba35e07f7d9d30f018386fd9e9bb0e29c0c5d2 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 18:51:21 -0300 Subject: [PATCH 06/31] =?UTF-8?q?fix:=20arrumando=20o=20formato=20em=20rea?= =?UTF-8?q?is=20dos=20n=C3=BAmeros=20do=20gr=C3=A1fico,=20erro=20de=20rend?= =?UTF-8?q?eriza=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/Grafico.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index 8d80b01..7239ee7 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -120,7 +120,12 @@ const Grafico: React.FC = () => { style: { fontSize: '14px', }, - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` + formatter: (value: number) => { + if (typeof value === 'number') { + return `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; + } + return 'R$ 0,00'; // Valor padrão se `value` não for um número + } }, tickAmount: 4, min: 5000000, // Define o valor mínimo do eixo y como 25 milhões From cd4b1404af57fa4a7f0b1812112cbbfe997e6a00 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 21:36:30 -0300 Subject: [PATCH 07/31] =?UTF-8?q?chore:=20responsividade=20do=20gr=C3=A1fi?= =?UTF-8?q?co=20e=20adicionando=20legenda=20explicativa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/Grafico.tsx | 115 +++++++++++++++++++++++++++---- front/src/components/Header.tsx | 2 +- 2 files changed, 101 insertions(+), 16 deletions(-) diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index 7239ee7..304fe76 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -85,8 +85,9 @@ const Grafico: React.FC = () => { pan: true, reset: true }, - autoSelected: 'pan' // Garantir que a ferramenta 'pan' seja selecionada inicialmente - } + autoSelected: 'pan' + }, + background: '#fff', // Fundo branco }, colors: ['#F19C28', '#ED1C24', '#2FB551'], dataLabels: { @@ -104,12 +105,16 @@ const Grafico: React.FC = () => { }, legend: { position: 'top', - horizontalAlign: 'center' + horizontalAlign: 'center', + fontSize: '14px', // Tamanho padrão da fonte da legenda }, xaxis: { type: 'datetime', labels: { format: 'yyyy', + style: { + fontSize: '12px', // Tamanho padrão da fonte do eixo X + }, }, tickAmount: 10, min: new Date(2014, 0, 1).getTime(), @@ -118,38 +123,118 @@ const Grafico: React.FC = () => { yaxis: { labels: { style: { - fontSize: '14px', + fontSize: '14px', // Tamanho padrão da fonte do eixo Y }, formatter: (value: number) => { if (typeof value === 'number') { return `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; } - return 'R$ 0,00'; // Valor padrão se `value` não for um número + return 'R$ 0,00'; } }, tickAmount: 4, - min: 5000000, // Define o valor mínimo do eixo y como 25 milhões + min: 5000000, }, tooltip: { x: { - format: 'yyyy' + format: 'DESPESA DE yyyy' + }, }, + responsive: [ + { + breakpoint: 768, // Ajustes abaixo de 768px + options: { + chart: { + height: 400, // Reduzir a altura do gráfico em telas menores + }, + legend: { + position: 'bottom', + horizontalAlign: 'center', + fontSize: '10px', // Reduzir tamanho da fonte da legenda + }, + xaxis: { + labels: { + style: { + fontSize: '10px', // Reduzir tamanho da fonte do eixo X + }, + }, + }, + yaxis: { + labels: { + style: { + fontSize: '6px', // Tamanho padrão da fonte do eixo Y + }, + formatter: (value: number) => { + if (typeof value === 'number') { + return `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; + } + return 'R$ 0,00'; + } + }, + tickAmount: 3, + }, + } + }, + { + breakpoint: 480, + options: { + chart: { + height: 300, + }, + legend: { + fontSize: '8px', + }, + xaxis: { + labels: { + style: { + fontSize: '8px', + }, + }, + }, + } + } + ] }; return ( -
-

+
+

Despesas em Cultura em Minas Gerais ao Longo dos Anos (2002-2023)

{errorMessage &&

{errorMessage}

} - +
+ +
+
    +
  • +
    +
    + Valor Empenhado: Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor. +
    +
  • +
  • +
    +
    + Valor Liquidado: Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue. +
    +
  • +
  • +
    +
    + Valor Pago: Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa. +
    +
  • +
+ + + ); }; diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index ede779a..f03fec1 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -64,7 +64,7 @@ export function Header() {
From f5184bea51468a274638d039d3f01e11e4e1a8c6 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 21:39:58 -0300 Subject: [PATCH 08/31] =?UTF-8?q?fix:=20arrumando=20a=20cor=20do=20gr?= =?UTF-8?q?=C3=A1fico=20para=20corresponder=20com=20a=20legenda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/Grafico.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index 304fe76..accc130 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -89,7 +89,7 @@ const Grafico: React.FC = () => { }, background: '#fff', // Fundo branco }, - colors: ['#F19C28', '#ED1C24', '#2FB551'], + colors: ['#ED1C24', '#F19C28', '#2FB551'], dataLabels: { enabled: false }, From d4f96a73d870f96818cf1d4e10b88256e218eac6 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Thu, 29 Aug 2024 23:59:31 -0300 Subject: [PATCH 09/31] =?UTF-8?q?chore:=20deixando=20a=20guia=20home=20ace?= =?UTF-8?q?ss=C3=ADvel=20e=20atualizando=20os=20seus=20componentes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/app/globals.css | 36 +++++ front/src/app/page.tsx | 7 +- front/src/components/Grafico.tsx | 221 ++++++++++++++++++------------- front/src/components/Header.tsx | 2 +- front/src/components/Slider.tsx | 16 +-- 5 files changed, 177 insertions(+), 105 deletions(-) diff --git a/front/src/app/globals.css b/front/src/app/globals.css index 745952f..cd891e5 100644 --- a/front/src/app/globals.css +++ b/front/src/app/globals.css @@ -59,6 +59,23 @@ color: #FFFFFF; /* Texto branco */ } +/* Estilo para linhas do gráfico */ +.high-contrast .chart-line-emp-enhanced, +.high-contrast .chart-line-liquidated, +.high-contrast .chart-line-paid { + stroke: #FFFFFF; /* Linha branca para manter contraste */ +} + +/* Estilo para a legenda do gráfico */ +.high-contrast .chart-legend-item { + color: #FFD700; /* Cor dourada para itens da legenda */ +} + +/* Estilo para explicações do gráfico */ +.high-contrast .chart-explanation { + color: #FFD700; /* Cor dourada para explicações */ +} + /* Estilo para aumentar o tamanho da fonte */ .font-lg { font-size: 1.25rem; /* Tamanho da fonte aumentado */ @@ -68,3 +85,22 @@ .font-original { font-size: 1rem; /* Tamanho da fonte original */ } + +/* Força a aplicação da cor vermelha */ +.swiper-button-next, +.swiper-button-prev { + color: #ED1C24 !important; +} +/* styles.css or a relevant CSS file */ +.legend-color-red { + background-color: #ED1C24 !important; + } + + .legend-color-orange { + background-color: #F19C28 !important; + } + + .legend-color-green { + background-color: #2FB551 !important; + } + \ No newline at end of file diff --git a/front/src/app/page.tsx b/front/src/app/page.tsx index f5cdae4..8960c47 100644 --- a/front/src/app/page.tsx +++ b/front/src/app/page.tsx @@ -12,12 +12,11 @@ export default function Home() { return (
- -
- -
+
+ +

); } diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index accc130..9c24893 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import dynamic from 'next/dynamic'; import { fetchYearlyTendersData } from '../services/api'; import { ApexOptions } from 'apexcharts'; +import { Info } from 'lucide-react'; // Import icon from lucide-react const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); @@ -15,56 +16,7 @@ interface Dados { const Grafico: React.FC = () => { const [series, setSeries] = useState([]); const [errorMessage, setErrorMessage] = useState(null); - - const fetchData = async () => { - try { - const data = await fetchYearlyTendersData(); - console.log('API Response:', data); - - if (Array.isArray(data) && data.length > 0) { - const series = [ - { - name: 'Valor Empenhado', - data: data.map((item) => ({ - x: new Date(item.year, 0, 1).toISOString(), - y: item.committed_value || 0 - })), - }, - { - name: 'Valor Liquidado', - data: data.map((item) => ({ - x: new Date(item.year, 0, 1).toISOString(), - y: item.liquidated_value || 0 - })), - }, - { - name: 'Valor Pago', - data: data.map((item) => ({ - x: new Date(item.year, 0, 1).toISOString(), - y: item.paid_value || 0 - })), - }, - ]; - - console.log('Series Data Transformada:', series); - - setSeries(series); - setErrorMessage(null); - } else { - console.error('Dados inválidos retornados pela API.'); - setErrorMessage('Dados inválidos retornados pela API.'); - } - } catch (error) { - console.error('Erro ao buscar dados para o gráfico:', error); - setErrorMessage('Erro ao buscar dados para o gráfico.'); - } - }; - - useEffect(() => { - fetchData(); - }, []); - - const options: ApexOptions = { + const [chartOptions, setChartOptions] = useState({ chart: { type: 'line', height: 600, @@ -85,9 +37,9 @@ const Grafico: React.FC = () => { pan: true, reset: true }, - autoSelected: 'pan' + autoSelected: 'pan' }, - background: '#fff', // Fundo branco + background: '#ffffff', }, colors: ['#ED1C24', '#F19C28', '#2FB551'], dataLabels: { @@ -106,14 +58,14 @@ const Grafico: React.FC = () => { legend: { position: 'top', horizontalAlign: 'center', - fontSize: '14px', // Tamanho padrão da fonte da legenda + fontSize: '14px', }, xaxis: { type: 'datetime', labels: { format: 'yyyy', style: { - fontSize: '12px', // Tamanho padrão da fonte do eixo X + fontSize: '12px', }, }, tickAmount: 10, @@ -123,14 +75,9 @@ const Grafico: React.FC = () => { yaxis: { labels: { style: { - fontSize: '14px', // Tamanho padrão da fonte do eixo Y + fontSize: '14px', }, - formatter: (value: number) => { - if (typeof value === 'number') { - return `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; - } - return 'R$ 0,00'; - } + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, }, tickAmount: 4, min: 5000000, @@ -138,74 +85,163 @@ const Grafico: React.FC = () => { tooltip: { x: { format: 'DESPESA DE yyyy' - }, }, responsive: [ { - breakpoint: 768, // Ajustes abaixo de 768px + breakpoint: 768, options: { chart: { - height: 400, // Reduzir a altura do gráfico em telas menores + height: 400, }, legend: { position: 'bottom', horizontalAlign: 'center', - fontSize: '10px', // Reduzir tamanho da fonte da legenda + fontSize: '10px', }, xaxis: { labels: { style: { - fontSize: '10px', // Reduzir tamanho da fonte do eixo X + fontSize: '10px', }, }, }, yaxis: { labels: { style: { - fontSize: '6px', // Tamanho padrão da fonte do eixo Y + fontSize: '6px', }, - formatter: (value: number) => { - if (typeof value === 'number') { - return `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; - } - return 'R$ 0,00'; - } + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, }, tickAmount: 3, }, } }, { - breakpoint: 480, + breakpoint: 480, options: { chart: { - height: 300, + height: 300, }, legend: { - fontSize: '8px', + fontSize: '8px', }, xaxis: { labels: { style: { - fontSize: '8px', + fontSize: '8px', }, }, }, } } ] + }); + + const updateChartOptionsForTheme = () => { + const isDarkMode = document.documentElement.classList.contains('dark'); + const isHighContrastMode = document.documentElement.classList.contains('high-contrast'); + + setChartOptions((prevOptions) => ({ + ...prevOptions, + chart: { + ...prevOptions.chart, + background: isHighContrastMode ? '#000000' : isDarkMode ? '#1f1f1f' : '#ffffff', + foreColor: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', + }, + xaxis: { + ...prevOptions.xaxis, + labels: { + style: { + colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', + } + } + }, + yaxis: { + ...prevOptions.yaxis, + labels: { + style: { + colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', + }, + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + } + }, + colors: isHighContrastMode ? ['#ED1C24', '#F19C28', '#2FB551'] : ['#ED1C24', '#F19C28', '#2FB551'], + })); }; + useEffect(() => { + updateChartOptionsForTheme(); + + const observer = new MutationObserver(() => { + updateChartOptionsForTheme(); + }); + + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'] + }); + + return () => observer.disconnect(); + }, []); + + const fetchData = async () => { + try { + const data = await fetchYearlyTendersData(); + console.log('API Response:', data); + + if (Array.isArray(data) && data.length > 0) { + const series = [ + { + name: 'Valor Empenhado', + data: data.map((item) => ({ + x: new Date(item.year, 0, 1).toISOString(), + y: item.committed_value || 0 + })), + }, + { + name: 'Valor Liquidado', + data: data.map((item) => ({ + x: new Date(item.year, 0, 1).toISOString(), + y: item.liquidated_value || 0 + })), + }, + { + name: 'Valor Pago', + data: data.map((item) => ({ + x: new Date(item.year, 0, 1).toISOString(), + y: item.paid_value || 0 + })), + }, + ]; + + console.log('Series Data Transformada:', series); + + setSeries(series); + setErrorMessage(null); + } else { + console.error('Dados inválidos retornados pela API.'); + setErrorMessage('Dados inválidos retornados pela API.'); + } + } catch (error) { + console.error('Erro ao buscar dados para o gráfico:', error); + setErrorMessage('Erro ao buscar dados para o gráfico.'); + } + }; + + useEffect(() => { + fetchData(); + }, []); + return ( -
-

+
+

Despesas em Cultura em Minas Gerais ao Longo dos Anos (2002-2023)

{errorMessage &&

{errorMessage}

}
{
  • -
    -
    - Valor Empenhado: Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor. +
    +
    + Valor Empenhado: + Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor.
  • -
    -
    - Valor Liquidado: Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue. +
    +
    + Valor Liquidado: + Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue.
  • -
    -
    - Valor Pago: Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa. +
    +
    + Valor Pago: + Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa.
-
- - +

); }; diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index f03fec1..6456fec 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -64,7 +64,7 @@ export function Header() {
diff --git a/front/src/components/Slider.tsx b/front/src/components/Slider.tsx index b063711..4ee6a9d 100644 --- a/front/src/components/Slider.tsx +++ b/front/src/components/Slider.tsx @@ -32,7 +32,7 @@ const Slider: React.FC = () => { }, []); return ( -
+
{ {data.map((item, index) => ( - Slider + Slider

{item.title}

From dc3f821f955b8ffaa64e31ee1b4a84afb6d498d5 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Fri, 30 Aug 2024 00:04:23 -0300 Subject: [PATCH 10/31] style: alterando o background e text-color de uma guia da header --- front/src/components/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index 6456fec..382d0a9 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -64,7 +64,7 @@ export function Header() {
From 2079cad7ee6a9b3054b6c65aa48d70d38642c466 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Fri, 30 Aug 2024 00:11:13 -0300 Subject: [PATCH 11/31] =?UTF-8?q?fix:=20arrumado=20bug=20que=20n=C3=A3o=20?= =?UTF-8?q?renderizava=20a=20guia=20corretamente=20com=20a=20ferramenta=20?= =?UTF-8?q?de=20acessibilidade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/Header.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index 382d0a9..4d9ccd3 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -14,11 +14,28 @@ export function Header() { const [accessibilityMenuOpen, setAccessibilityMenuOpen] = useState(false); const [keepMenuOpen, setKeepMenuOpen] = useState(false); + // Carrega as configurações do localStorage + useEffect(() => { + const savedDarkMode = localStorage.getItem('darkMode') === 'true'; + const savedHighContrast = localStorage.getItem('highContrast') === 'true'; + const savedFontSize = localStorage.getItem('fontSize') || 'text-base'; + + setDarkMode(savedDarkMode); + setHighContrast(savedHighContrast); + setFontSize(savedFontSize); + }, []); + + // Atualiza as configurações e aplica as classes useEffect(() => { document.documentElement.classList.toggle('dark', darkMode); document.documentElement.classList.toggle('high-contrast', highContrast); document.documentElement.classList.toggle('font-lg', fontSize === 'text-lg'); document.documentElement.classList.toggle('font-original', fontSize === 'text-base'); + + // Salva as configurações no localStorage + localStorage.setItem('darkMode', darkMode.toString()); + localStorage.setItem('highContrast', highContrast.toString()); + localStorage.setItem('fontSize', fontSize); }, [darkMode, highContrast, fontSize]); const toggleNavbar = () => setIsOpen(!isOpen); From 81aa56a77f85f51fbf23f0ab6dc18113fc859906 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Fri, 30 Aug 2024 01:34:33 -0300 Subject: [PATCH 12/31] style: responsividade dos componentes da Home --- front/src/components/Grafico.tsx | 176 +++++++++++++++++++----------- front/src/components/Mensagem.tsx | 11 +- 2 files changed, 117 insertions(+), 70 deletions(-) diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index 9c24893..5b68d9c 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react'; import dynamic from 'next/dynamic'; import { fetchYearlyTendersData } from '../services/api'; import { ApexOptions } from 'apexcharts'; -import { Info } from 'lucide-react'; // Import icon from lucide-react const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); @@ -77,7 +76,7 @@ const Grafico: React.FC = () => { style: { fontSize: '14px', }, - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + formatter: (value: number) => `R$ ${Number(value).toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, }, tickAmount: 4, min: 5000000, @@ -88,18 +87,41 @@ const Grafico: React.FC = () => { }, }, responsive: [ + { + breakpoint: 1024, + options: { + xaxis: { + tickAmount: 5, + min: new Date(2016, 0, 1).getTime(), + max: new Date(2023, 0, 1).getTime(), + labels: { + style: { + fontSize: '10px', + }, + }, + }, + yaxis: { + labels: { + style: { + fontSize: '12px', + }, + }, + }, + legend: { + fontSize: '12px', + }, + } + }, { breakpoint: 768, options: { chart: { height: 400, }, - legend: { - position: 'bottom', - horizontalAlign: 'center', - fontSize: '10px', - }, xaxis: { + tickAmount: 3, + min: new Date(2018, 0, 1).getTime(), + max: new Date(2023, 0, 1).getTime(), labels: { style: { fontSize: '10px', @@ -109,11 +131,15 @@ const Grafico: React.FC = () => { yaxis: { labels: { style: { - fontSize: '6px', + fontSize: '10px', }, - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + formatter: (value: number) => `${(value / 1000000).toFixed(1)}M`, // Formato em milhões }, - tickAmount: 3, + }, + legend: { + position: 'bottom', + horizontalAlign: 'center', + fontSize: '10px', }, } }, @@ -123,66 +149,86 @@ const Grafico: React.FC = () => { chart: { height: 300, }, - legend: { - fontSize: '8px', - }, xaxis: { + tickAmount: 2, + min: new Date(2020, 0, 1).getTime(), + max: new Date(2023, 0, 1).getTime(), labels: { style: { fontSize: '8px', }, }, }, + yaxis: { + labels: { + style: { + fontSize: '8px', + }, + formatter: (value: number) => `${(value / 1000000).toFixed(1)}M`, // Formato em milhões + }, + }, + legend: { + fontSize: '8px', + }, } } ] }); - const updateChartOptionsForTheme = () => { - const isDarkMode = document.documentElement.classList.contains('dark'); - const isHighContrastMode = document.documentElement.classList.contains('high-contrast'); - - setChartOptions((prevOptions) => ({ - ...prevOptions, - chart: { - ...prevOptions.chart, - background: isHighContrastMode ? '#000000' : isDarkMode ? '#1f1f1f' : '#ffffff', - foreColor: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - }, - xaxis: { - ...prevOptions.xaxis, - labels: { - style: { - colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - } - } - }, - yaxis: { - ...prevOptions.yaxis, - labels: { - style: { - colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - }, - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, - } - }, - colors: isHighContrastMode ? ['#ED1C24', '#F19C28', '#2FB551'] : ['#ED1C24', '#F19C28', '#2FB551'], - })); - }; - + const isMobileScreen = () => window.innerWidth <= 768; + useEffect(() => { + const updateChartOptionsForTheme = () => { + const isDarkMode = document.documentElement.classList.contains('dark'); + const isHighContrastMode = document.documentElement.classList.contains('high-contrast'); + + setChartOptions((prevOptions) => ({ + ...prevOptions, + chart: { + ...prevOptions.chart, + background: isHighContrastMode ? '#000000' : isDarkMode ? '#1f1f1f' : '#ffffff', + foreColor: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', + }, + xaxis: { + ...prevOptions.xaxis, + labels: { + style: { + colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', + } + } + }, + yaxis: { + ...prevOptions.yaxis, + labels: { + style: { + colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', + }, + formatter: (value: number) => isMobileScreen() + ? `${(value / 1000000).toFixed(1)}M` + : `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + } + }, + colors: isHighContrastMode ? ['#ED1C24', '#F19C28', '#2FB551'] : ['#ED1C24', '#F19C28', '#2FB551'], + })); + }; + updateChartOptionsForTheme(); - + const observer = new MutationObserver(() => { updateChartOptionsForTheme(); }); - + observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); - - return () => observer.disconnect(); + + window.addEventListener('resize', () => updateChartOptionsForTheme()); + + return () => { + observer.disconnect(); + window.removeEventListener('resize', () => updateChartOptionsForTheme()); + }; }, []); const fetchData = async () => { @@ -214,9 +260,9 @@ const Grafico: React.FC = () => { })), }, ]; - + console.log('Series Data Transformada:', series); - + setSeries(series); setErrorMessage(null); } else { @@ -228,6 +274,7 @@ const Grafico: React.FC = () => { setErrorMessage('Erro ao buscar dados para o gráfico.'); } }; + useEffect(() => { fetchData(); @@ -235,7 +282,7 @@ const Grafico: React.FC = () => { return (
-

+

Despesas em Cultura em Minas Gerais ao Longo dos Anos (2002-2023)

{errorMessage &&

{errorMessage}

} @@ -248,29 +295,28 @@ const Grafico: React.FC = () => { />
    -
  • +
  • -
    - Valor Empenhado: - Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor. +
    + Valor Empenhado: + Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor.
  • -
  • +
  • -
    - Valor Liquidado: - Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue. +
    + Valor Liquidado: + Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue.
  • -
  • +
  • -
    - Valor Pago: - Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa. +
    + Valor Pago: + Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa.
-
); }; diff --git a/front/src/components/Mensagem.tsx b/front/src/components/Mensagem.tsx index 38843e5..f8a6231 100644 --- a/front/src/components/Mensagem.tsx +++ b/front/src/components/Mensagem.tsx @@ -4,7 +4,7 @@ const Mensagem = () => { const [currentText, setCurrentText] = useState(0); const texts = [ - "Simplificando o acesso aos investimentos culturais pelo estado", + "Simplificando o acesso aos investimentos culturais", "Facilitando a visualização de dados culturais", "Promovendo a transparência nos gastos culturais", "Aprimorando o acesso à cultura em Minas Gerais" @@ -19,14 +19,15 @@ const Mensagem = () => { }, [texts.length]); return ( -
-
+
+

Dados oficiais das despesas da Secretaria de Estado de Cultura de Minas Gerais

-
-
+
+ +

{texts[currentText]}

From 3f25106d02230621572a86ad44b7286b8a703e78 Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sat, 31 Aug 2024 18:50:27 -0300 Subject: [PATCH 13/31] fix: Ajuste na logica do backend que fazia com que nao pegasse todos os meses na API --- backend/src/services/tendersMonthService.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/services/tendersMonthService.js b/backend/src/services/tendersMonthService.js index 185be54..1f25a3b 100644 --- a/backend/src/services/tendersMonthService.js +++ b/backend/src/services/tendersMonthService.js @@ -9,11 +9,12 @@ async function getTendersByMonth(start, end) { const { data, error } = await supabase .from('tendersmonth') .select('*') - .gte('year', startYear) - .lte('year', endYear) - .gte('month', startMonth) - .lte('month', endMonth); - + .or( + `and(year.eq.${startYear},month.gte.${startMonth}),` + + `and(year.gt.${startYear},year.lt.${endYear}),` + + `and(year.eq.${endYear},month.lte.${endMonth})` + ); + if (error) { throw new Error(error.message); } @@ -23,5 +24,4 @@ async function getTendersByMonth(start, end) { module.exports = { getTendersByMonth, -}; - +}; \ No newline at end of file From dae0ab89cf3d1f79554c5c8b19c844aefa7e5cd9 Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 14:08:39 -0300 Subject: [PATCH 14/31] fix: Ajustando o Dashboard para puxar os dados da API pelo arquivo da API --- front/src/components/Dashboard.tsx | 22 +++++++++++----------- front/src/services/api.ts | 8 +++----- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/front/src/components/Dashboard.tsx b/front/src/components/Dashboard.tsx index 7b97829..6c8a6f5 100644 --- a/front/src/components/Dashboard.tsx +++ b/front/src/components/Dashboard.tsx @@ -7,9 +7,9 @@ import { CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { ptBR } from 'date-fns/locale'; +import { fettchYearAndMonthTender } from '../services/api'; interface Dados { - id: number; committed_value: number; liquidated_value: number; paid_value: number; @@ -17,7 +17,7 @@ interface Dados { month: number; } -const DashboardWithFilter: React.FC = () => { +const Dashboard: React.FC = () => { const [data, setData] = useState([]); const [lineChartSeries, setLineChartSeries] = useState([]); const [pieChartSeries, setPieChartSeries] = useState([]); @@ -37,13 +37,13 @@ const DashboardWithFilter: React.FC = () => { const endMonth = (endDate.getMonth() + 1).toString().padStart(2, '0'); const endYear = endDate.getFullYear().toString(); - const response = await fetch(`http://localhost:5000/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`); - const data: Dados[] = await response.json(); + const data = await fettchYearAndMonthTender({ startYear, startMonth, endYear, endMonth }); setData(data); - const committedValues = data.map(item => item.committed_value); - const liquidatedValues = data.map(item => item.liquidated_value); - const paidValues = data.map(item => item.paid_value); + const committedValues = data.map((item: Dados) => item.committed_value); + const liquidatedValues = data.map((item: Dados) => item.liquidated_value); + const paidValues = data.map((item: Dados) => item.paid_value); + setLineChartSeries([ { name: 'Valor Empenhado', data: committedValues }, @@ -51,9 +51,9 @@ const DashboardWithFilter: React.FC = () => { { name: 'Valor Pago', data: paidValues } ]); - const totalCommitted = data.reduce((acc, item) => acc + item.committed_value, 0); - const totalLiquidated = data.reduce((acc, item) => acc + item.liquidated_value, 0); - const totalPaid = data.reduce((acc, item) => acc + item.paid_value, 0); + const totalCommitted = data.reduce((acc: number, item: Dados) => acc + item.committed_value, 0); + const totalLiquidated = data.reduce((acc: number, item: Dados) => acc + item.liquidated_value, 0); + const totalPaid = data.reduce((acc: number, item: Dados) => acc + item.paid_value, 0); setPieChartSeries([totalCommitted, totalLiquidated, totalPaid]); setTotalSales(totalCommitted); @@ -234,4 +234,4 @@ const DashboardWithFilter: React.FC = () => { ); }; -export default DashboardWithFilter; \ No newline at end of file +export default Dashboard; \ No newline at end of file diff --git a/front/src/services/api.ts b/front/src/services/api.ts index 6891b7a..642f65f 100644 --- a/front/src/services/api.ts +++ b/front/src/services/api.ts @@ -5,8 +5,6 @@ interface SearchParams { startMonth: string; endYear: string; endMonth: string; - cityId: number; - unitId: string; } export const fetchCities = async () => { @@ -53,10 +51,10 @@ export const fetchYearTender = async (year: number) => { } } -export const searchLicitacoes = async (params: SearchParams) => { - const { startYear, startMonth, endYear, endMonth, cityId, unitId } = params; +export const fettchYearAndMonthTender = async (params: SearchParams) => { + const { startYear, startMonth, endYear, endMonth } = params; - const url = `http://localhost:5000/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}&city=${cityId}`; + const url = `https://minas-cultura-api.onrender.com/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`; try { const response = await axios.get(url); From 61f5cd8172ff82f3bec5cf5bf20d1cf28b94da8a Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 14:11:28 -0300 Subject: [PATCH 15/31] delete: exclundo os exports da api antiga --- front/src/services/api.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/front/src/services/api.ts b/front/src/services/api.ts index 642f65f..715bef9 100644 --- a/front/src/services/api.ts +++ b/front/src/services/api.ts @@ -54,7 +54,7 @@ export const fetchYearTender = async (year: number) => { export const fettchYearAndMonthTender = async (params: SearchParams) => { const { startYear, startMonth, endYear, endMonth } = params; - const url = `https://minas-cultura-api.onrender.com/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`; + const url = `http://localhost:5000/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`; try { const response = await axios.get(url); @@ -64,3 +64,17 @@ export const fettchYearAndMonthTender = async (params: SearchParams) => { throw error; } }; +/* pra quando a API remota estiver com a lógica certa +export const fettchYearAndMonthTender = async (params: SearchParams) => { + const { startYear, startMonth, endYear, endMonth } = params; + + const url = `https://minas-cultura-api.onrender.com/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`; + + try { + const response = await axios.get(url); + return response.data; + } catch (error) { + console.error('Error searching tenders:', error); + throw error; + } +};*/ From 015e0714100c87ec0b0d7fa547b54fe17c39cbfa Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 15:45:13 -0300 Subject: [PATCH 16/31] refactor: Substituicao do componente Dashboard para o componente Filtro --- front/src/app/Pesquisa/page.tsx | 2 - front/src/components/Filtro.tsx | 438 +++++++++++--------------------- 2 files changed, 155 insertions(+), 285 deletions(-) diff --git a/front/src/app/Pesquisa/page.tsx b/front/src/app/Pesquisa/page.tsx index 5936172..89760b4 100644 --- a/front/src/app/Pesquisa/page.tsx +++ b/front/src/app/Pesquisa/page.tsx @@ -1,11 +1,9 @@ import Filtro from "@/components/Filtro"; -import Dashboard from '@/components/Dashboard'; export default function Pesquisa() { return (
-
) } \ No newline at end of file diff --git a/front/src/components/Filtro.tsx b/front/src/components/Filtro.tsx index 77f8509..6c8a6f5 100644 --- a/front/src/components/Filtro.tsx +++ b/front/src/components/Filtro.tsx @@ -1,16 +1,15 @@ 'use client'; + import React, { useState, useEffect } from 'react'; -import Chart from 'react-apexcharts'; +import ApexCharts from 'react-apexcharts'; import { ApexOptions } from 'apexcharts'; -import { fetchCities, fetchUnits, searchLicitacoes } from '../services/api'; -import { MapPin, CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; +import { CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { ptBR } from 'date-fns/locale'; +import { fettchYearAndMonthTender } from '../services/api'; -interface LicitacaoData { - administrative_unit_id: number; - city_id: number; +interface Dados { committed_value: number; liquidated_value: number; paid_value: number; @@ -18,138 +17,60 @@ interface LicitacaoData { month: number; } -const Filtro: React.FC = () => { - const [selectedCityId, setSelectedCityId] = useState(null); - const [selectedUnit, setSelectedUnit] = useState(''); - const [cities, setCities] = useState<{ id: number; name: string }[]>([]); - const [units, setUnits] = useState<{ id: number; name: string }[]>([]); - const [empenhadoSeries, setEmpenhadoSeries] = useState([]); - const [liquidadoSeries, setLiquidadoSeries] = useState([]); - const [pagoSeries, setPagoSeries] = useState([]); +const Dashboard: React.FC = () => { + const [data, setData] = useState([]); + const [lineChartSeries, setLineChartSeries] = useState([]); + const [pieChartSeries, setPieChartSeries] = useState([]); + const [totalSales, setTotalSales] = useState(0); + const [totalRevenue, setTotalRevenue] = useState(0); + const [totalUsers, setTotalUsers] = useState(0); const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); - const [isClient, setIsClient] = useState(false); const [errorMessage, setErrorMessage] = useState(null); - const isHighContrastMode = document.documentElement.classList.contains('high-contrast'); - const [chartOptions, setChartOptions] = useState({ - chart: { - type: 'bar', - background: '#ffffff', - }, - plotOptions: { - bar: { - horizontal: false, - distributed: true, - barHeight: '100%', - colors: { - ranges: [{ from: 0, to: 5000000000, color: '#ED1C24' }], - }, - }, - }, - xaxis: { - categories: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], - type: 'category', - }, - yaxis: { - labels: { - formatter: (value) => `R$ ${value.toLocaleString('pt-BR')}`, // Adiciona o símbolo R$ e formata os números - }, - }, - dataLabels: { - enabled: false, - }, - - legend: { - show: false, - }, - responsive: [ - { breakpoint: 2500, options: { chart: { height: 200, width: 1200 } } }, - { breakpoint: 1025, options: { chart: { height: 424, width: 800 } } }, - { breakpoint: 640, options: { chart: { height: 200, width: 310 } } }, - { breakpoint: 769, options: { chart: { height: 424, width: 700 } } }, - - ], - theme: { - mode: 'light', // Default mode - }, - }); - useEffect(() => { - setIsClient(true); - - const root = document.documentElement; - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.attributeName === "class") { - const isDarkMode = root.classList.contains('dark'); - const isHighContrastMode = root.classList.contains('high-contrast'); - - setChartOptions((prevOptions) => ({ - ...prevOptions, - chart: { - ...prevOptions.chart, - background: isHighContrastMode ? '#000000' : isDarkMode ? '#1f1f1f' : '#ffffff', - foreColor: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - }, - xaxis: { - ...prevOptions.xaxis, - labels: { - style: { - colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - } - } - }, - yaxis: { - ...prevOptions.yaxis, - labels: { - style: { - colors: isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000', - }, - formatter: (value) => `R$ ${value.toLocaleString('pt-BR')}`, // Adiciona o símbolo R$ e formata os números - } - }, - plotOptions: { - ...prevOptions.plotOptions, - bar: { - ...prevOptions.plotOptions?.bar ?? {}, - colors: { - ranges: [{ - from: 0, - to: 5000000000, - color: isHighContrastMode ? '#FFEA00' : isDarkMode ? '#ED1C24' : '#ED1C24', - }] - } - } - } - })); - } - }); - }); - - observer.observe(root, { - attributes: true, - attributeFilter: ['class'] - }); - - return () => observer.disconnect(); - }, []); - const loadCities = async () => { - try { - const cities = await fetchCities(); - setCities(cities); - } catch (error) { - console.error('Erro ao buscar cidades:', error); - } - }; - const loadUnits = async () => { + const fetchData = async (startDate: Date | null, endDate: Date | null) => { + if (!startDate || !endDate) return; + try { - const units = await fetchUnits(); - setUnits(units); + const startMonth = (startDate.getMonth() + 1).toString().padStart(2, '0'); + const startYear = startDate.getFullYear().toString(); + const endMonth = (endDate.getMonth() + 1).toString().padStart(2, '0'); + const endYear = endDate.getFullYear().toString(); + + const data = await fettchYearAndMonthTender({ startYear, startMonth, endYear, endMonth }); + setData(data); + + const committedValues = data.map((item: Dados) => item.committed_value); + const liquidatedValues = data.map((item: Dados) => item.liquidated_value); + const paidValues = data.map((item: Dados) => item.paid_value); + + + setLineChartSeries([ + { name: 'Valor Empenhado', data: committedValues }, + { name: 'Valor Liquidado', data: liquidatedValues }, + { name: 'Valor Pago', data: paidValues } + ]); + + const totalCommitted = data.reduce((acc: number, item: Dados) => acc + item.committed_value, 0); + const totalLiquidated = data.reduce((acc: number, item: Dados) => acc + item.liquidated_value, 0); + const totalPaid = data.reduce((acc: number, item: Dados) => acc + item.paid_value, 0); + + setPieChartSeries([totalCommitted, totalLiquidated, totalPaid]); + setTotalSales(totalCommitted); + setTotalRevenue(totalLiquidated); + setTotalUsers(totalPaid); + + setErrorMessage(null); } catch (error) { - console.error('Erro ao buscar unidades:', error); + console.error('Erro ao buscar dados:', error); + setErrorMessage('Erro ao buscar dados.'); } }; + useEffect(() => { + fetchData(startDate, endDate); + }, [startDate, endDate]); + const renderCustomHeader = ({ date, decreaseYear, @@ -193,173 +114,124 @@ const Filtro: React.FC = () => { } }; - const fetchAndProcessData = async () => { - if (!selectedCityId) return; - - try { - const startMonth = startDate ? (startDate.getMonth() + 1).toString().padStart(2, '0') : ''; - const startYear = startDate ? startDate.getFullYear().toString() : ''; - const endMonth = endDate ? (endDate.getMonth() + 1).toString().padStart(2, '0') : ''; - const endYear = endDate ? endDate.getFullYear().toString() : ''; - - const data: LicitacaoData[] = await searchLicitacoes({ - startYear, - startMonth, - endYear, - endMonth, - cityId: selectedCityId, - unitId: selectedUnit, - }); - - const empData: { [key: number]: number[] } = {}; - const liquidData: { [key: number]: number[] } = {}; - const pagoData: { [key: number]: number[] } = {}; - - const filteredData = selectedUnit - ? data.filter(item => item.administrative_unit_id === Number(selectedUnit)) - : data; - - filteredData.forEach((item: LicitacaoData) => { - const unitId = item.administrative_unit_id; - const month = item.month - 1; - - if (!empData[unitId]) empData[unitId] = new Array(12).fill(0); - if (!liquidData[unitId]) liquidData[unitId] = new Array(12).fill(0); - if (!pagoData[unitId]) pagoData[unitId] = new Array(12).fill(0); - - empData[unitId][month] += item.committed_value; - liquidData[unitId][month] += item.liquidated_value; - pagoData[unitId][month] += item.paid_value; - }); - - const empenhadoSeries: ApexAxisChartSeries = Object.keys(empData).map(key => ({ - name: `Unidade ${key}`, - data: empData[parseInt(key)] - })); - - const liquidadoSeries: ApexAxisChartSeries = Object.keys(liquidData).map(key => ({ - name: `Unidade ${key}`, - data: liquidData[parseInt(key)] - })); - - const pagoSeries: ApexAxisChartSeries = Object.keys(pagoData).map(key => ({ - name: `Unidade ${key}`, - data: pagoData[parseInt(key)] - })); - - setEmpenhadoSeries(empenhadoSeries); - setLiquidadoSeries(liquidadoSeries); - setPagoSeries(pagoSeries); - setErrorMessage(null); - - - } catch (error) { - console.error('Erro ao buscar dados de licitações:', error); - setErrorMessage('Erro ao buscar dados de licitações.'); - } + const lineChartOptions: ApexOptions = { + chart: { + type: 'line', + }, + title: { + text: 'Valores', + align: 'center' + }, + xaxis: { + categories: data.map(item => `${item.year}-${String(item.month).padStart(2, '0')}`) + }, + yaxis: { + title: { + text: 'Valores' + }, + labels: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` + } + }, + colors: ['#ED1C24', '#F19C28', '#2FB551'] }; - useEffect(() => { - loadCities(); - loadUnits(); - }, []); - - useEffect(() => { - if (selectedCityId && startDate && endDate) { - fetchAndProcessData(); + const pieChartOptions: ApexOptions = { + chart: { + type: 'pie', + }, + title: { + text: 'Distribuição de valores', + align: 'center' + }, + labels: ['Valor Empenhado', 'Valor Liquidado', 'Valor Pago'], + tooltip: { + y: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` + } + }, + colors: ['#ED1C24', '#F19C28', '#2FB551'], + legend: { + position: 'bottom', } - }, [selectedCityId, selectedUnit, startDate, endDate]); - + }; return ( - <> -
-

- Pesquise por cidade, período e tema -

-
    -
  • - - -
    -
  • - - {/*
  • - - -
    -
  • */} +
    +
    +
    +

    + Pesquise por período +

    +
      +
    • + + + + +
      +
    • +
    +
    -
  • - - - - -
    -
  • -
-
-
-
-
-

Valor Empenhado

- +
+
+

Total Empenhado

+

R$ {totalSales.toLocaleString()}

+
+
+

Total Liquidado

+

R$ {totalRevenue.toLocaleString()}

-
-

Valor Liquidado

- +
+

Total Pago

+

R$ {totalUsers.toLocaleString()}

-
-

Valor Pago

- +
+
+
+ +
+
+
- +
); }; -export default Filtro; \ No newline at end of file +export default Dashboard; \ No newline at end of file From bc46ce745d9aa8beb4328473a43e8279e8f686c4 Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 15:45:48 -0300 Subject: [PATCH 17/31] test: Ajuste nos testes do componente Filtro --- front/src/testes/Filtro.test.jsx | 60 ++++++++++---------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/front/src/testes/Filtro.test.jsx b/front/src/testes/Filtro.test.jsx index b00965c..fefd71c 100644 --- a/front/src/testes/Filtro.test.jsx +++ b/front/src/testes/Filtro.test.jsx @@ -1,9 +1,8 @@ import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import Filtro from '../components/Filtro'; -import { fetchCities, fetchUnits, searchLicitacoes } from '../services/api'; -import ApexCharts from 'react-apexcharts'; +import Dashboard from '../components/Dashboard'; +import { fettchYearAndMonthTender } from '../services/api'; // Mock das funções de API jest.mock('../services/api'); @@ -15,66 +14,43 @@ jest.mock('react-apexcharts', () => { }; }); -const mockCities = [ - { id: 1, name: 'Cidade 1' }, - { id: 2, name: 'Cidade 2' }, -]; - -const mockUnits = [ - { id: 1, name: 'Unidade 1' }, - { id: 2, name: 'Unidade 2' }, -]; - -const mockLicitacoes = [ +const mockData = [ { - administrative_unit_id: 1, - city_id: 1, committed_value: 1000, liquidated_value: 800, paid_value: 600, year: 2023, month: 1, }, - // Adicione mais dados conforme necessário ]; beforeEach(() => { - fetchCities.mockResolvedValue(mockCities); - fetchUnits.mockResolvedValue(mockUnits); - searchLicitacoes.mockResolvedValue(mockLicitacoes); + fettchYearAndMonthTender.mockResolvedValue(mockData); }); -test('renders Filtro component', async () => { - render(); +test('renders Dashboard component and interacts with date pickers', async () => { + render(); // Verifica se o título está presente - expect(screen.getByText('Pesquise por cidade, período e tema')).toBeInTheDocument(); - - // Verifica se o seletor de cidades está presente - expect(screen.getByText('Selecione uma cidade')).toBeInTheDocument(); + expect(screen.getByText('Pesquise por período')).toBeInTheDocument(); - // Aguarda a carga das cidades - await waitFor(() => expect(fetchCities).toHaveBeenCalled()); - - // Simula a seleção de uma cidade - fireEvent.change(screen.getByRole('combobox'), { target: { value: '1' } }); - - // Verifica se a cidade foi selecionada - expect(screen.getByRole('combobox')).toHaveValue('1'); + // Verifica se os campos de data inicial e final estão presentes + expect(screen.getByPlaceholderText('Data Inicial')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Data final')).toBeInTheDocument(); // Simula a seleção de datas fireEvent.change(screen.getByPlaceholderText('Data Inicial'), { target: { value: '01 / 2023' } }); fireEvent.change(screen.getByPlaceholderText('Data final'), { target: { value: '12 / 2023' } }); - // Aguarda a busca de licitações - await waitFor(() => expect(searchLicitacoes).toHaveBeenCalled()); + // Aguarda a busca de dados + await waitFor(() => expect(fettchYearAndMonthTender).toHaveBeenCalled()); - // Verifica se os gráficos são renderizados - expect(screen.getByText('Valor Empenhado')).toBeInTheDocument(); - expect(screen.getByText('Valor Liquidado')).toBeInTheDocument(); - expect(screen.getByText('Valor Pago')).toBeInTheDocument(); + // Verifica se os valores totais são exibidos + expect(screen.getByText('Total Empenhado')).toBeInTheDocument(); + expect(screen.getByText('Total Liquidado')).toBeInTheDocument(); + expect(screen.getByText('Total Pago')).toBeInTheDocument(); - // Verifica se há três gráficos renderizados + // Verifica se os gráficos são renderizados const charts = screen.getAllByTestId('mock-apexcharts'); - expect(charts).toHaveLength(3); + expect(charts).toHaveLength(2); // O Dashboard tem dois gráficos }); \ No newline at end of file From ca10707df403bd713b4c14d626259a42da5c069d Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 15:47:45 -0300 Subject: [PATCH 18/31] delete: Remocao do componente Dashboard --- front/src/components/Dashboard.tsx | 237 ----------------------------- 1 file changed, 237 deletions(-) delete mode 100644 front/src/components/Dashboard.tsx diff --git a/front/src/components/Dashboard.tsx b/front/src/components/Dashboard.tsx deleted file mode 100644 index 6c8a6f5..0000000 --- a/front/src/components/Dashboard.tsx +++ /dev/null @@ -1,237 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import ApexCharts from 'react-apexcharts'; -import { ApexOptions } from 'apexcharts'; -import { CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; -import DatePicker from 'react-datepicker'; -import 'react-datepicker/dist/react-datepicker.css'; -import { ptBR } from 'date-fns/locale'; -import { fettchYearAndMonthTender } from '../services/api'; - -interface Dados { - committed_value: number; - liquidated_value: number; - paid_value: number; - year: number; - month: number; -} - -const Dashboard: React.FC = () => { - const [data, setData] = useState([]); - const [lineChartSeries, setLineChartSeries] = useState([]); - const [pieChartSeries, setPieChartSeries] = useState([]); - const [totalSales, setTotalSales] = useState(0); - const [totalRevenue, setTotalRevenue] = useState(0); - const [totalUsers, setTotalUsers] = useState(0); - const [startDate, setStartDate] = useState(null); - const [endDate, setEndDate] = useState(null); - const [errorMessage, setErrorMessage] = useState(null); - - const fetchData = async (startDate: Date | null, endDate: Date | null) => { - if (!startDate || !endDate) return; - - try { - const startMonth = (startDate.getMonth() + 1).toString().padStart(2, '0'); - const startYear = startDate.getFullYear().toString(); - const endMonth = (endDate.getMonth() + 1).toString().padStart(2, '0'); - const endYear = endDate.getFullYear().toString(); - - const data = await fettchYearAndMonthTender({ startYear, startMonth, endYear, endMonth }); - setData(data); - - const committedValues = data.map((item: Dados) => item.committed_value); - const liquidatedValues = data.map((item: Dados) => item.liquidated_value); - const paidValues = data.map((item: Dados) => item.paid_value); - - - setLineChartSeries([ - { name: 'Valor Empenhado', data: committedValues }, - { name: 'Valor Liquidado', data: liquidatedValues }, - { name: 'Valor Pago', data: paidValues } - ]); - - const totalCommitted = data.reduce((acc: number, item: Dados) => acc + item.committed_value, 0); - const totalLiquidated = data.reduce((acc: number, item: Dados) => acc + item.liquidated_value, 0); - const totalPaid = data.reduce((acc: number, item: Dados) => acc + item.paid_value, 0); - - setPieChartSeries([totalCommitted, totalLiquidated, totalPaid]); - setTotalSales(totalCommitted); - setTotalRevenue(totalLiquidated); - setTotalUsers(totalPaid); - - setErrorMessage(null); - } catch (error) { - console.error('Erro ao buscar dados:', error); - setErrorMessage('Erro ao buscar dados.'); - } - }; - - useEffect(() => { - fetchData(startDate, endDate); - }, [startDate, endDate]); - - const renderCustomHeader = ({ - date, - decreaseYear, - increaseYear, - }: { - date: Date; - decreaseYear: () => void; - increaseYear: () => void; - }) => { - const months = [ - 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', - 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' - ]; - return ( -
- - - {months[date.getMonth()]} de {date.getFullYear()} - - -
- ); - }; - - const handleStartDateChange = (date: Date | null) => { - if (date && endDate && date > endDate) { - setEndDate(date); - } - setStartDate(date); - }; - - const handleEndDateChange = (date: Date | null) => { - if (date && startDate && date < startDate) { - setEndDate(startDate); - } else { - setEndDate(date); - } - }; - - const lineChartOptions: ApexOptions = { - chart: { - type: 'line', - }, - title: { - text: 'Valores', - align: 'center' - }, - xaxis: { - categories: data.map(item => `${item.year}-${String(item.month).padStart(2, '0')}`) - }, - yaxis: { - title: { - text: 'Valores' - }, - labels: { - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` - } - }, - colors: ['#ED1C24', '#F19C28', '#2FB551'] - }; - - const pieChartOptions: ApexOptions = { - chart: { - type: 'pie', - }, - title: { - text: 'Distribuição de valores', - align: 'center' - }, - labels: ['Valor Empenhado', 'Valor Liquidado', 'Valor Pago'], - tooltip: { - y: { - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` - } - }, - colors: ['#ED1C24', '#F19C28', '#2FB551'], - legend: { - position: 'bottom', - } - }; - - return ( -
-
-
-

- Pesquise por período -

-
    -
  • - - - - -
    -
  • -
-
- - - -
-
-

Total Empenhado

-

R$ {totalSales.toLocaleString()}

-
-
-

Total Liquidado

-

R$ {totalRevenue.toLocaleString()}

-
-
-

Total Pago

-

R$ {totalUsers.toLocaleString()}

-
-
-
-
- -
-
- -
-
-
-
- ); -}; - -export default Dashboard; \ No newline at end of file From dd70e03be1126773e5381a1f6144b2ceb8477cb8 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Sun, 1 Sep 2024 16:47:15 -0300 Subject: [PATCH 19/31] chore: mudando texto do guia sobre e trabalhando responsividade --- front/src/app/page.tsx | 7 ++-- front/src/components/Grafico.tsx | 16 ++++---- front/src/components/Informacoes.tsx | 12 +++++- front/src/components/Integrantes.tsx | 2 +- front/src/components/Pilares.tsx | 18 ++++----- front/src/components/Slider.tsx | 59 ++++++++++++++++------------ 6 files changed, 65 insertions(+), 49 deletions(-) diff --git a/front/src/app/page.tsx b/front/src/app/page.tsx index 8960c47..2eb0581 100644 --- a/front/src/app/page.tsx +++ b/front/src/app/page.tsx @@ -12,11 +12,12 @@ export default function Home() { return (
-
- -
+ +
+ +
); } diff --git a/front/src/components/Grafico.tsx b/front/src/components/Grafico.tsx index 5b68d9c..91b4c73 100644 --- a/front/src/components/Grafico.tsx +++ b/front/src/components/Grafico.tsx @@ -282,8 +282,8 @@ const Grafico: React.FC = () => { return (
-

- Despesas em Cultura em Minas Gerais ao Longo dos Anos (2002-2023) +

+ Despesas em Cultura em Minas Gerais ao Longo dos Anos (2002-2023)

{errorMessage &&

{errorMessage}

}
@@ -295,22 +295,22 @@ const Grafico: React.FC = () => { />
    -
  • -
    +
  • +
    Valor Empenhado: Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor.
  • -
  • -
    +
  • +
    Valor Liquidado: Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue.
  • -
  • -
    +
  • +
    Valor Pago: Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa. diff --git a/front/src/components/Informacoes.tsx b/front/src/components/Informacoes.tsx index b56d5a5..619b945 100644 --- a/front/src/components/Informacoes.tsx +++ b/front/src/components/Informacoes.tsx @@ -20,8 +20,16 @@ const Informacoes = () => {

    Este é um projeto desenvolvido como parte da disciplina de Métodos de Desenvolvimento de Software (MDS) da Universidade de Brasília (UnB). O objetivo principal deste projeto é criar uma plataforma online para análise e - armazenamento de dados de licitações relacionadas aos gastos culturais apoiados pelo Governo Federal, utilizando a - plataforma e a API do Querido Diário. + armazenamento de dados de licitações relacionadas aos gastos culturais apoiados pelo Governo Federal, utilizando + informações da Secretaria de Cultura do Estado de Minas Gerais. +

    +

    +

    + A plataforma permite aos usuários filtrar dados por um intervalo de tempo para obter informações de interesse, + além de oferecer um dashboard informativo que facilita a consulta e visualização dos dados. Adicionalmente, + a plataforma busca abranger a comunidade com funcionalidades como modo noturno, alto contraste e aumento de + fonte para melhorar a acessibilidade, além de permitir o acesso via dispositivos móveis, garantindo + disponibilidade em várias plataformas.

diff --git a/front/src/components/Integrantes.tsx b/front/src/components/Integrantes.tsx index 8f6036f..875505a 100644 --- a/front/src/components/Integrantes.tsx +++ b/front/src/components/Integrantes.tsx @@ -9,7 +9,7 @@ import Image from "next/image"; const Integrantes = () => { return (
-
+
diff --git a/front/src/components/Pilares.tsx b/front/src/components/Pilares.tsx index 9d1672a..5739580 100644 --- a/front/src/components/Pilares.tsx +++ b/front/src/components/Pilares.tsx @@ -17,17 +17,17 @@ const Pilares = () => { A Secretaria de Cultura e Turismo do Estado de Minas Gerais:
-
-

Deve preservar

-

o patrimônio cultural do estado de Minas gerais

+
+

Deve preservar

+

patrimônio cultural do estado de Minas gerais

-
-

Deve promover

-

a acessibilidade e inclusão social à cultura

+
+

Deve promover

+

acessibilidade e inclusão social à cultura

-
-

Deve fomentar

-

a produção artística da população

+
+

Deve fomentar

+

produção artística da população

diff --git a/front/src/components/Slider.tsx b/front/src/components/Slider.tsx index 4ee6a9d..1cffac9 100644 --- a/front/src/components/Slider.tsx +++ b/front/src/components/Slider.tsx @@ -32,33 +32,40 @@ const Slider: React.FC = () => { }, []); return ( -
- - {data.map((item, index) => ( - - - Slider + ); }; From cc61b8e41ddc9a2e452d3612b29d84773dd168eb Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Sun, 1 Sep 2024 16:47:41 -0300 Subject: [PATCH 20/31] =?UTF-8?q?fix:=20atualizando=20endere=C3=A7o=20da?= =?UTF-8?q?=20requisi=C3=A7=C3=A3o=20do=20gr=C3=A1fico=20de=20anos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/services/api.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/front/src/services/api.ts b/front/src/services/api.ts index 6891b7a..6a05a93 100644 --- a/front/src/services/api.ts +++ b/front/src/services/api.ts @@ -33,7 +33,7 @@ export const fetchYearlyTendersData = async () => { try { const requests = []; for (let year = 2002; year <= 2023; year++) { - requests.push(axios.get(`http://localhost:5000/tenders/year?year=${year}`)); + requests.push(axios.get(`https://minas-cultura-api.onrender.com/tenders/year?year=${year}`)); } const responses = await Promise.all(requests); return responses.flatMap((response) => response.data); @@ -43,16 +43,6 @@ export const fetchYearlyTendersData = async () => { } }; -export const fetchYearTender = async (year: number) => { - try { - const response = await axios.get(`http://localhost:5000/tenders/year?year=${year}`); - return response.data; - } catch (error) { - console.error('Error fetching year tender data:', error); - return []; - } -} - export const searchLicitacoes = async (params: SearchParams) => { const { startYear, startMonth, endYear, endMonth, cityId, unitId } = params; From f6998b71f3d9aa1204f19d5780bba39007bc9c8b Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Sun, 1 Sep 2024 16:55:49 -0300 Subject: [PATCH 21/31] =?UTF-8?q?remove:=20fun=C3=A7=C3=A3o=20fetch=20Year?= =?UTF-8?q?lyTender?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/services/api.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/front/src/services/api.ts b/front/src/services/api.ts index c837458..acc7a04 100644 --- a/front/src/services/api.ts +++ b/front/src/services/api.ts @@ -41,16 +41,6 @@ export const fetchYearlyTendersData = async () => { } }; -export const fetchYearTender = async (year: number) => { - try { - const response = await axios.get(`http://localhost:5000/tenders/year?year=${year}`); - return response.data; - } catch (error) { - console.error('Error fetching year tender data:', error); - return []; - } -} - export const fettchYearAndMonthTender = async (params: SearchParams) => { const { startYear, startMonth, endYear, endMonth } = params; From c41eb37fb28dae48440b1484957be6296e825cc2 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Sun, 1 Sep 2024 18:00:48 -0300 Subject: [PATCH 22/31] =?UTF-8?q?feat:=20adicionando=20op=C3=A7=C3=B5es=20?= =?UTF-8?q?de=20acessibilidade=20no=20Filtro=20e=20refatorando=20gr=C3=A1f?= =?UTF-8?q?ico=20de=20linhas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/Filtro.tsx | 480 +++++++++++++++++++++++--------- 1 file changed, 345 insertions(+), 135 deletions(-) diff --git a/front/src/components/Filtro.tsx b/front/src/components/Filtro.tsx index 6c8a6f5..bf2c7e5 100644 --- a/front/src/components/Filtro.tsx +++ b/front/src/components/Filtro.tsx @@ -1,7 +1,7 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import ApexCharts from 'react-apexcharts'; +import dynamic from 'next/dynamic'; import { ApexOptions } from 'apexcharts'; import { CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; import DatePicker from 'react-datepicker'; @@ -17,6 +17,8 @@ interface Dados { month: number; } +const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); + const Dashboard: React.FC = () => { const [data, setData] = useState([]); const [lineChartSeries, setLineChartSeries] = useState([]); @@ -28,6 +30,163 @@ const Dashboard: React.FC = () => { const [endDate, setEndDate] = useState(null); const [errorMessage, setErrorMessage] = useState(null); + // Inicialização dos estados dos gráficos com opções padrão + const [lineChartOptions, setLineChartOptions] = useState({ + chart: { + type: 'line', + height: 350, + toolbar: { + show: true, + }, + zoom: { + enabled: true, + }, + animations: { + enabled: true, + }, + background: '#ffffff', + foreColor: '#000000', + }, + stroke: { + curve: 'smooth', + width: 2, + }, + dataLabels: { + enabled: false, + }, + markers: { + size: 4, + }, + xaxis: { + categories: [], + labels: { + style: { + fontSize: '12px', + }, + }, + tickAmount: 10, + }, + yaxis: { + title: { + text: 'Valores', + style: { + fontSize: '14px', + }, + }, + labels: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + style: { + fontSize: '12px', + }, + }, + }, + legend: { + position: 'top', + horizontalAlign: 'center', + fontSize: '14px', + }, + tooltip: { + y: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + }, + }, + colors: ['#ED1C24', '#F19C28', '#2FB551'], + responsive: [ + { + breakpoint: 1024, + options: { + xaxis: { + labels: { + style: { + fontSize: '10px', + }, + }, + }, + yaxis: [ + { + labels: { + style: { + fontSize: '10px', + }, + }, + }, + ], + legend: { + fontSize: '12px', + }, + }, + }, + { + breakpoint: 768, + options: { + chart: { + height: 300, + }, + xaxis: { + labels: { + style: { + fontSize: '8px', + }, + }, + }, + yaxis: [ + { + labels: { + style: { + fontSize: '8px', + }, + }, + }, + ], + legend: { + fontSize: '10px', + }, + }, + }, + ], + }); + + const [pieChartOptions, setPieChartOptions] = useState({ + chart: { + type: 'pie', + height: 350, + background: '#ffffff', + foreColor: '#000000', + }, + labels: ['Valor Empenhado', 'Valor Liquidado', 'Valor Pago'], + legend: { + position: 'bottom', + fontSize: '14px', + }, + tooltip: { + y: { + formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`, + }, + }, + colors: ['#ED1C24', '#F19C28', '#2FB551'], + responsive: [ + { + breakpoint: 1024, + options: { + legend: { + fontSize: '12px', + }, + }, + }, + { + breakpoint: 768, + options: { + chart: { + height: 300, + }, + legend: { + fontSize: '10px', + }, + }, + }, + ], + }); + const fetchData = async (startDate: Date | null, endDate: Date | null) => { if (!startDate || !endDate) return; @@ -37,30 +196,43 @@ const Dashboard: React.FC = () => { const endMonth = (endDate.getMonth() + 1).toString().padStart(2, '0'); const endYear = endDate.getFullYear().toString(); - const data = await fettchYearAndMonthTender({ startYear, startMonth, endYear, endMonth }); - setData(data); + const fetchedData = await fettchYearAndMonthTender({ startYear, startMonth, endYear, endMonth }); + + if (Array.isArray(fetchedData) && fetchedData.length > 0) { + setData(fetchedData); - const committedValues = data.map((item: Dados) => item.committed_value); - const liquidatedValues = data.map((item: Dados) => item.liquidated_value); - const paidValues = data.map((item: Dados) => item.paid_value); + const categories = fetchedData.map(item => `${item.year}-${String(item.month).padStart(2, '0')}`); + const committedValues = fetchedData.map((item: Dados) => item.committed_value); + const liquidatedValues = fetchedData.map((item: Dados) => item.liquidated_value); + const paidValues = fetchedData.map((item: Dados) => item.paid_value); + setLineChartSeries([ + { name: 'Valor Empenhado', data: committedValues }, + { name: 'Valor Liquidado', data: liquidatedValues }, + { name: 'Valor Pago', data: paidValues }, + ]); - setLineChartSeries([ - { name: 'Valor Empenhado', data: committedValues }, - { name: 'Valor Liquidado', data: liquidatedValues }, - { name: 'Valor Pago', data: paidValues } - ]); + setLineChartOptions(prevOptions => ({ + ...prevOptions, + xaxis: { + ...prevOptions.xaxis, + categories, + }, + })); - const totalCommitted = data.reduce((acc: number, item: Dados) => acc + item.committed_value, 0); - const totalLiquidated = data.reduce((acc: number, item: Dados) => acc + item.liquidated_value, 0); - const totalPaid = data.reduce((acc: number, item: Dados) => acc + item.paid_value, 0); + const totalCommitted = committedValues.reduce((acc, val) => acc + val, 0); + const totalLiquidated = liquidatedValues.reduce((acc, val) => acc + val, 0); + const totalPaid = paidValues.reduce((acc, val) => acc + val, 0); - setPieChartSeries([totalCommitted, totalLiquidated, totalPaid]); - setTotalSales(totalCommitted); - setTotalRevenue(totalLiquidated); - setTotalUsers(totalPaid); + setPieChartSeries([totalCommitted, totalLiquidated, totalPaid]); + setTotalSales(totalCommitted); + setTotalRevenue(totalLiquidated); + setTotalUsers(totalPaid); - setErrorMessage(null); + setErrorMessage(null); + } else { + setErrorMessage('Nenhum dado encontrado para o período selecionado.'); + } } catch (error) { console.error('Erro ao buscar dados:', error); setErrorMessage('Erro ao buscar dados.'); @@ -71,28 +243,107 @@ const Dashboard: React.FC = () => { fetchData(startDate, endDate); }, [startDate, endDate]); + const updateChartOptionsForTheme = () => { + const isDarkMode = document.documentElement.classList.contains('dark'); + const isHighContrastMode = document.documentElement.classList.contains('high-contrast'); + + const backgroundColor = isHighContrastMode ? '#000000' : isDarkMode ? '#1f1f1f' : '#ffffff'; + const foreColor = isHighContrastMode ? '#FFFFFF' : isDarkMode ? '#e5e7eb' : '#000000'; + + setLineChartOptions(prevOptions => ({ + ...prevOptions, + chart: { + ...prevOptions.chart, + background: backgroundColor, + foreColor: foreColor, + }, + xaxis: { + ...prevOptions.xaxis, + labels: { + ...prevOptions.xaxis?.labels, + style: { + ...prevOptions.xaxis?.labels?.style, + colors: foreColor, + }, + }, + }, + yaxis: { + ...prevOptions.yaxis, + labels: { + ...(Array.isArray(prevOptions.yaxis) ? {} : prevOptions.yaxis?.labels), + style: { + ...(Array.isArray(prevOptions.yaxis) ? {} : prevOptions.yaxis?.labels?.style), + colors: foreColor, + }, + }, + }, + legend: { + ...prevOptions.legend, + labels: { + colors: foreColor, + }, + }, + })); + + setPieChartOptions(prevOptions => ({ + ...prevOptions, + chart: { + ...prevOptions.chart, + background: backgroundColor, + foreColor: foreColor, + }, + legend: { + ...prevOptions.legend, + labels: { + colors: foreColor, + }, + }, + })); + }; + + useEffect(() => { + updateChartOptionsForTheme(); + + const observer = new MutationObserver(() => { + updateChartOptionsForTheme(); + }); + + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'], + }); + + return () => { + observer.disconnect(); + }; + }, []); + const renderCustomHeader = ({ date, + decreaseMonth, + increaseMonth, decreaseYear, increaseYear, }: { date: Date; + decreaseMonth: () => void; + increaseMonth: () => void; decreaseYear: () => void; increaseYear: () => void; }) => { const months = [ 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', - 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' + 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro', ]; return (
- {months[date.getMonth()]} de {date.getFullYear()} -
@@ -108,130 +359,89 @@ const Dashboard: React.FC = () => { const handleEndDateChange = (date: Date | null) => { if (date && startDate && date < startDate) { - setEndDate(startDate); - } else { - setEndDate(date); - } - }; - - const lineChartOptions: ApexOptions = { - chart: { - type: 'line', - }, - title: { - text: 'Valores', - align: 'center' - }, - xaxis: { - categories: data.map(item => `${item.year}-${String(item.month).padStart(2, '0')}`) - }, - yaxis: { - title: { - text: 'Valores' - }, - labels: { - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` - } - }, - colors: ['#ED1C24', '#F19C28', '#2FB551'] - }; - - const pieChartOptions: ApexOptions = { - chart: { - type: 'pie', - }, - title: { - text: 'Distribuição de valores', - align: 'center' - }, - labels: ['Valor Empenhado', 'Valor Liquidado', 'Valor Pago'], - tooltip: { - y: { - formatter: (value: number) => `R$ ${value.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}` - } - }, - colors: ['#ED1C24', '#F19C28', '#2FB551'], - legend: { - position: 'bottom', + setStartDate(date); } + setEndDate(date); }; return ( -
-
-
-

- Pesquise por período -

-
    -
  • - - - - -
    -
  • -
-
- - - -
-
-

Total Empenhado

-

R$ {totalSales.toLocaleString()}

-
-
-

Total Liquidado

-

R$ {totalRevenue.toLocaleString()}

-
-
-

Total Pago

-

R$ {totalUsers.toLocaleString()}

-
-
-
-
- +
+

+ Pesquise por período +

+
+
+ +
-
- até +
+ +
+ {errorMessage &&

{errorMessage}

} +
+ +
+
+

Total Empenhado

+

R$ {totalSales.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}

+
+
+

Total Liquidado

+

R$ {totalRevenue.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}

+
+
+

Total Pago

+

R$ {totalUsers.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}

+
+
+ +
+
+ +
+
+ +
); }; -export default Dashboard; \ No newline at end of file +export default Dashboard; From 6df7fcc35efca042297e701e42cb75c35a05e9c6 Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 18:42:06 -0300 Subject: [PATCH 23/31] test: Ajuste nos testes do Filtro --- front/src/testes/Filtro.test.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/front/src/testes/Filtro.test.jsx b/front/src/testes/Filtro.test.jsx index fefd71c..1810409 100644 --- a/front/src/testes/Filtro.test.jsx +++ b/front/src/testes/Filtro.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import Dashboard from '../components/Dashboard'; +import Filtro from '../components/Filtro'; import { fettchYearAndMonthTender } from '../services/api'; // Mock das funções de API @@ -29,18 +29,18 @@ beforeEach(() => { }); test('renders Dashboard component and interacts with date pickers', async () => { - render(); + render(); // Verifica se o título está presente expect(screen.getByText('Pesquise por período')).toBeInTheDocument(); // Verifica se os campos de data inicial e final estão presentes expect(screen.getByPlaceholderText('Data Inicial')).toBeInTheDocument(); - expect(screen.getByPlaceholderText('Data final')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Data Final')).toBeInTheDocument(); // Simula a seleção de datas - fireEvent.change(screen.getByPlaceholderText('Data Inicial'), { target: { value: '01 / 2023' } }); - fireEvent.change(screen.getByPlaceholderText('Data final'), { target: { value: '12 / 2023' } }); + fireEvent.change(screen.getByPlaceholderText('Data Inicial'), { target: { value: '01/2023' } }); + fireEvent.change(screen.getByPlaceholderText('Data Final'), { target: { value: '12/2023' } }); // Aguarda a busca de dados await waitFor(() => expect(fettchYearAndMonthTender).toHaveBeenCalled()); From e0c647050667dd3db68333a44d0022c45fb28be7 Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 19:22:50 -0300 Subject: [PATCH 24/31] test: Ajuste no teste do componente informacoes --- front/src/testes/Informacoes.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/testes/Informacoes.test.jsx b/front/src/testes/Informacoes.test.jsx index 58eb6f2..eaf708e 100644 --- a/front/src/testes/Informacoes.test.jsx +++ b/front/src/testes/Informacoes.test.jsx @@ -16,7 +16,7 @@ describe('Informacoes', () => { expect(title).toBeInTheDocument(); // Verifica se o parágrafo é renderizado - const paragraphText = "Este é um projeto desenvolvido como parte da disciplina de Métodos de Desenvolvimento de Software (MDS) da Universidade de Brasília (UnB). O objetivo principal deste projeto é criar uma plataforma online para análise e armazenamento de dados de licitações relacionadas aos gastos culturais apoiados pelo Governo Federal, utilizando a plataforma e a API do Querido Diário."; + const paragraphText = "Este é um projeto desenvolvido como parte da disciplina de Métodos de Desenvolvimento de Software (MDS) da Universidade de Brasília (UnB). O objetivo principal deste projeto é criar uma plataforma online para análise e armazenamento de dados de licitações relacionadas aos gastos culturais apoiados pelo Governo Federal, utilizando informações da Secretaria de Cultura do Estado de Minas Gerais."; const paragraph = screen.getByText((content, element) => { const hasText = element => element.textContent === paragraphText; const elementHasText = hasText(element); From fc64f2a2a84ace5a1467e1dfede731b9740eaa3a Mon Sep 17 00:00:00 2001 From: Mateushqms Date: Sun, 1 Sep 2024 19:23:23 -0300 Subject: [PATCH 25/31] test: Ajuste no teste do componente Pilares --- front/src/testes/Pilares.test.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/testes/Pilares.test.jsx b/front/src/testes/Pilares.test.jsx index d8f92a8..c3e8b14 100644 --- a/front/src/testes/Pilares.test.jsx +++ b/front/src/testes/Pilares.test.jsx @@ -13,19 +13,19 @@ describe('Pilares', () => { // Check if the first section is rendered const firstHeading = screen.getByText(/Deve preservar/i); - const firstSubheading = screen.getByText(/o patrimônio cultural do estado de Minas Gerais/i); + const firstSubheading = screen.getByText(/patrimônio cultural do estado de Minas Gerais/i); expect(firstHeading).toBeInTheDocument(); expect(firstSubheading).toBeInTheDocument(); // Check if the second section is rendered const secondHeading = screen.getByText(/Deve promover/i); - const secondSubheading = screen.getByText(/a acessibilidade e inclusão social à cultura/i); + const secondSubheading = screen.getByText(/acessibilidade e inclusão social à cultura/i); expect(secondHeading).toBeInTheDocument(); expect(secondSubheading).toBeInTheDocument(); // Check if the third section is rendered const thirdHeading = screen.getByText(/Deve fomentar/i); - const thirdSubheading = screen.getByText(/a produção artística da população/i); + const thirdSubheading = screen.getByText(/produção artística da população/i); expect(thirdHeading).toBeInTheDocument(); expect(thirdSubheading).toBeInTheDocument(); }); From ba15b5344f3bbf8d4bf4b909deed616e76114169 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Mon, 2 Sep 2024 00:28:01 -0300 Subject: [PATCH 26/31] style: arrumando a sensibilidade do componente busca --- front/src/components/Busca.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/front/src/components/Busca.tsx b/front/src/components/Busca.tsx index 98450b4..0b09e79 100644 --- a/front/src/components/Busca.tsx +++ b/front/src/components/Busca.tsx @@ -5,21 +5,21 @@ import Link from 'next/link'; const Busca = () => { return ( -
+
Lupa1 -
-

- Faça sua busca
filtrada
: -

- Veja os dados para cada município do Estado de
Minas Gerais -

+
+

+ Faça sua busca
filtrada
:

+

+ Veja os dados para cada município do Estado de
Minas Gerais +

- From a2e8c4e5d757a6b395ccb6f1b6b5cd7a6ab24c5d Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Mon, 2 Sep 2024 00:48:05 -0300 Subject: [PATCH 27/31] feat: adicionando HelpCircle explicativo --- front/src/components/Filtro.tsx | 58 +++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/front/src/components/Filtro.tsx b/front/src/components/Filtro.tsx index bf2c7e5..99e15ec 100644 --- a/front/src/components/Filtro.tsx +++ b/front/src/components/Filtro.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react'; import dynamic from 'next/dynamic'; import { ApexOptions } from 'apexcharts'; -import { CalendarClock, MoveLeft, MoveRight } from 'lucide-react'; +import { CalendarClock, MoveLeft, MoveRight, HelpCircle } from 'lucide-react'; // Importação do HelpCircle import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { ptBR } from 'date-fns/locale'; @@ -408,17 +408,61 @@ const Dashboard: React.FC = () => {
+ {/* Total Empenhado */}
-

Total Empenhado

-

R$ {totalSales.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}

+

+ Total Empenhado + {/* Tooltip */} +
+ +
+
+ Valor Empenhado: Valor do orçamento reservado para fazer face a compromisso formalmente assumido com fornecedor ou credor. +
+
+
+

+

+ R$ {totalSales.toLocaleString('pt-BR', { minimumFractionDigits: 2 })} +

+ + {/* Total Liquidado */}
-

Total Liquidado

-

R$ {totalRevenue.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}

+

+ Total Liquidado + {/* Tooltip */} +
+ +
+
+ Valor Liquidado: Valor que o fornecedor ou credor tem direito a receber referente a produto ou serviço devidamente entregue. +
+
+
+

+

+ R$ {totalRevenue.toLocaleString('pt-BR', { minimumFractionDigits: 2 })} +

+ + {/* Total Pago */}
-

Total Pago

-

R$ {totalUsers.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}

+

+ Total Pago + {/* Tooltip */} +
+ +
+
+ Valor Pago: Valor referente aos pagamentos efetuados através de movimentações bancárias, escriturais e apropriação contábil da despesa. +
+
+
+

+

+ R$ {totalUsers.toLocaleString('pt-BR', { minimumFractionDigits: 2 })} +

From 4e188e93ac7597107cc72eed1f43859312a09f5e Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Mon, 2 Sep 2024 01:07:42 -0300 Subject: [PATCH 28/31] fix: arrumando link do componente Integrantes --- front/src/components/Integrantes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/components/Integrantes.tsx b/front/src/components/Integrantes.tsx index 875505a..c495d51 100644 --- a/front/src/components/Integrantes.tsx +++ b/front/src/components/Integrantes.tsx @@ -90,7 +90,7 @@ const Integrantes = () => { -
+ From 8d15cc77617bc85b2dab2864b214a898f9bc5496 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Mon, 2 Sep 2024 10:10:25 -0300 Subject: [PATCH 29/31] chore: adicionando link ao site da UnB --- front/src/components/Filtro.tsx | 2 +- front/src/components/Footer.tsx | 10 ++++++---- front/src/components/Informacoes.tsx | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/front/src/components/Filtro.tsx b/front/src/components/Filtro.tsx index 99e15ec..cff4e22 100644 --- a/front/src/components/Filtro.tsx +++ b/front/src/components/Filtro.tsx @@ -1,4 +1,4 @@ -'use client'; +"use client"; import React, { useState, useEffect } from 'react'; import dynamic from 'next/dynamic'; diff --git a/front/src/components/Footer.tsx b/front/src/components/Footer.tsx index 55ff157..5b2ac72 100644 --- a/front/src/components/Footer.tsx +++ b/front/src/components/Footer.tsx @@ -27,10 +27,12 @@ const Footer = () => {
- unblogo + + unblogo + { Este é um projeto desenvolvido como parte da disciplina de Métodos de Desenvolvimento de Software (MDS) da Universidade de Brasília (UnB). O objetivo principal deste projeto é criar uma plataforma online para análise e armazenamento de dados de licitações relacionadas aos gastos culturais apoiados pelo Governo Federal, utilizando - informações da Secretaria de Cultura do Estado de Minas Gerais. + informações da Secretaria de Cultura do Estado de Minas Gerais.

- A plataforma permite aos usuários filtrar dados por um intervalo de tempo para obter informações de interesse, +

A plataforma permite aos usuários filtrar dados por um intervalo de tempo para obter informações de interesse, além de oferecer um dashboard informativo que facilita a consulta e visualização dos dados. Adicionalmente, a plataforma busca abranger a comunidade com funcionalidades como modo noturno, alto contraste e aumento de fonte para melhorar a acessibilidade, além de permitir o acesso via dispositivos móveis, garantindo From 6e5edaab3c7cb70b77b74fef29d348830629fea2 Mon Sep 17 00:00:00 2001 From: devMarcosVM Date: Mon, 2 Sep 2024 10:10:46 -0300 Subject: [PATCH 30/31] =?UTF-8?q?docs:=20atualizando=20a=20documenta=C3=A7?= =?UTF-8?q?=C3=A3o=20do=20teste=20Filtro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Testes do Componente Filtro.md" | 46 ++++++------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git "a/docs/Como executar/Front-end/Testes unit\303\241rios/Testes do Componente Filtro.md" "b/docs/Como executar/Front-end/Testes unit\303\241rios/Testes do Componente Filtro.md" index 3130ce3..972c368 100644 --- "a/docs/Como executar/Front-end/Testes unit\303\241rios/Testes do Componente Filtro.md" +++ "b/docs/Como executar/Front-end/Testes unit\303\241rios/Testes do Componente Filtro.md" @@ -2,43 +2,25 @@ ## Descrição Geral -Este conjunto de testes foi desenvolvido para verificar o comportamento e a renderização correta do componente `Filtro` no projeto. A abordagem adotada utiliza a biblioteca `@testing-library/react` e `Jest` para renderizar o componente e validar os elementos visíveis na interface de usuário, garantindo que todos os elementos essenciais estejam presentes e corretamente configurados. +Este conjunto de testes foi desenvolvido para verificar o comportamento e a renderização correta do componente `Filtro` no projeto. A abordagem adotada utiliza a biblioteca `@testing-library/react` e `Jest` para renderizar o componente e validar os elementos visíveis na interface do usuário, garantindo que todos os elementos essenciais estejam presentes e configurados corretamente. Além disso, o mock de funções de API e de gráficos foi implementado para isolar o componente e testar seu comportamento de forma independente. -### Casos de Teste +## Casos de Teste -#### Renderização do Componente Filtro +### 1. Renderização e Interação com o Componente Filtro -1. **Objetivo**: Verificar se o componente `Filtro` é renderizado corretamente com todos os seus elementos principais. -2. **Teste**: - - Renderizar o componente `Filtro`. - - Verificar se o título com o texto "Pesquise por cidade, período e tema" está presente no documento. - - Verificar se o seletor de cidades está presente com a opção "Selecione uma cidade". +**Objetivo:** Verificar se o componente `Filtro` é renderizado corretamente, com todos os seus elementos principais, e interage corretamente com os selecionadores de data. -#### Manipulação da Seleção de Cidade +**Teste:** -1. **Objetivo**: Garantir que a seleção de uma cidade no campo de entrada é manipulada corretamente. -2. **Teste**: - - Renderizar o componente `Filtro`. - - Simular a seleção de uma cidade do seletor de cidades. - - Verificar se a cidade selecionada é refletida no campo de entrada. - -#### Manipulação de Seleção de Datas Inicial e Final - -1. **Objetivo**: Testar a funcionalidade de seleção de datas e garantir que as datas inicial e final são manipuladas corretamente. -2. **Teste**: - - Renderizar o componente `Filtro`. - - Simular a seleção de uma data inicial com o valor "01 / 2023". - - Simular a seleção de uma data final com o valor "12 / 2023". - - Verificar se os campos de entrada para "Data Inicial" e "Data final" contêm os valores selecionados. - -#### Verificação dos Gráficos - -1. **Objetivo**: Garantir que os gráficos são renderizados corretamente após a seleção de dados. -2. **Teste**: - - Renderizar o componente `Filtro`. - - Verificar se os gráficos para "Valor Empenhado", "Valor Liquidado" e "Valor Pago" estão presentes no documento. - - Verificar se o número de gráficos renderizados corresponde ao esperado (três gráficos). +- Renderizar o componente `Filtro`. +- Verificar se o título com o texto "Pesquise por período" está presente no documento. +- Verificar se os campos de data inicial e final estão presentes. +- Simular a seleção de uma data inicial com o valor "01/2023". +- Simular a seleção de uma data final com o valor "12/2023". +- Verificar se a função `fettchYearAndMonthTender` foi chamada após a seleção das datas. +- Verificar se os valores totais ("Total Empenhado", "Total Liquidado", "Total Pago") são exibidos no documento. +- Verificar se os gráficos são renderizados corretamente, garantindo que o mock de gráficos está presente e o número de gráficos renderizados corresponde ao esperado (dois gráficos). ## Considerações Finais -Esses testes garantem que o componente `Filtro` está sendo renderizado corretamente e que os elementos principais estão presentes com o conteúdo esperado. Além disso, verificam se as funcionalidades básicas, como a manipulação de entradas e a seleção de datas, estão funcionando conforme o esperado. Estes testes são essenciais para assegurar que o componente se comporta conforme projetado em diversos cenários de uso. +Os testes garantem que o componente `Filtro` é renderizado corretamente, com os elementos essenciais presentes, e que as funcionalidades básicas, como a manipulação das seleções de data e a exibição de gráficos, estão funcionando conforme o esperado. A utilização de mocks para funções de API e gráficos assegura que o teste seja executado de maneira isolada, focando exclusivamente no comportamento do componente. From 2f64579d14c8936ae1929c6abf16e18b5b3fa278 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Sep 2024 11:10:04 -0300 Subject: [PATCH 31/31] bugfix: Acessando API do backend para gerar graficos --- front/src/services/api.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/front/src/services/api.ts b/front/src/services/api.ts index acc7a04..3daacfa 100644 --- a/front/src/services/api.ts +++ b/front/src/services/api.ts @@ -41,20 +41,7 @@ export const fetchYearlyTendersData = async () => { } }; -export const fettchYearAndMonthTender = async (params: SearchParams) => { - const { startYear, startMonth, endYear, endMonth } = params; - - const url = `http://localhost:5000/tenders?start=${startYear}${startMonth}&end=${endYear}${endMonth}`; - - try { - const response = await axios.get(url); - return response.data; - } catch (error) { - console.error('Error searching tenders:', error); - throw error; - } -}; -/* pra quando a API remota estiver com a lógica certa +/* pra quando a API remota estiver com a lógica certa*/ export const fettchYearAndMonthTender = async (params: SearchParams) => { const { startYear, startMonth, endYear, endMonth } = params; @@ -67,4 +54,4 @@ export const fettchYearAndMonthTender = async (params: SearchParams) => { console.error('Error searching tenders:', error); throw error; } -};*/ +};