diff --git a/src/components/edit-modal/index.tsx b/src/components/edit-modal/index.tsx index 8b61ff2..8ff5962 100644 --- a/src/components/edit-modal/index.tsx +++ b/src/components/edit-modal/index.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from "react"; + import { Modal, ModalTitle, @@ -12,6 +13,7 @@ import { import { Contribute_Production, Contribution, Inputs } from "../../types"; import { postHeaders } from "../../config/api"; import Select from "react-select"; +import { toast } from "react-toastify"; type EditModalProps = { isOpen: boolean; @@ -20,6 +22,8 @@ type EditModalProps = { refetch; }; +// const queryClient = useQueryClient(); + const EditModal: React.FC = ({ isOpen, data, @@ -41,7 +45,7 @@ const EditModal: React.FC = ({ const [inputs, setInputs] = useState({ team: [user], status: "treated", - tag: [], + tags: [], idRef: "", comment: "", }); @@ -49,25 +53,29 @@ const EditModal: React.FC = ({ setInputs({ team: [user], status: "treated", - tag: [], + tags: [], idRef: "", comment: "", }); }, [data, user]); - const handleStatusChange = (selectedOption) => { setInputs((prevInputs) => ({ ...prevInputs, status: selectedOption.value, })); }; - const handleTagChange = (event) => { const newTag = event.target.value; - setInputs((prevInputs) => ({ - ...prevInputs, - tag: [...prevInputs.tag, newTag], - })); + setInputs((prevInputs) => { + const { tags } = prevInputs; + if (tags.length > 0) { + tags.pop(); + } + return { + ...prevInputs, + tags: [...tags, newTag], + }; + }); }; const handleCommentChange = (event) => { const newComment = event.target.value; @@ -81,16 +89,39 @@ const EditModal: React.FC = ({ const handleSubmit = async () => { try { + const body: { + status?: string; + tags?: string[]; + team?: string[]; + idref?: string; + comment?: string; + } = {}; + + if (inputs.status) { + body.status = inputs.status; + } + + if (inputs.tags) { + body.tags = inputs.tags; + } + if (inputs.team) { + body.team = inputs.team; + } + + if (inputs.idRef) { + body.idref = inputs.idRef; + } + + if (inputs.comment) { + body.comment = inputs.comment; + } + const response = await fetch(url, { method: "PATCH", headers: postHeaders, - body: JSON.stringify({ - status: inputs.status, - tag: inputs.tag, - idref: inputs.idRef, - comment: inputs.comment, - }), + body: JSON.stringify(body), }); + if (!response.ok) { console.log("Erreur de réponse", response); } else { @@ -98,9 +129,17 @@ const EditModal: React.FC = ({ console.log("Données de réponse", responseData); refetch(); onClose(); + + if (inputs.tags) { + toast.success("Nouveau tag ajouté!"); + } else if (inputs.comment) { + toast.success("Nouveau commentaire ajouté!"); + } else if (inputs.idRef) { + toast.success("Nouvelle référence ajoutée!"); + } } } catch (error) { - console.error(error); + console.error("Erreur lors de la soumission du formulaire", error); } }; @@ -143,7 +182,7 @@ const EditModal: React.FC = ({ label="Ajouter un tag" maxLength={6} hint="Décrivez en un mot la contribution" - value={inputs.tag} + value={inputs.tags} onChange={handleTagChange} /> diff --git a/src/layout/Header.tsx b/src/layout/Header.tsx index 0383444..dedc34b 100644 --- a/src/layout/Header.tsx +++ b/src/layout/Header.tsx @@ -8,6 +8,7 @@ import { Nav, FastAccess, Button, + NavItem, } from "@dataesr/dsfr-plus"; import ProfileModal from "../components/profil-modal"; @@ -91,12 +92,23 @@ const Header: React.FC = () => { Contributions via formulaire de contact - - Opérations sur l'API - + + Lier des publications + + + Supprimer des personnes de la base de donnée + + + Changer le nom d'une personne + + ); diff --git a/src/pages/api-operation-page/contributor-requests.tsx b/src/pages/api-operation-page/contributor-requests.tsx deleted file mode 100644 index ecb03fa..0000000 --- a/src/pages/api-operation-page/contributor-requests.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { Col, Row, Text } from "@dataesr/dsfr-plus"; -import React, { ReactNode, useState } from "react"; -import { Production } from "../../types"; -import { ExternalLinks } from "./external-links"; -import SelectWithNames from "./name-selector"; - -const ContributorRequests: React.FC<{ - data: { - id: any; - name: ReactNode; - productions: Production[]; - }; - setDataList; - coloredName; -}> = ({ data, setDataList, coloredName }) => { - const [copiedId, setCopiedId] = useState(null); - - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text).then(() => { - setCopiedId(text); - }); - }; - - return ( - <> - {data.productions.map((production, index) => ( - - - - ID :{" "} - copyToClipboard(production.id)}> - {production.id} - - {copiedId === production.id && ( - - ID copié - - )} - - - - - - - - - - ))} - - ); -}; - -export default ContributorRequests; diff --git a/src/pages/api-operation-page/external-links.tsx b/src/pages/api-operation-page/external-links.tsx deleted file mode 100644 index 4859e51..0000000 --- a/src/pages/api-operation-page/external-links.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Link } from "@dataesr/dsfr-plus"; - -export const ExternalLinks = ({ productionId, name }) => ( - <> - - scanR - - - Google - - -); diff --git a/src/pages/api-operation-page/contribution-production-card.tsx b/src/pages/api-operation-page/link-publications/contribution-production-card.tsx similarity index 94% rename from src/pages/api-operation-page/contribution-production-card.tsx rename to src/pages/api-operation-page/link-publications/contribution-production-card.tsx index 5e7fe56..e87bf70 100644 --- a/src/pages/api-operation-page/contribution-production-card.tsx +++ b/src/pages/api-operation-page/link-publications/contribution-production-card.tsx @@ -8,7 +8,7 @@ import { Text, } from "@dataesr/dsfr-plus"; import "./styles.scss"; -import { Contribute_Production } from "../../types"; +import { Contribute_Production } from "../../../types"; import ContributorProductionInfo from "./contributor-production-info"; import StaffProductionActions from "./staff-production-action"; @@ -16,10 +16,12 @@ const ContributionProductionItem = ({ data, refetch, setDataList, + dataList, }: { data: Contribute_Production; refetch; setDataList; + dataList; }) => { const renderAccordion = () => ( @@ -72,6 +74,7 @@ const ContributionProductionItem = ({ data={data} refetch={refetch} setDataList={setDataList} + dataList={dataList} /> diff --git a/src/pages/api-operation-page/contributor-production-info.tsx b/src/pages/api-operation-page/link-publications/contributor-production-info.tsx similarity index 54% rename from src/pages/api-operation-page/contributor-production-info.tsx rename to src/pages/api-operation-page/link-publications/contributor-production-info.tsx index 0cbc761..53d5690 100644 --- a/src/pages/api-operation-page/contributor-production-info.tsx +++ b/src/pages/api-operation-page/link-publications/contributor-production-info.tsx @@ -1,17 +1,24 @@ -import { Contribute_Production } from "../../types"; +import { Contribute_Production } from "../../../types"; import MessagePreview from "./message-preview"; const ContributorProductionInfo = ({ data, refetch, setDataList, + dataList, }: { data: Contribute_Production; refetch; setDataList; + dataList; }) => { return ( - + ); }; diff --git a/src/pages/api-operation-page/link-publications/contributor-requests.tsx b/src/pages/api-operation-page/link-publications/contributor-requests.tsx new file mode 100644 index 0000000..5b3c378 --- /dev/null +++ b/src/pages/api-operation-page/link-publications/contributor-requests.tsx @@ -0,0 +1,88 @@ +import { Col } from "@dataesr/dsfr-plus"; +import React, { ReactNode, useEffect, useState } from "react"; +import { FaShoppingCart } from "react-icons/fa"; +import { Production } from "../../../types"; +import SelectWithNames from "./name-selector"; +import { ExternalLinks } from "./external-links"; + +const ContributorRequests: React.FC<{ + data: { + id: any; + name: ReactNode; + productions: Production[]; + }; + setDataList; + coloredName; + dataList; +}> = ({ data, setDataList, coloredName, dataList }) => { + const [copiedId, setCopiedId] = useState(null); + const [selectedIds, setSelectedIds] = useState([]); + const [, setIsSelected] = useState(false); + + useEffect(() => { + setSelectedIds(dataList.map((item) => item.publi_id)); + }, [dataList]); + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text).then(() => { + setCopiedId(text); + }); + }; + + return ( + <> + {data.productions.map((production) => { + const isSelected = selectedIds.includes(production.id); + return ( + +
{ + copyToClipboard(production.id); + }} + style={{ flex: 2, marginRight: "10px" }} + > + ID de la publication : {production.id} + {isSelected && ( + + )} +
+ {copiedId === production.id && ( + + ID copié + + )} + + + + + + + + ); + })} + + ); +}; + +export default ContributorRequests; diff --git a/src/pages/api-operation-page/export-to-xlsx.tsx b/src/pages/api-operation-page/link-publications/export-to-xlsx.tsx similarity index 58% rename from src/pages/api-operation-page/export-to-xlsx.tsx rename to src/pages/api-operation-page/link-publications/export-to-xlsx.tsx index e393cb6..291679c 100644 --- a/src/pages/api-operation-page/export-to-xlsx.tsx +++ b/src/pages/api-operation-page/link-publications/export-to-xlsx.tsx @@ -1,6 +1,7 @@ -import { Button, Col, Row, Text, Title } from "@dataesr/dsfr-plus"; +import { Badge, Button, ButtonGroup, Text, Title } from "@dataesr/dsfr-plus"; import * as XLSX from "xlsx"; import { AiOutlineDelete } from "react-icons/ai"; +import { toast } from "react-toastify"; const ExcelExportButton = ({ dataList, setDataList }) => { const handleExportClick = () => { @@ -17,7 +18,25 @@ const ExcelExportButton = ({ dataList, setDataList }) => { XLSX.writeFile(workbook, "export.xlsx"); }; const handleRemoveClick = (index: number) => { - setDataList((prevDataList) => prevDataList.filter((_, i) => i !== index)); + setDataList((prevState) => { + const newList = prevState.filter((_, i) => i !== index); + toast(`Element retiré ! : ${prevState[index].fullName}`, { + style: { + backgroundColor: "#d64d00", + color: "#fff", + }, + }); + return newList; + }); + }; + + const handleClearClick = () => { + setDataList([]); + toast("Panier vidé !", { + style: { + backgroundColor: "#c3fad5", + }, + }); }; return ( @@ -27,6 +46,17 @@ const ExcelExportButton = ({ dataList, setDataList }) => { Liste des publications à exporter
    +
    + + {`${dataList.length} publication${ + dataList.length > 1 ? "s" : "" + }`} + +
    {Array.isArray(dataList) && dataList.map((item, index) => (
  • {
  • ))}
- +
+ + + + +
); diff --git a/src/pages/api-operation-page/link-publications/external-links.tsx b/src/pages/api-operation-page/link-publications/external-links.tsx new file mode 100644 index 0000000..0a41c75 --- /dev/null +++ b/src/pages/api-operation-page/link-publications/external-links.tsx @@ -0,0 +1,25 @@ +import { Link } from "@dataesr/dsfr-plus"; + +export const ExternalLinks = ({ productionId, name }) => { + const formattedProductionId = productionId.replace(/\//g, "%2f"); + return ( + <> + + scanR + + + Google + + + ); +}; diff --git a/src/pages/api-operation-page/index.tsx b/src/pages/api-operation-page/link-publications/index.tsx similarity index 89% rename from src/pages/api-operation-page/index.tsx rename to src/pages/api-operation-page/link-publications/index.tsx index e0a2637..cc280b6 100644 --- a/src/pages/api-operation-page/index.tsx +++ b/src/pages/api-operation-page/link-publications/index.tsx @@ -7,14 +7,14 @@ import { Text, Title, } from "@dataesr/dsfr-plus"; -import { Contribute_Production, ContributionPageProps } from "../../types"; +import { Contribute_Production, 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 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"; -import { buildURL } from "../../api/utils/buildURL"; +import ContributionData from "../../../api/contribution-api/getData"; +import { buildURL } from "../../../api/utils/buildURL"; import ExcelExportButton from "./export-to-xlsx"; const ContributionPage: React.FC = () => { @@ -96,7 +96,7 @@ const ContributionPage: React.FC = () => { - Opérations sur l'API + Lier des publications = () => { data={contribution as Contribute_Production} refetch={refetch} setDataList={setDataList} + dataList={dataList} /> ))} {dataList.length > 0 && ( diff --git a/src/pages/api-operation-page/message-preview.tsx b/src/pages/api-operation-page/link-publications/message-preview.tsx similarity index 94% rename from src/pages/api-operation-page/message-preview.tsx rename to src/pages/api-operation-page/link-publications/message-preview.tsx index 7576eb7..b022d33 100644 --- a/src/pages/api-operation-page/message-preview.tsx +++ b/src/pages/api-operation-page/link-publications/message-preview.tsx @@ -1,19 +1,21 @@ import { Button, Col, Container, Link, Row, Text } from "@dataesr/dsfr-plus"; -import type { Contribute_Production } from "../../types"; -import EditModal from "../../components/edit-modal"; +import type { Contribute_Production } from "../../../types"; +import EditModal from "../../../components/edit-modal"; import { useState, useCallback } from "react"; import ContributorRequests from "./contributor-requests"; import "./styles.scss"; -import NameFromIdref from "../../api/contribution-api/getNamesFromIdref"; +import NameFromIdref from "../../../api/contribution-api/getNamesFromIdref"; const MessagePreview = ({ data, refetch, setDataList, + dataList, }: { data: Contribute_Production; refetch; setDataList: any; + dataList; }) => { const [showModal, setShowModal] = useState(false); const [copySuccess, setCopySuccess] = useState(""); @@ -145,6 +147,7 @@ const MessagePreview = ({ data={data} setDataList={setDataList} coloredName={data.name} + dataList={dataList} /> diff --git a/src/pages/api-operation-page/link-publications/name-selector.tsx b/src/pages/api-operation-page/link-publications/name-selector.tsx new file mode 100644 index 0000000..f213c8a --- /dev/null +++ b/src/pages/api-operation-page/link-publications/name-selector.tsx @@ -0,0 +1,71 @@ +import ReactSelect from "react-select"; +import "react-toastify/dist/ReactToastify.css"; +import NameFromScanr from "../../../api/contribution-api/getNames"; +import { levenshteinDistance } from "../utils/compare"; +import { Col, Row } from "@dataesr/dsfr-plus"; +import { useEffect, useState } from "react"; + +export default function SelectWithNames({ + productionId, + setDataList, + idRef, + coloredName, + setSelectedId, + setIsSelected, +}) { + const { fullName, firstName, lastName } = NameFromScanr(productionId); + const [selectedOption, setSelectedOption] = useState(null); + const customStyles = { + option: (provided, state) => ({ + ...provided, + color: state.data.isColored ? "#1f8d49" : "black", + }), + }; + + const threshold = 7; + + const handleChange = (option) => { + setSelectedOption(option); + }; + + useEffect(() => { + const optionToValidate = + selectedOption || options.find((option) => option.label === coloredName); + + if (optionToValidate) { + const selectedIndex = fullName.indexOf(optionToValidate.value); + setDataList((prevState) => [ + ...prevState, + { + fullName: optionToValidate.value, + person_id: idRef, + publi_id: productionId, + first_name: firstName[selectedIndex], + last_name: lastName[selectedIndex], + }, + ]); + setSelectedId((prevIds) => [...prevIds, productionId]); + setIsSelected(true); + } + }, [selectedOption]); + + const options = fullName.map((name, index) => ({ + value: name, + label: name, + firstName: firstName[index], + isColored: levenshteinDistance(name, coloredName) <= threshold, + })); + + return ( + + + + + + ); +} diff --git a/src/pages/api-operation-page/staff-production-action.tsx b/src/pages/api-operation-page/link-publications/staff-production-action.tsx similarity index 86% rename from src/pages/api-operation-page/staff-production-action.tsx rename to src/pages/api-operation-page/link-publications/staff-production-action.tsx index dce6db1..361ec8a 100644 --- a/src/pages/api-operation-page/staff-production-action.tsx +++ b/src/pages/api-operation-page/link-publications/staff-production-action.tsx @@ -1,7 +1,7 @@ import { Col, Text } from "@dataesr/dsfr-plus"; import "./styles.scss"; -import EmailSender from "../../api/send-mail"; -import { Contribute_Production } from "../../types"; +import { Contribute_Production } from "../../../types"; +import EmailSender from "../../../api/send-mail"; const StaffProductionActions = ({ data, diff --git a/src/pages/api-operation-page/styles.scss b/src/pages/api-operation-page/link-publications/styles.scss similarity index 69% rename from src/pages/api-operation-page/styles.scss rename to src/pages/api-operation-page/link-publications/styles.scss index f03eb07..18207fb 100644 --- a/src/pages/api-operation-page/styles.scss +++ b/src/pages/api-operation-page/link-publications/styles.scss @@ -3,11 +3,12 @@ justify-content: center; justify-content: space-evenly; } -.contributorProductionSide { +.contributorProductionContent { display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; + align-items: center; + justify-content: space-evenly; + padding: 10px; + border-bottom: 1px solid black; } .staffSide { text-align: right; @@ -45,3 +46,18 @@ flex-direction: column; align-items: center; } +@keyframes flash { + 0% { + background-color: transparent; + } + 50% { + background-color: yellow; + } + 100% { + background-color: transparent; + } +} + +.basket.item-added { + animation: flash 0.5s; +} diff --git a/src/pages/api-operation-page/name-selector.tsx b/src/pages/api-operation-page/name-selector.tsx deleted file mode 100644 index 2199c2f..0000000 --- a/src/pages/api-operation-page/name-selector.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import ReactSelect from "react-select"; -import NameFromScanr from "../../api/contribution-api/getNames"; -import { levenshteinDistance } from "./utils/compare"; - -export default function SelectWithNames({ - productionId, - setDataList, - idRef, - coloredName, -}) { - const { fullName, firstName, lastName } = NameFromScanr(productionId); - const customStyles = { - option: (provided, state) => ({ - ...provided, - color: state.data.isColored ? "#1f8d49" : "black", - }), - }; - - const threshold = 10; - - const handleChange = (selectedOption) => { - const selectedIndex = fullName.indexOf(selectedOption.value); - setDataList((prevState) => [ - ...prevState, - { - fullName: selectedOption.value, - person_id: idRef, - publi_id: productionId, - first_name: firstName[selectedIndex], - last_name: lastName[selectedIndex], - }, - ]); - }; - - const options = fullName.map((name, index) => ({ - value: name, - label: name, - firstName: firstName[index], - isColored: levenshteinDistance(name, coloredName) <= threshold, - })); - - return ( - - ); -} diff --git a/src/pages/change-name/index.tsx b/src/pages/change-name/index.tsx new file mode 100644 index 0000000..0633651 --- /dev/null +++ b/src/pages/change-name/index.tsx @@ -0,0 +1,11 @@ +import { Container, Title } from "@dataesr/dsfr-plus"; + +const ChangeNamePage = () => { + return ( + + Changer le nom des personnes de la base de donnée + + ); +}; + +export default ChangeNamePage; diff --git a/src/pages/contribution-page/contribution-card.tsx b/src/pages/contribution-page/contribution-card.tsx index 8b5fd67..3ef5fbe 100644 --- a/src/pages/contribution-page/contribution-card.tsx +++ b/src/pages/contribution-page/contribution-card.tsx @@ -28,13 +28,13 @@ const ContributionItem = ({ - {data.tags && ( + {data?.tags?.length > 0 && ( - {data.tags.join(", ")} + {data?.tags.join(", ")} )} { + return ( + + Supprimer des personnes de la base de donnée + + ); +}; + +export default DeletePage; diff --git a/src/router.tsx b/src/router.tsx index a481973..ddb202a 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -2,8 +2,10 @@ import { Route, Routes } from "react-router-dom"; import Layout from "./layout"; import Home from "./pages/home"; -import ApiOperationPage from "./pages/api-operation-page"; +import ApiOperationPage from "./pages/api-operation-page/link-publications"; import ContributionPage from "./pages/contribution-page"; +import DeletePage from "./pages/delete-persons"; +import ChangeNamePage from "./pages/change-name"; export default function Router() { return ( @@ -16,6 +18,8 @@ export default function Router() { /> } /> } /> + } /> + } /> ); diff --git a/src/types/index.ts b/src/types/index.ts index 305f717..d79dcce 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -83,6 +83,10 @@ export type EditModalProps = { }; export type Production = { + lastName: any; + firstName: any; + productionId: any; + fullName: any; name: string; id: string; treated: boolean; @@ -91,7 +95,7 @@ export type Production = { export type Inputs = { team: string[]; status: string; - tag: string[]; + tags: string[]; idRef: string; comment: string; };