From da9229675379235a5313a52f1c6e15366215451d Mon Sep 17 00:00:00 2001 From: Lionel Date: Fri, 12 Mar 2021 17:06:35 +0100 Subject: [PATCH] feat: add handle service-public.fr (#359) * feat: add handle service-public.fr * refactor: split files * fix: import * fix button --- shared/graphql-client/src/index.d.ts | 1 + .../frontend/src/components/dialog/index.js | 2 +- .../src/components/fiches-sp/addFiche.js | 45 ++ .../src/components/fiches-sp/addFicheForm.js | 130 ++++ .../components/fiches-sp/fichesSpContainer.js | 96 +++ .../frontend/src/components/fiches-sp/list.js | 39 ++ .../src/components/fiches-sp/selectActions.js | 56 ++ targets/frontend/src/components/layout/Nav.js | 5 + .../pages/contenus/create/[[...source]].js | 4 +- .../frontend/src/pages/contenus/fiches-sp.js | 23 + targets/hasura/metadata/tables.yaml | 3 + .../down.sql | 1 + .../up.sql | 571 ++++++++++++++++++ .../transform/fichesServicePublic/filter.js | 161 +---- .../transform/fichesServicePublic/index.js | 44 +- 15 files changed, 1021 insertions(+), 160 deletions(-) create mode 100644 targets/frontend/src/components/fiches-sp/addFiche.js create mode 100644 targets/frontend/src/components/fiches-sp/addFicheForm.js create mode 100644 targets/frontend/src/components/fiches-sp/fichesSpContainer.js create mode 100644 targets/frontend/src/components/fiches-sp/list.js create mode 100644 targets/frontend/src/components/fiches-sp/selectActions.js create mode 100644 targets/frontend/src/pages/contenus/fiches-sp.js create mode 100644 targets/hasura/migrations/1615474434522_create_table_public_service_public_contents/down.sql create mode 100644 targets/hasura/migrations/1615474434522_create_table_public_service_public_contents/up.sql diff --git a/shared/graphql-client/src/index.d.ts b/shared/graphql-client/src/index.d.ts index 0877d9950..49d9f3de3 100644 --- a/shared/graphql-client/src/index.d.ts +++ b/shared/graphql-client/src/index.d.ts @@ -2,4 +2,5 @@ import { Client } from '@urql/core'; declare module '@shared/graphql-client' { export const client: Client + export * from "@urql/core" } diff --git a/targets/frontend/src/components/dialog/index.js b/targets/frontend/src/components/dialog/index.js index 1db80826a..fe18080b2 100644 --- a/targets/frontend/src/components/dialog/index.js +++ b/targets/frontend/src/components/dialog/index.js @@ -35,7 +35,7 @@ export function Dialog({ const styles = { closeBt: css({ - position: "fixed", + position: "absolute", right: "xxsmall", top: "xxsmall", }), diff --git a/targets/frontend/src/components/fiches-sp/addFiche.js b/targets/frontend/src/components/fiches-sp/addFiche.js new file mode 100644 index 000000000..8da434469 --- /dev/null +++ b/targets/frontend/src/components/fiches-sp/addFiche.js @@ -0,0 +1,45 @@ +import { useState } from "react"; +import { Box } from "theme-ui"; +import { useMutation } from "urql"; + +import { Button } from "../button"; +import { Dialog } from "../dialog"; +import { AddFicheSpForm } from "./addFicheForm"; + +export function AddFiches() { + const [showAddDialog, setAddDialogVisible] = useState(false); + const openAddDialog = () => setAddDialogVisible(true); + const closeAddDialog = () => setAddDialogVisible(false); + const [, insertFicheMutation] = useMutation(insertFicheServicePublicId); + const insertFicheSp = (ids) => { + insertFicheMutation({ + objects: ids.map((id) => ({ id })), + }); + closeAddDialog(); + }; + return ( + + + + + + + ); +} + +const insertFicheServicePublicId = ` +mutation addFichesServicePublic($objects: [service_public_contents_insert_input!]!) { + fiches: insert_service_public_contents(objects: $objects) { + affected_rows + returning { + id, status + } + } +} +`; diff --git a/targets/frontend/src/components/fiches-sp/addFicheForm.js b/targets/frontend/src/components/fiches-sp/addFicheForm.js new file mode 100644 index 000000000..de7742f85 --- /dev/null +++ b/targets/frontend/src/components/fiches-sp/addFicheForm.js @@ -0,0 +1,130 @@ +import { ErrorMessage } from "@hookform/error-message"; +import { useMemo } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { IoMdCheckmark, IoMdClose } from "react-icons/io"; +import { Box, Field, Flex, Heading, Text } from "theme-ui"; +import { useQuery } from "urql"; + +import { Button, IconButton } from "../button"; + +export function AddFicheSpForm({ onAdd }) { + const [result] = useQuery({ + query: `query ids { + ids: service_public_contents { id } + }`, + }); + const { data: { ids = [] } = {} } = result; + const existingIds = useMemo(() => ids.map(({ id }) => id), [ids]); + + const { + control, + register, + handleSubmit, + errors, + formState: { isDirty }, + } = useForm({ + defaultValues: { + items: [{ id: "" }], + }, + }); + const { fields, append, remove } = useFieldArray({ + control, + keyName: "key", + name: "items", + }); + const handleKeyDown = (event) => { + // 13 is the keyCode for "enter" + if (event.keyCode === 13) { + event.preventDefault(); + append({ id: "" }); + } + }; + function onSubmit({ items }) { + const uniqueIds = [...new Set(items.map(({ id }) => id.toUpperCase()))]; + const filteredIds = uniqueIds.filter((id) => !existingIds.includes(id)); + onAdd(filteredIds); + } + return ( +
+ + Ajouter des fiches + + {`Renseignez l’identifiant ${ + fields.length > 1 ? "des" : "de la" + } fiche${fields.length > 1 ? "s" : ""} à ajouter`} + {fields.map((field, index) => { + return ( + + + {" "} + {fields.length > 1 && ( + remove(index)} + > + + + )} + {index === fields.length - 1 && ( + + )} + + {message}} + /> + + ); + })} + + + +
+ ); +} diff --git a/targets/frontend/src/components/fiches-sp/fichesSpContainer.js b/targets/frontend/src/components/fiches-sp/fichesSpContainer.js new file mode 100644 index 000000000..42d57bc26 --- /dev/null +++ b/targets/frontend/src/components/fiches-sp/fichesSpContainer.js @@ -0,0 +1,96 @@ +import { useRouter } from "next/router"; +import { useCallback, useContext, useMemo } from "react"; +import { SelectionContext } from "src/pages/contenus/fiches-sp"; +import { Message, Spinner } from "theme-ui"; +import { useMutation, useQuery } from "urql"; + +import { Stack } from "../layout/Stack"; +import { Pagination } from "../pagination"; +import { AddFiches } from "./addFiche"; +import { ServicPublicList } from "./list"; +import { Actions } from "./selectActions"; + +export function FichesServicePublicContainer() { + const router = useRouter(); + const context = useMemo( + () => ({ additionalTypenames: ["service_public_contents"] }), + [] + ); + + const [, setSelectedItems] = useContext(SelectionContext); + + const itemsPerPage = 25; + + const page = parseInt(router.query?.page) || 0; + + const [result] = useQuery({ + context, + query: getFicheServicePublicId, + variables: { + limit: itemsPerPage, + offset: page * itemsPerPage, + }, + }); + + const [, deleteFicheMutation] = useMutation(deleteFicheServicePublicId); + const deleteFicheSp = useCallback( + (ids) => { + deleteFicheMutation({ ids }); + setSelectedItems([]); + }, + [deleteFicheMutation] + ); + + const { fetching, error, data } = result; + + if (error) { + return {error.message}; + } + if (fetching) { + return ; + } + return ( + + + {data.ficheIds.length > 0 ? ( + + + + + + ) : ( + "Pas de résultat" + )} + + ); +} + +const getFicheServicePublicId = ` +query getServicePublicId($offset: Int = 0, $limit: Int = 50) { + ficheIds: service_public_contents( offset: $offset, limit: $limit, order_by: [{status: desc, id: asc}]) { + id, status + } + aggs:service_public_contents_aggregate { + aggregate{ + count + } + } +} +`; + +const deleteFicheServicePublicId = ` +mutation deleteServicePublicIds($ids: [String!] = []) { + delete_service_public_contents(where: { + id: {_in: $ids} + }) { + affected_rows + returning { + id + } + } +} +`; diff --git a/targets/frontend/src/components/fiches-sp/list.js b/targets/frontend/src/components/fiches-sp/list.js new file mode 100644 index 000000000..2c7f62c47 --- /dev/null +++ b/targets/frontend/src/components/fiches-sp/list.js @@ -0,0 +1,39 @@ +import { useContext } from "react"; +import { SelectionContext } from "src/pages/contenus/fiches-sp"; +import { Label } from "theme-ui"; + +import { Li, List } from "../list"; + +export function ServicPublicList({ items }) { + return ( + + {items.map((item) => ( +
  • + +
  • + ))} +
    + ); +} + +function ServicePublicItem({ item }) { + const [selectedItems, setSelectedItems] = useContext(SelectionContext); + + function updateSelection(event) { + if (event.target.checked) { + setSelectedItems(selectedItems.concat(item.id)); + } else { + setSelectedItems(selectedItems.filter((id) => item.id !== id)); + } + } + return ( + + ); +} diff --git a/targets/frontend/src/components/fiches-sp/selectActions.js b/targets/frontend/src/components/fiches-sp/selectActions.js new file mode 100644 index 000000000..ad3b371e5 --- /dev/null +++ b/targets/frontend/src/components/fiches-sp/selectActions.js @@ -0,0 +1,56 @@ +import { useContext, useState } from "react"; +import { SelectionContext } from "src/pages/contenus/fiches-sp"; +import { Box, Text } from "theme-ui"; + +import { Button } from "../button"; +import { Dialog } from "../dialog"; +import { Inline } from "../layout/Inline"; +import { Stack } from "../layout/Stack"; + +export function Actions({ onDelete }) { + const [selectedItems] = useContext(SelectionContext); + const [showDeleteDialog, setDeleteDialogVisible] = useState(false); + const openDeleteDialog = () => setDeleteDialogVisible(true); + const closeDeleteDialog = () => setDeleteDialogVisible(false); + + function deleteAction() { + onDelete(selectedItems); + closeDeleteDialog(); + } + + return ( + + + + + + Êtes vous sûr de vouloir supprimer {selectedItems.length} fiches? + + + + + + + + + {selectedItems.length > 0 && ( + + )} + + ); +} diff --git a/targets/frontend/src/components/layout/Nav.js b/targets/frontend/src/components/layout/Nav.js index b59ba30d9..f26cbfb9b 100644 --- a/targets/frontend/src/components/layout/Nav.js +++ b/targets/frontend/src/components/layout/Nav.js @@ -136,6 +136,11 @@ export function Nav() { Contenus sans thème +
  • + + fiches service-public + +
  • diff --git a/targets/frontend/src/pages/contenus/create/[[...source]].js b/targets/frontend/src/pages/contenus/create/[[...source]].js index 5c65d3f6f..359cb87dc 100644 --- a/targets/frontend/src/pages/contenus/create/[[...source]].js +++ b/targets/frontend/src/pages/contenus/create/[[...source]].js @@ -109,11 +109,11 @@ export function CreateDocumentPage() { } return ( - +