From 320ef603cd9a4e699079ae6d240767b35278b147 Mon Sep 17 00:00:00 2001 From: Mihoub Date: Fri, 24 May 2024 12:20:28 +0200 Subject: [PATCH] feat(graphs): add some graphs in homepage --- src/api/send-mail/index.tsx | 4 + src/components/graphs/by-domains.tsx | 89 +++++++++++++ src/components/graphs/by-status.tsx | 91 ++++++++++++++ src/components/graphs/by-tags.tsx | 96 ++++++++++++++ src/components/graphs/by-types.tsx | 68 ++++++++++ .../graphs/contributions-by-name.tsx | 33 ++--- .../graphs/contributions-by-year.tsx | 32 ++--- src/components/graphs/response-by-admin.tsx | 119 ++++++++++++++++++ src/config/api.ts | 4 + src/pages/home/index.tsx | 35 +++++- 10 files changed, 538 insertions(+), 33 deletions(-) create mode 100644 src/components/graphs/by-domains.tsx create mode 100644 src/components/graphs/by-status.tsx create mode 100644 src/components/graphs/by-tags.tsx create mode 100644 src/components/graphs/by-types.tsx create mode 100644 src/components/graphs/response-by-admin.tsx diff --git a/src/api/send-mail/index.tsx b/src/api/send-mail/index.tsx index 2919904..b5be945 100644 --- a/src/api/send-mail/index.tsx +++ b/src/api/send-mail/index.tsx @@ -42,6 +42,7 @@ function EmailSender({ }; const responseBrevo = await fetch("/email/", { + // const responseBrevo = await fetch("https://api.brevo.com/v3/smtp/email", { method: "POST", headers: { "api-key": import.meta.env.VITE_BREVO_API_AUTHORIZATION, @@ -59,6 +60,9 @@ function EmailSender({ }; const responseScanR = await fetch(`/api/${basePath}/${contribution._id}`, { + // const responseScanR = await fetch( + // `https://scanr-api.dataesr.ovh/${basePath}/${contribution._id}`, + // { method: "PATCH", headers: postHeaders, body: JSON.stringify(dataForScanR), diff --git a/src/components/graphs/by-domains.tsx b/src/components/graphs/by-domains.tsx new file mode 100644 index 0000000..7ecfc5f --- /dev/null +++ b/src/components/graphs/by-domains.tsx @@ -0,0 +1,89 @@ +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; +import { ContributionData } from "../../types"; +import { contactUrl, contributionUrl } from "../../config/api"; +import { Button, Col } from "@dataesr/dsfr-plus"; +import { useState } from "react"; + +const ContributionsGraphByDomains = () => { + const [filter, setFilter] = useState("contributions"); + const url = filter === "object" ? contributionUrl : contactUrl; + const { data, isLoading, isError } = useGetContributionData(url, 0); + const contributions = (data as { data: [] })?.data; + if (isLoading) { + return
Chargement...
; + } + + if (isError) { + return
Une erreur s'est produite
; + } + + if (!Array.isArray(contributions)) { + return
Les données ne sont pas disponibles
; + } + + const contributionsByEmailDomain = contributions.reduce( + (acc: Record, contribution: ContributionData) => { + const { email } = contribution; + if (email) { + const domain = email.split("@")[1]; + if (!acc[domain]) { + acc[domain] = 1; + } else { + acc[domain] += 1; + } + } + + return acc; + }, + {} + ); + + const chartData = Object.entries(contributionsByEmailDomain).map( + ([name, y]) => ({ + name, + y, + }) + ); + + const options = { + chart: { + type: "pie", + }, + title: { + text: "Contributions par domaine de courrier électronique", + }, + series: [ + { + name: "Domaine de courrier électronique", + data: chartData, + }, + ], + }; + + return ( + <> + + + + + + + ); +}; + +export default ContributionsGraphByDomains; diff --git a/src/components/graphs/by-status.tsx b/src/components/graphs/by-status.tsx new file mode 100644 index 0000000..6e821d9 --- /dev/null +++ b/src/components/graphs/by-status.tsx @@ -0,0 +1,91 @@ +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; +import { ContributionData } from "../../types"; +import { contactUrl, contributionUrl } from "../../config/api"; +import { Button, Col } from "@dataesr/dsfr-plus"; +import { useState } from "react"; + +const ContributionsGraphByStatus = () => { + const [filter, setFilter] = useState("contributions"); + const url = filter === "object" ? contributionUrl : contactUrl; + const { data, isLoading, isError } = useGetContributionData(url, 0); + const contributions = (data as { data: [] })?.data; + if (isLoading) { + return
Chargement...
; + } + + if (isError) { + return
Une erreur s'est produite
; + } + + if (!Array.isArray(contributions)) { + return
Les données ne sont pas disponibles
; + } + + const contributionsByStatus = contributions.reduce( + (acc: Record, contribution: ContributionData) => { + const { status } = contribution; + if (!acc[status]) { + acc[status] = 1; + } else { + acc[status] += 1; + } + + return acc; + }, + {} + ); + const chartData = Object.entries(contributionsByStatus).map(([name, y]) => ({ + name, + y, + })); + + const options = { + chart: { + type: "column", + }, + title: { + text: "Contributions par statut", + }, + xAxis: { + type: "category", + }, + yAxis: { + title: { + text: "Nombre de contributions", + }, + }, + series: [ + { + name: "Statut", + data: chartData, + }, + ], + }; + + return ( + <> + + + + + + + ); +}; + +export default ContributionsGraphByStatus; diff --git a/src/components/graphs/by-tags.tsx b/src/components/graphs/by-tags.tsx new file mode 100644 index 0000000..126a35e --- /dev/null +++ b/src/components/graphs/by-tags.tsx @@ -0,0 +1,96 @@ +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; +import { ContributionData } from "../../types"; +import { contactUrl, contributionUrl } from "../../config/api"; +import { Button, Col } from "@dataesr/dsfr-plus"; +import { useState } from "react"; + +const ContributionsGraphByTags = () => { + const [filter, setFilter] = useState("contributions"); + const url = filter === "object" ? contributionUrl : contactUrl; + const { data, isLoading, isError } = useGetContributionData(url, 0); + const contributions = (data as { data: [] })?.data; + if (isLoading) { + return
Chargement...
; + } + + if (isError) { + return
Une erreur s'est produite
; + } + + if (!Array.isArray(contributions)) { + return
Les données ne sont pas disponibles
; + } + + const contributionsByTag = contributions.reduce( + (acc: Record, contribution: ContributionData) => { + const { tags } = contribution; + if (tags) { + tags.forEach((tag: string) => { + if (!acc[tag]) { + acc[tag] = 1; + } else { + acc[tag] += 1; + } + }); + } + + return acc; + }, + {} + ); + + const chartData = Object.entries(contributionsByTag).map(([name, y]) => ({ + name, + y, + })); + + const options = { + chart: { + type: "pie", + }, + title: { + text: "Contributions par tag", + }, + xAxis: { + type: "category", + }, + yAxis: { + title: { + text: "Nombre de contributions", + }, + }, + series: [ + { + name: "Tag", + data: chartData, + }, + ], + }; + + return ( + <> + + + + + + + ); +}; + +export default ContributionsGraphByTags; diff --git a/src/components/graphs/by-types.tsx b/src/components/graphs/by-types.tsx new file mode 100644 index 0000000..b07355b --- /dev/null +++ b/src/components/graphs/by-types.tsx @@ -0,0 +1,68 @@ +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; +import { ContributionData } from "../../types"; +import { contributionUrl } from "../../config/api"; + +const ContributionsGraphByTypes = () => { + const { data, isLoading, isError } = useGetContributionData( + contributionUrl, + 0 + ); + const contributions = (data as { data: [] })?.data; + if (isLoading) { + return
Chargement...
; + } + + if (isError) { + return
Une erreur s'est produite
; + } + + if (!Array.isArray(contributions)) { + return
Les données ne sont pas disponibles
; + } + + const contributionsByType = contributions.reduce( + (acc: Record, contribution: ContributionData) => { + const { type } = contribution; + if (type) { + if (!acc[type]) { + acc[type] = 1; + } else { + acc[type] += 1; + } + } + + return acc; + }, + {} + ); + + const chartData = Object.entries(contributionsByType).map(([name, y]) => ({ + name, + y, + })); + + const options = { + chart: { + type: "pie", + }, + title: { + text: "Contributions par type d'objet", + }, + series: [ + { + name: "Type", + data: chartData, + }, + ], + }; + + return ( + <> + + + ); +}; + +export default ContributionsGraphByTypes; diff --git a/src/components/graphs/contributions-by-name.tsx b/src/components/graphs/contributions-by-name.tsx index bb1e467..c718e0a 100644 --- a/src/components/graphs/contributions-by-name.tsx +++ b/src/components/graphs/contributions-by-name.tsx @@ -3,7 +3,7 @@ import HighchartsReact from "highcharts-react-official"; import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; import { Contribution } from "../../types"; import { contactUrl, contributionUrl } from "../../config/api"; -import { Button } from "@dataesr/dsfr-plus"; +import { Button, Col } from "@dataesr/dsfr-plus"; import { useState } from "react"; const ContributionsGraphByName = () => { @@ -11,7 +11,6 @@ const ContributionsGraphByName = () => { const url = filter === "object" ? contributionUrl : contactUrl; const { data, isLoading, isError } = useGetContributionData(url, 0); const contributions = (data as { data: [] })?.data; - console.log(filter); if (isLoading) { return
Chargement...
; } @@ -83,19 +82,23 @@ const ContributionsGraphByName = () => { return ( <> - - + + + + ); diff --git a/src/components/graphs/contributions-by-year.tsx b/src/components/graphs/contributions-by-year.tsx index e8bc714..48441f1 100644 --- a/src/components/graphs/contributions-by-year.tsx +++ b/src/components/graphs/contributions-by-year.tsx @@ -3,7 +3,7 @@ import HighchartsReact from "highcharts-react-official"; import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; import { contributionUrl, contactUrl } from "../../config/api"; import { useState } from "react"; -import { Button } from "@dataesr/dsfr-plus"; +import { Button, Col } from "@dataesr/dsfr-plus"; const ContributionsGraphByYear = () => { const [filter, setFilter] = useState("contributions"); @@ -74,19 +74,23 @@ const ContributionsGraphByYear = () => { return ( <> - - + + + + ); diff --git a/src/components/graphs/response-by-admin.tsx b/src/components/graphs/response-by-admin.tsx new file mode 100644 index 0000000..a502ff5 --- /dev/null +++ b/src/components/graphs/response-by-admin.tsx @@ -0,0 +1,119 @@ +import HighchartsReact from "highcharts-react-official"; +import Highcharts from "highcharts"; + +import { useState } from "react"; +import { ContributionData } from "../../types"; +import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; +import { contactUrl, contributionUrl } from "../../config/api"; +import { Button, Col } from "@dataesr/dsfr-plus"; + +const AdminResponseGraph = () => { + const [filter, setFilter] = useState("contributions"); + const url = filter === "object" ? contributionUrl : contactUrl; + const { data, isLoading, isError } = useGetContributionData(url, 0); + + const contributions = (data as { data: ContributionData[] })?.data; + + if (isLoading) { + return
Chargement...
; + } + + if (isError) { + return
Une erreur s'est produite
; + } + + if (!Array.isArray(contributions)) { + return
Les données ne sont pas disponibles
; + } + + const responsesByAdmin = contributions.reduce( + (acc: Record, contribution: ContributionData) => { + if (contribution.team) { + contribution.team.forEach((admin: string) => { + if (!acc[admin]) { + acc[admin] = 1; + } else { + acc[admin] += 1; + } + }); + } + + return acc; + }, + {} + ); + + const chartData = Object.entries(responsesByAdmin) + .map(([name, y]) => ({ name, y })) + .sort((a, b) => b.y - a.y); + + const options = { + chart: { + type: "column", + options3d: { + enabled: true, + alpha: 15, + beta: 15, + depth: 50, + viewDistance: 25, + }, + }, + title: { + text: "Réponses par admin", + }, + plotOptions: { + column: { + depth: 25, + dataLabels: { + enabled: true, + format: function () { + if (this.point.index === 0) { + return ''; + } + return ""; + }, + useHTML: true, + }, + }, + }, + xAxis: { + type: "category", + }, + yAxis: { + title: { + text: "Nombre de réponses", + }, + }, + series: [ + { + name: "Traitements", + data: chartData, + }, + ], + }; + + return ( + <> + + + + + + + ); +}; + +export default AdminResponseGraph; diff --git a/src/config/api.ts b/src/config/api.ts index 456fb09..22b6937 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -3,4 +3,8 @@ export const headers = API_KEY ? { Authorization: `Basic ${API_KEY}` } : {}; export const postHeaders = { ...headers, "Content-Type": "application/json" }; export const contributionUrl = "/api/contribute?max_results=2000"; +// export const contributionUrl = +// "https://scanr-api.dataesr.ovh/contribute?max_results=2000"; export const contactUrl = "/api/contact?max_results=2000"; +// export const contactUrl = +// "https://scanr-api.dataesr.ovh/contact?max_results=2000"; diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index f3dc1ed..f1c42a1 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,21 +1,48 @@ import { Col, Container, Row } from "@dataesr/dsfr-plus"; import ContributionsGraphByYear from "../../components/graphs/contributions-by-year"; import ContributionsGraphByName from "../../components/graphs/contributions-by-name"; +import AdminResponseGraph from "../../components/graphs/response-by-admin"; +import ContributionsGraphByStatus from "../../components/graphs/by-status"; +import ContributionsGraphByTags from "../../components/graphs/by-tags"; +import ContributionsGraphByDomains from "../../components/graphs/by-domains"; +import ContributionsGraphByTypes from "../../components/graphs/by-types"; const Home = () => { return (

Bienvenue sur le Guichet numérique du DISD

- + - - - + + + + + + + + + + + + + + + + + + + + + + {/* + + */} +
); };