diff --git a/package-lock.json b/package-lock.json index b0b70fb..d0b62bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@dataesr/dsfr-plus": "^0.3.2", "@getbrevo/brevo": "^2.0.0-beta.4", - "@tanstack/react-query": "^4.29.5", + "@tanstack/react-query": "^4.36.1", "@tanstack/react-query-devtools": "^4.29.6", "classnames": "^2.3.2", "highcharts": "^11.4.1", @@ -18,6 +18,7 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-query": "^3.39.3", "react-router-dom": "^6.11.1", "react-select": "^5.8.0", "react-toastify": "^10.0.5", @@ -3720,6 +3721,14 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3758,6 +3767,21 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/browserslist": { "version": "4.23.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", @@ -4046,6 +4070,11 @@ "node": ">=0.10.0" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4614,8 +4643,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -4660,7 +4688,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4692,7 +4719,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4702,7 +4728,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4895,7 +4920,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5035,6 +5059,11 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5209,6 +5238,15 @@ "yallist": "^3.0.2" } }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -5244,6 +5282,11 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -5313,6 +5356,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -5368,11 +5419,15 @@ "node": ">=0.10.0" } }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -5723,6 +5778,31 @@ "@types/react": ">=16.8" } }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -5972,7 +6052,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -6438,6 +6517,15 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -6602,8 +6690,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { "version": "3.1.1", diff --git a/package.json b/package.json index a8fc3f6..60dd687 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dependencies": { "@dataesr/dsfr-plus": "^0.3.2", "@getbrevo/brevo": "^2.0.0-beta.4", - "@tanstack/react-query": "^4.29.5", + "@tanstack/react-query": "^4.36.1", "@tanstack/react-query-devtools": "^4.29.6", "classnames": "^2.3.2", "highcharts": "^11.4.1", @@ -21,6 +21,7 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-query": "^3.39.3", "react-router-dom": "^6.11.1", "react-select": "^5.8.0", "react-toastify": "^10.0.5", @@ -41,4 +42,4 @@ "typescript": "^5.2.2", "vite": "^5.0.4" } -} \ No newline at end of file +} diff --git a/src/api/contribution-api/getData.tsx b/src/api/contribution-api/getData.tsx new file mode 100644 index 0000000..9760270 --- /dev/null +++ b/src/api/contribution-api/getData.tsx @@ -0,0 +1,32 @@ +import { useQuery } from "@tanstack/react-query"; +import { postHeaders } from "../../config/api"; +import { buildURL } from "../utils/buildURL"; + +const ContributionData = ({ + location, + sort, + status, + query, + page, + searchInMessage, +}) => { + const fetchContributions = async () => { + const url = buildURL(location, sort, status, query, page, searchInMessage); + const response = await fetch(url, { + headers: postHeaders, + }); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }; + + const { data, isLoading, isError, refetch } = useQuery( + [location, sort, status, query, page, searchInMessage], + fetchContributions + ); + + return { data, isLoading, isError, refetch }; +}; + +export default ContributionData; diff --git a/src/api/send-mail/index.tsx b/src/api/send-mail/index.tsx index a87d9e5..5f61276 100644 --- a/src/api/send-mail/index.tsx +++ b/src/api/send-mail/index.tsx @@ -6,10 +6,10 @@ import { toast } from "react-toastify"; function EmailSender({ contribution, - setResponseScanR, + refetch, }: { contribution: Contribution | Contribute_Production; - setResponseScanR: any; + refetch; }) { const [, setEmailSent] = useState(false); const [userResponse, setUserResponse] = useState(""); @@ -38,15 +38,22 @@ function EmailSender({ to: [ { email: "mihoub.debache@enseignementsup.gouv.fr", - name: "Mihoub mihoub", + name: "Mihoub Debache", }, ], subject: `Réponse à votre contribution`, - htmlContent: userResponse, + htmlContent: ` +

Réponse à votre contribution

+

Bonjour,

+

En réponse à votre contribution :

+
${contribution.message}
+

Voici notre réponse :

+

${userResponse}

+`, }; - - const responseBrevo = await fetch("/email/", { - // const responseBrevo = await fetch("https://api.brevo.com/v3/smtp/email", { + console.log(contribution); + // 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, @@ -64,22 +71,23 @@ function EmailSender({ responseFrom: selectedProfile, }; - 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), - }); + // 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), + } + ); if (!responseScanR.ok) { throw new Error(`HTTP error! status: ${responseScanR.status}`); } - setResponseScanR(dataForScanR); setEmailSent(true); - toast.success("Mail envoyé!"); + refetch(); setUserResponse(""); + toast.success("Mail envoyé!"); }; return ( diff --git a/src/api/utils/buildURL.tsx b/src/api/utils/buildURL.tsx index e92b48b..262e689 100644 --- a/src/api/utils/buildURL.tsx +++ b/src/api/utils/buildURL.tsx @@ -1,21 +1,18 @@ -import { useLocation } from "react-router-dom"; - export const buildURL = ( + location: any, sort: string, status: string, query: string, page: number, searchInMessages: boolean = false ): string => { - const location = useLocation(); let baseUrl = "contact"; - if (location.pathname.includes("contributionpage")) { + if (location?.pathname?.includes("contributionpage")) { baseUrl = "contribute"; - } else if (location.pathname.includes("apioperations")) { + } else if (location?.pathname?.includes("apioperations")) { baseUrl = "contribute_productions"; } - const sorted = sort === "ASC" ? "sort=created_at" : "sort=-created_at"; const where: any = {}; if (query.trim() !== "") { @@ -32,6 +29,6 @@ export const buildURL = ( const whereQuery = Object.keys(where).length > 0 ? `&where=${JSON.stringify(where)}` : ""; - return `/api/${baseUrl}?${sorted}&page=${page}&max_results=20${whereQuery}`; - // return `https://scanr-api.dataesr.ovh/${baseUrl}?${sorted}&page=${page}&max_results=20${whereQuery}`; + // return `/api/${baseUrl}?${sorted}&page=${page}&max_results=20${whereQuery}`; + return `https://scanr-api.dataesr.ovh/${baseUrl}?${sorted}&page=${page}&max_results=20${whereQuery}`; }; diff --git a/src/components/edit-modal/index.tsx b/src/components/edit-modal/index.tsx index b28eedf..d65e5a6 100644 --- a/src/components/edit-modal/index.tsx +++ b/src/components/edit-modal/index.tsx @@ -9,7 +9,7 @@ import { Button, Row, } from "@dataesr/dsfr-plus"; -import { Contribute_Production, Contribution } from "../../types"; +import { Contribute_Production, Contribution, Inputs } from "../../types"; import { postHeaders } from "../../config/api"; import Select from "react-select"; @@ -17,23 +17,28 @@ type EditModalProps = { isOpen: boolean; data: Contribution | Contribute_Production; onClose: () => void; + refetch; }; -const EditModal: React.FC = ({ isOpen, data, onClose }) => { +const EditModal: React.FC = ({ + isOpen, + data, + onClose, + refetch, +}) => { const user = sessionStorage.getItem("selectedProfile"); - const [inputs, setInputs] = useState({ + const [inputs, setInputs] = useState({ team: [user], - status: data.status, - tag: "", + status: "treated", + tag: [], idRef: "", comment: "", }); - useEffect(() => { setInputs({ team: [user], status: "treated", - tag: "", + tag: [], idRef: "", comment: "", }); @@ -48,7 +53,10 @@ const EditModal: React.FC = ({ isOpen, data, onClose }) => { const handleTagChange = (event) => { const newTag = event.target.value; - setInputs((prevInputs) => ({ ...prevInputs, tag: newTag })); + setInputs((prevInputs) => ({ + ...prevInputs, + tag: [...prevInputs.tag, newTag], + })); }; const handleCommentChange = (event) => { const newComment = event.target.value; @@ -70,8 +78,8 @@ const EditModal: React.FC = ({ isOpen, data, onClose }) => { const handleSubmit = async () => { try { const response = await fetch( - // `https://scanr-api.dataesr.ovh/${basePath}/${data._id}`, - `${window.location.origin}/api/${basePath}/${data._id}`, + `https://scanr-api.dataesr.ovh/${basePath}/${data._id}`, + // `${window.location.origin}/api/${basePath}/${data._id}`, { method: "PATCH", headers: postHeaders, @@ -88,6 +96,7 @@ const EditModal: React.FC = ({ isOpen, data, onClose }) => { } else { const responseData = await response.json(); console.log("Données de réponse", responseData); + refetch(); onClose(); } } catch (error) { @@ -155,7 +164,6 @@ const EditModal: React.FC = ({ isOpen, data, onClose }) => { ? "Mettre à jour le commentaire pour l'équipe" : "Ajouter un commentaire pour l'équipe" } - maxLength={6} hint="Ce commentaire ne sera lu que par les membres de l'équipe" value={inputs.comment} onChange={handleCommentChange} @@ -164,7 +172,7 @@ const EditModal: React.FC = ({ isOpen, data, onClose }) => { diff --git a/src/components/graphs/by-domains.tsx b/src/components/graphs/by-domains.tsx index 7ecfc5f..1b5fe2d 100644 --- a/src/components/graphs/by-domains.tsx +++ b/src/components/graphs/by-domains.tsx @@ -1,10 +1,10 @@ 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"; +import { ContributionDataType } from "../../types"; const ContributionsGraphByDomains = () => { const [filter, setFilter] = useState("contributions"); @@ -24,7 +24,7 @@ const ContributionsGraphByDomains = () => { } const contributionsByEmailDomain = contributions.reduce( - (acc: Record, contribution: ContributionData) => { + (acc: Record, contribution: ContributionDataType) => { const { email } = contribution; if (email) { const domain = email.split("@")[1]; diff --git a/src/components/graphs/by-status.tsx b/src/components/graphs/by-status.tsx index 6e821d9..ae6b2dd 100644 --- a/src/components/graphs/by-status.tsx +++ b/src/components/graphs/by-status.tsx @@ -1,10 +1,10 @@ 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"; +import { ContributionDataType } from "../../types"; const ContributionsGraphByStatus = () => { const [filter, setFilter] = useState("contributions"); @@ -24,7 +24,7 @@ const ContributionsGraphByStatus = () => { } const contributionsByStatus = contributions.reduce( - (acc: Record, contribution: ContributionData) => { + (acc: Record, contribution: ContributionDataType) => { const { status } = contribution; if (!acc[status]) { acc[status] = 1; diff --git a/src/components/graphs/by-tags.tsx b/src/components/graphs/by-tags.tsx index 126a35e..b905f5b 100644 --- a/src/components/graphs/by-tags.tsx +++ b/src/components/graphs/by-tags.tsx @@ -1,10 +1,10 @@ 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"; +import { ContributionDataType } from "../../types"; const ContributionsGraphByTags = () => { const [filter, setFilter] = useState("contributions"); @@ -24,7 +24,7 @@ const ContributionsGraphByTags = () => { } const contributionsByTag = contributions.reduce( - (acc: Record, contribution: ContributionData) => { + (acc: Record, contribution: ContributionDataType) => { const { tags } = contribution; if (tags) { tags.forEach((tag: string) => { diff --git a/src/components/graphs/by-types.tsx b/src/components/graphs/by-types.tsx index b07355b..785968a 100644 --- a/src/components/graphs/by-types.tsx +++ b/src/components/graphs/by-types.tsx @@ -1,8 +1,8 @@ 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"; +import { ContributionDataType } from "../../types"; const ContributionsGraphByTypes = () => { const { data, isLoading, isError } = useGetContributionData( @@ -23,7 +23,7 @@ const ContributionsGraphByTypes = () => { } const contributionsByType = contributions.reduce( - (acc: Record, contribution: ContributionData) => { + (acc: Record, contribution: ContributionDataType) => { const { type } = contribution; if (type) { if (!acc[type]) { diff --git a/src/components/graphs/contribution-by-type.tsx b/src/components/graphs/contribution-by-type.tsx new file mode 100644 index 0000000..1ec7448 --- /dev/null +++ b/src/components/graphs/contribution-by-type.tsx @@ -0,0 +1,80 @@ +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import { contributionUrl } from "../../config/api"; +import { Contribution } from "../../types"; +import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; + +const ContributionsGraphByYearAndType = () => { + const url = contributionUrl; + const { data, isLoading, isError } = useGetContributionData(url, 0); + + const contributions = (data as { data: Contribution[] })?.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 contributionsByYearAndType = contributions.reduce( + (acc, contribution) => { + const date = new Date(contribution.created_at); + const year = date.getFullYear(); + const type = contribution.type; + + if (!acc[year]) { + acc[year] = {}; + } + + if (!acc[year][type]) { + acc[year][type] = 0; + } + + acc[year][type]++; + + return acc; + }, + {} + ); + + const options = { + chart: { + type: "column", + }, + title: { + text: `Nombre de contributions par année et par type`, + }, + xAxis: { + categories: Object.keys(contributionsByYearAndType), + }, + series: Object.entries(contributionsByYearAndType).reduce( + (acc, [, contributions]) => { + Object.entries(contributions).forEach(([type, count]) => { + const series = acc.find((series) => series.name === type); + + if (series) { + series.data.push(count); + } else { + acc.push({ + name: type, + data: [count], + }); + } + }); + + return acc; + }, + [] + ), + }; + + return ; +}; + +export default ContributionsGraphByYearAndType; diff --git a/src/components/graphs/response-by-admin.tsx b/src/components/graphs/response-by-admin.tsx index 23a3f9d..1dafc28 100644 --- a/src/components/graphs/response-by-admin.tsx +++ b/src/components/graphs/response-by-admin.tsx @@ -2,17 +2,17 @@ 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"; +import { ContributionDataType } from "../../types"; 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; + const contributions = (data as { data: ContributionDataType[] })?.data; if (isLoading) { return
Chargement...
; @@ -27,7 +27,7 @@ const AdminResponseGraph = () => { } const occurrencesByUser = contributions.reduce( - (acc: Record, contribution: ContributionData) => { + (acc: Record, contribution: ContributionDataType) => { const { responseFrom } = contribution; if (responseFrom) { if (!acc[responseFrom]) { diff --git a/src/components/graphs/treatment-by-admin.tsx b/src/components/graphs/treatment-by-admin.tsx index f400b29..0da1dd4 100644 --- a/src/components/graphs/treatment-by-admin.tsx +++ b/src/components/graphs/treatment-by-admin.tsx @@ -2,17 +2,17 @@ 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"; +import { ContributionDataType } from "../../types"; const AdminTreatmentGraph = () => { 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; + const contributions = (data as { data: ContributionDataType[] })?.data; if (isLoading) { return
Chargement...
; @@ -27,7 +27,7 @@ const AdminTreatmentGraph = () => { } const responsesByAdmin = contributions.reduce( - (acc: Record, contribution: ContributionData) => { + (acc: Record, contribution: ContributionDataType) => { if (contribution.team) { contribution.team.forEach((admin: string) => { if (!acc[admin]) { diff --git a/src/main.tsx b/src/main.tsx index 51c888b..ea5a9e7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,16 +1,23 @@ import React, { ReactNode } from "react"; -import ReactDOM from "react-dom/client"; -import { BrowserRouter, Link } from "react-router-dom"; -import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import ReactDOM from "react-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import "react-toastify/dist/ReactToastify.css"; import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.min.css"; +import { BrowserRouter, Link } from "react-router-dom"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import Router from "./router"; -import "./styles.scss"; import { DSFRConfig } from "@dataesr/dsfr-plus"; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + refetchOnMount: false, + staleTime: Infinity, + }, + }, +}); type RouterLinkProps = { href: string; @@ -24,19 +31,23 @@ const RouterLink = ({ href, replace, target, ...props }: RouterLinkProps) => { return ; }; -const rootElement = document.getElementById("root"); -if (rootElement) { - ReactDOM.createRoot(rootElement).render( - +ReactDOM.render( + + - - - - - - - + + + + + - - ); -} + + , + document.getElementById("root") +); diff --git a/src/pages/api-operation-page/contribution-production-card.tsx b/src/pages/api-operation-page/contribution-production-card.tsx index bf4e7aa..1072c9b 100644 --- a/src/pages/api-operation-page/contribution-production-card.tsx +++ b/src/pages/api-operation-page/contribution-production-card.tsx @@ -14,8 +14,10 @@ import StaffProductionActions from "./staff-production-action"; const ContributionProductionItem = ({ data, + refetch, }: { data: Contribute_Production; + refetch; }) => { const renderAccordion = () => ( @@ -37,7 +39,6 @@ const ContributionProductionItem = ({ {data.tag} )} - - - + + ); diff --git a/src/pages/api-operation-page/contributor-production-info.tsx b/src/pages/api-operation-page/contributor-production-info.tsx index 6497f24..1e74b33 100644 --- a/src/pages/api-operation-page/contributor-production-info.tsx +++ b/src/pages/api-operation-page/contributor-production-info.tsx @@ -3,10 +3,12 @@ import MessagePreview from "./message-preview"; const ContributorProductionInfo = ({ data, + refetch, }: { data: Contribute_Production; + refetch; }) => { - return ; + return ; }; export default ContributorProductionInfo; diff --git a/src/pages/api-operation-page/index.tsx b/src/pages/api-operation-page/index.tsx index ad52341..b7d74ad 100644 --- a/src/pages/api-operation-page/index.tsx +++ b/src/pages/api-operation-page/index.tsx @@ -7,14 +7,13 @@ import { Text, Title, } from "@dataesr/dsfr-plus"; -import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; import { Contribute_Production, ContributionPageProps } from "../../types"; import { useLocation } from "react-router-dom"; -import { buildURL } from "../../api/utils/buildURL"; import BottomPaginationButtons from "../../components/pagination/bottom-buttons"; import Selectors from "../../components/selectors"; import TopPaginationButtons from "../../components/pagination/top-buttons"; import ContributionProductionItem from "./contribution-production-card"; +import ContributionData from "../../api/contribution-api/getData"; const ContributionPage: React.FC = () => { const [reload] = useState(0); @@ -22,7 +21,7 @@ const ContributionPage: React.FC = () => { const [status, setStatus] = useState("new"); const [query, setQuery] = useState(""); const [page, setPage] = useState(1); - const [data, setData] = useState(null); + const [, setData] = useState(null); const location = useLocation(); @@ -51,19 +50,27 @@ const ContributionPage: React.FC = () => { }, [location.pathname]); const { + data: fetchedData, isLoading, isError, - data: fetchedData, - } = useGetContributionData(buildURL(sort, status, query, page), reload); + refetch, + } = ContributionData({ + location: location, + sort, + status, + query, + page, + searchInMessage: reload, + }); useEffect(() => { setData(fetchedData); }, [fetchedData]); - const meta = (data as { meta: any })?.meta; + const meta = (fetchedData as { meta: any })?.meta; const maxPage = meta ? Math.ceil(meta?.total / 10) : 1; const contrib: Contribute_Production[] = ( - data as { data: Contribute_Production[] } + fetchedData as { data: Contribute_Production[] } )?.data; const handleSearch = (value: string) => { @@ -123,6 +130,7 @@ const ContributionPage: React.FC = () => { {filteredContributions?.map((contribution) => ( ))} { +const MessagePreview = ({ + data, + refetch, +}: { + data: Contribute_Production; + refetch; +}) => { const [showModal, setShowModal] = useState(false); const [copySuccess, setCopySuccess] = useState(""); @@ -28,19 +34,26 @@ const MessagePreview = ({ data }: { data: Contribute_Production }) => { {data.comment && ( - Commentaire de l'équipe ({data.team[0]}) + + Commentaire ({data?.team ? data.team[0] : ""}) + {" "} {data.comment} )} - + {data.id && ( copyToClipboard(data.id, "ID copié")} + className={"fr-icon-user-line"} > ID de l'objet concerné: {data.id} {copySuccess === "ID copié" && ( diff --git a/src/pages/api-operation-page/staff-production-action.tsx b/src/pages/api-operation-page/staff-production-action.tsx index 35fc010..dce6db1 100644 --- a/src/pages/api-operation-page/staff-production-action.tsx +++ b/src/pages/api-operation-page/staff-production-action.tsx @@ -1,28 +1,29 @@ -import { useState } from "react"; import { Col, Text } from "@dataesr/dsfr-plus"; import "./styles.scss"; import EmailSender from "../../api/send-mail"; import { Contribute_Production } from "../../types"; -const StaffProductionActions = ({ data }: { data: Contribute_Production }) => { - const [responseScanR, setResponseScanR] = useState(null); - +const StaffProductionActions = ({ + data, + refetch, +}: { + data: Contribute_Production; + refetch; +}) => { return ( <> {data?.mailSent && ( - {(responseScanR?.responseFrom || data.responseFrom) !== "" - ? "Réponse apportée par " - : ""} - {responseScanR?.responseFrom || data.responseFrom} + {data.responseFrom !== "" ? "Réponse apportée par " : ""} + {data.responseFrom} {" le "} {new Date(data?.mailSentDate).toLocaleDateString()}{" "} - {responseScanR?.mailSent || data.mailSent} + {data.mailSent} )} - + ); }; diff --git a/src/pages/contribution-page/contribution-card.tsx b/src/pages/contribution-page/contribution-card.tsx index d6a3f7b..8b5fd67 100644 --- a/src/pages/contribution-page/contribution-card.tsx +++ b/src/pages/contribution-page/contribution-card.tsx @@ -18,9 +18,11 @@ import { BadgeColor } from "./utils"; const ContributionItem = ({ data, highlightedQuery, + refetch, }: { data: Contribution; highlightedQuery: string; + refetch: any; }) => { const renderAccordion = () => ( @@ -42,6 +44,24 @@ const ContributionItem = ({ > {data.status} + {data.responseFrom && ( + + {`Réponse envoyé par ${data.responseFrom}`} + + )} + {data.comment && data?.team && data.team.length > 0 && ( + + {`commenté par ${data.team[0]}`} + + )} {data.type && ( )} - - + + ); diff --git a/src/pages/contribution-page/contributor-info.tsx b/src/pages/contribution-page/contributor-info.tsx index 9569b8e..aadba72 100644 --- a/src/pages/contribution-page/contributor-info.tsx +++ b/src/pages/contribution-page/contributor-info.tsx @@ -4,11 +4,19 @@ import MessagePreview from "./message-preview"; const ContributorInfo = ({ data, highlightedQuery, + refetch, }: { data: Contribution; highlightedQuery: string; + refetch; }) => { - return ; + return ( + + ); }; export default ContributorInfo; diff --git a/src/pages/contribution-page/index.tsx b/src/pages/contribution-page/index.tsx index 17f0570..9fbefff 100644 --- a/src/pages/contribution-page/index.tsx +++ b/src/pages/contribution-page/index.tsx @@ -1,4 +1,11 @@ import { useState, useEffect } from "react"; + +import { Contribution, ContributionPageProps } from "../../types"; +import { useLocation } from "react-router-dom"; +import BottomPaginationButtons from "../../components/pagination/bottom-buttons"; +import Selectors from "../../components/selectors"; + +import TopPaginationButtons from "../../components/pagination/top-buttons"; import { Col, Container, @@ -8,13 +15,7 @@ import { Title, } from "@dataesr/dsfr-plus"; import ContributionItem from "./contribution-card"; -import useGetContributionData from "../../api/contribution-api/useGetObjectContributeData"; -import { Contribution, ContributionPageProps } from "../../types"; -import { useLocation } from "react-router-dom"; -import { buildURL } from "../../api/utils/buildURL"; -import BottomPaginationButtons from "../../components/pagination/bottom-buttons"; -import Selectors from "../../components/selectors"; -import TopPaginationButtons from "../../components/pagination/top-buttons"; +import ContributionData from "../../api/contribution-api/getData"; const ContributionPage: React.FC = () => { const [reload] = useState(0); @@ -56,15 +57,18 @@ const ContributionPage: React.FC = () => { setSearchInMessage(false); } }, [location.pathname]); + const { data, isLoading, isError, refetch } = ContributionData({ + location: location, + sort, + status, + query, + page, + searchInMessage, + }); - const { data, isLoading, isError } = useGetContributionData( - buildURL(sort, status, query, page, searchInMessage), - reload - ); - - const meta = (data as { meta: any }).meta; + const meta = (data as { meta: any })?.meta; const maxPage = meta ? Math.ceil(meta?.total / 10) : 1; - const contrib: Contribution[] = (data as { data: Contribution[] }).data; + const contrib: Contribution[] = (data as { data: Contribution[] })?.data; const handleSearch = (value: string) => { setQuery(value.trim()); @@ -100,7 +104,7 @@ const ContributionPage: React.FC = () => { return ( - {location.pathname.includes("contributionPage") ? ( + {location.pathname.includes("contributionpage") ? ( Contribution par objets ) : ( Contribution via formulaire @@ -132,6 +136,7 @@ const ContributionPage: React.FC = () => { {filteredContributions?.map((contribution) => ( ))} diff --git a/src/pages/contribution-page/message-preview.tsx b/src/pages/contribution-page/message-preview.tsx index 618b155..4f60291 100644 --- a/src/pages/contribution-page/message-preview.tsx +++ b/src/pages/contribution-page/message-preview.tsx @@ -1,4 +1,4 @@ -import { Button, Col, Row, Text } from "@dataesr/dsfr-plus"; +import { Button, Col, Link, Row, Text } from "@dataesr/dsfr-plus"; import type { Contribution } from "../../types"; import HighlightedMessage from "../../components/highlighted-message"; import { useLocation } from "react-router-dom"; @@ -8,9 +8,11 @@ import { useState, useCallback } from "react"; const MessagePreview = ({ data, highlightedQuery, + refetch, }: { data: Contribution; highlightedQuery: string; + refetch; }) => { const location = useLocation(); const [showModal, setShowModal] = useState(false); @@ -28,6 +30,7 @@ const MessagePreview = ({ ) ? "contributorSideInfo" : "contributorSideContactInfo"; + const handleOpenModal = () => { setShowModal(true); }; @@ -43,29 +46,37 @@ const MessagePreview = ({ return ( <> - - {data.comment && ( + + + + + {data?.comment && ( - - Commentaire de l'équipe ({data.team[0]}) - {data.comment} + + + Commentaire ({data?.team ? data.team[0] : ""}){" "} + + {data?.comment} )} {data.id && ( - + copyToClipboard(data.id, "ID copié")} + className={ + data.type === "structure" + ? "fr-icon-building-line" + : "fr-icon-user-line" + } > ID de l'objet concerné: {data.id} {copySuccess === "ID copié" && ( @@ -89,6 +100,69 @@ const MessagePreview = ({ {new Date(data.modified_at).toLocaleDateString()} )} + {data.type === "structures" && ( + <> + + Sur scanR + +
+ + Sur dataESR + + + )} + {data.type === "publications" && ( + <> + + Sur scanR + +
+ + Sur dataESR + + + )} + {data.type === "persons" && ( +
+ + Sur scanR + +
+ + Sur dataEsr + +
+ )} )} @@ -128,7 +202,7 @@ const MessagePreview = ({ {data.fonction ? ( Fonction: {data.fonction} ) : ( - Fonction non renseignée + Fonction non renseignée )}
@@ -140,9 +214,6 @@ const MessagePreview = ({ />
- - - ); }; diff --git a/src/pages/contribution-page/staff-action.tsx b/src/pages/contribution-page/staff-action.tsx index ce45179..8f7955e 100644 --- a/src/pages/contribution-page/staff-action.tsx +++ b/src/pages/contribution-page/staff-action.tsx @@ -2,11 +2,9 @@ import { Col, Text } from "@dataesr/dsfr-plus"; import EmailSender from "../../api/send-mail"; import type { Contribution } from "../../types"; import { useLocation } from "react-router-dom"; -import { useState } from "react"; -const StaffActions = ({ data }: { data: Contribution }) => { +const StaffActions = ({ data, refetch }: { data: Contribution; refetch }) => { const location = useLocation(); - const [responseScanR, setResponseScanR] = useState(null); const contributorClassName = location.pathname.includes("contributionpage") ? "staffSide" : "staffSideContact"; @@ -15,16 +13,13 @@ const StaffActions = ({ data }: { data: Contribution }) => { {data?.mailSent && ( - Réponse apportée par{" "} - {responseScanR?.responseFrom || data.responseFrom} le{" "} - {new Date( - responseScanR?.mailSentData || data?.mailSentDate - ).toLocaleDateString()}{" "} + Réponse apportée par {data.responseFrom} le{" "} + {new Date(data?.mailSentDate).toLocaleDateString()}{" "} - {responseScanR?.mailSent || data.mailSent} + {data.mailSent} )} - + ); }; diff --git a/src/pages/contribution-page/styles.scss b/src/pages/contribution-page/styles.scss index 77e4a0a..f935ab2 100644 --- a/src/pages/contribution-page/styles.scss +++ b/src/pages/contribution-page/styles.scss @@ -2,31 +2,31 @@ background-color: var(--background-alt-green-emeraude); border: 1px solid var(--background-alt-green-emeraude); border-radius: 10px; - margin-bottom: 20px; - padding: 20px; + margin-bottom: 5px; + padding: 5px; } .contributorSideMessage { background-color: var(--background-alt-green-emeraude); border: 1px solid var(--background-alt-green-emeraude); border-radius: 10px; - margin-bottom: 20px; + margin-bottom: 5px; margin-right: 250px; - padding: 20px; + padding: 5px; } .contributorSideContactInfo { background-color: var(--background-alt-brown-caramel); border: 1px solid var(--background-alt-brown-caramel); border-radius: 10px; - margin-bottom: 20px; - padding: 20px; + margin-bottom: 5px; + padding: 5px; } .contributorSideContactMessage { background-color: var(--background-alt-brown-caramel); border: 1px solid var(--background-alt-brown-caramel); border-radius: 10px; - margin-bottom: 20px; + margin-bottom: 5px; margin-right: 250px; - padding: 20px; + padding: 50px; } .staffSide { text-align: right; diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 5482f36..7ffa5b5 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -9,6 +9,7 @@ import AdminTreatmentGraph from "../../components/graphs/treatment-by-admin"; import AdminResponseGraph from "../../components/graphs/response-by-admin"; import ContributionsGraphByProductions from "../../components/graphs/by-missing-productions"; import CommentsGraphByTeamMember from "../../components/graphs/comment-by-team"; +import ContributionsGraphByYearAndType from "../../components/graphs/contribution-by-type"; const Home = () => { return ( @@ -54,6 +55,11 @@ const Home = () => { + + + + +
); }; diff --git a/src/types/index.ts b/src/types/index.ts index 4713c1f..8ada623 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -7,7 +7,7 @@ export type Contribution = { team: any; _id: any; status: string; - tags: any; + tags: Array; message: string; created_at: string | number | Date; fonction: any; @@ -17,14 +17,14 @@ export type Contribution = { type: string; id: string; comment: string; - data: ContributionData[]; + data: ContributionDataType[]; meta: { total: number; }; highlightedQuery: string; }; -export type ContributionData = { +export type ContributionDataType = { responseFrom: string; idref: any; tags: any; @@ -86,3 +86,11 @@ export type Production = { id: string; treated: boolean; }; + +export type Inputs = { + team: string[]; + status: string; + tag: string[]; + idRef: string; + comment: string; +};