From 96cc8c2f0d14a932b7bfb19315d90a5b96ca5a83 Mon Sep 17 00:00:00 2001 From: Tomi Korkalainen <77731851+tkork@users.noreply.github.com> Date: Tue, 28 Jun 2022 09:36:52 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20Mahdollista=20aineistojen=20lis=C3=A4ys?= =?UTF-8?q?=20p=C3=A4=C3=A4kategorioihin=20(#286)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/handler/projektiAdapter.ts | 1 + common/aineistoKategoriat.ts | 4 + src/components/ExtLink.tsx | 8 +- src/components/HassuAccordion.tsx | 4 +- src/components/form/Select.tsx | 8 +- .../kuulutuksentiedot/KuulutuksenTiedot.tsx | 5 +- .../NahtavillaoloPainikkeet.tsx | 20 +- .../NahtavilleAsetettavatAineistot.tsx | 50 ++++- .../SuunnitelmatJaAineistot.tsx | 195 +++++++++++++++++- 9 files changed, 275 insertions(+), 20 deletions(-) diff --git a/backend/src/handler/projektiAdapter.ts b/backend/src/handler/projektiAdapter.ts index 2c7e88287..fec9090b2 100644 --- a/backend/src/handler/projektiAdapter.ts +++ b/backend/src/handler/projektiAdapter.ts @@ -538,6 +538,7 @@ function adaptAineistotToSave( // Update existing one dbAineisto.nimi = updateAineistoInput.nimi; dbAineisto.jarjestys = updateAineistoInput.jarjestys; + dbAineisto.kategoriaId = updateAineistoInput.kategoriaId; resultAineistot.push(dbAineisto); } if (!updateAineistoInput) { diff --git a/common/aineistoKategoriat.ts b/common/aineistoKategoriat.ts index 0e2db6a1c..065ee0cc6 100644 --- a/common/aineistoKategoriat.ts +++ b/common/aineistoKategoriat.ts @@ -36,6 +36,10 @@ export class AineistoKategoria { public get hakulauseet() { return this.props.hakulauseet; } + + public get nimi() { + return this.props.nimi; + } } export class AineistoKategoriat { diff --git a/src/components/ExtLink.tsx b/src/components/ExtLink.tsx index 4c772b731..d28022f49 100644 --- a/src/components/ExtLink.tsx +++ b/src/components/ExtLink.tsx @@ -41,6 +41,12 @@ const ExtLink = ( ); -const Anchor = styled("a")({ display: "flex", flexFlow: "row wrap" }); +const Anchor = styled("a")({ + display: "flex", + flexFlow: "row", + alignItems: "flex-start", + textAlign: "left", + justifyContent: "flex-start", +}); export default React.forwardRef(ExtLink); diff --git a/src/components/HassuAccordion.tsx b/src/components/HassuAccordion.tsx index 12b01cd18..116e0c1d8 100644 --- a/src/components/HassuAccordion.tsx +++ b/src/components/HassuAccordion.tsx @@ -49,7 +49,7 @@ const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ })); interface AccordionItem { - title: string; + title: string | JSX.Element; content: ReactNode | string; id?: Key; } @@ -88,7 +88,7 @@ export default function CustomizedAccordions(props: Props) { onChange={handleChange(key)} > - {item.title} + {typeof item.title === "string" ? {item.title} : item.title} {item.content} diff --git a/src/components/form/Select.tsx b/src/components/form/Select.tsx index b2df1059b..4b5061431 100644 --- a/src/components/form/Select.tsx +++ b/src/components/form/Select.tsx @@ -4,11 +4,17 @@ import React from "react"; import { FieldError } from "react-hook-form"; import FormGroup from "./FormGroup"; +export interface SelectOption { + label: string; + value: string; + disabled?: boolean; +} + interface Props { error?: FieldError; label?: string; hideErrorMessage?: boolean; - options: { label: string; value: string; disabled?: boolean }[]; + options: SelectOption[]; addEmptyOption?: boolean; } diff --git a/src/components/projekti/nahtavillaolo/kuulutuksentiedot/KuulutuksenTiedot.tsx b/src/components/projekti/nahtavillaolo/kuulutuksentiedot/KuulutuksenTiedot.tsx index 8c6ff6fcb..ba42070e5 100644 --- a/src/components/projekti/nahtavillaolo/kuulutuksentiedot/KuulutuksenTiedot.tsx +++ b/src/components/projekti/nahtavillaolo/kuulutuksentiedot/KuulutuksenTiedot.tsx @@ -16,7 +16,10 @@ import { removeTypeName } from "src/util/removeTypeName"; import Lukunakyma from "./Lukunakyma"; import useKirjaamoOsoitteet from "src/hooks/useKirjaamoOsoitteet"; -function defaultValues(projekti: Projekti, kirjaamoOsoitteet: KirjaamoOsoite[] | undefined) { +function defaultValues( + projekti: Projekti, + kirjaamoOsoitteet: KirjaamoOsoite[] | undefined +): KuulutuksenTiedotFormValues { return { oid: projekti.oid, nahtavillaoloVaihe: { diff --git a/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavillaoloPainikkeet.tsx b/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavillaoloPainikkeet.tsx index 42b8c9485..ea6e40327 100644 --- a/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavillaoloPainikkeet.tsx +++ b/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavillaoloPainikkeet.tsx @@ -2,29 +2,39 @@ import Button from "@components/button/Button"; import HassuSpinner from "@components/HassuSpinner"; import Section from "@components/layout/Section"; import { Stack } from "@mui/material"; -import { api } from "@services/api"; +import { api, TallennaProjektiInput } from "@services/api"; import log from "loglevel"; import React, { useState } from "react"; import { useFormContext } from "react-hook-form"; import { useProjektiRoute } from "src/hooks/useProjektiRoute"; import useSnackbars from "src/hooks/useSnackbars"; +import deleteFieldArrayIds from "src/util/deleteFieldArrayIds"; +import { NahtavilleAsetettavatAineistotFormValues } from "./NahtavilleAsetettavatAineistot"; + +const mapFormValuesToTallennaProjektiInput = ( + formData: NahtavilleAsetettavatAineistotFormValues +): TallennaProjektiInput => { + const aineistoNahtavilla = Object.values(formData.aineistoNahtavilla).flat(); + deleteFieldArrayIds(aineistoNahtavilla); + return { oid: formData.oid, nahtavillaoloVaihe: { aineistoNahtavilla } }; +}; export default function NahtavillaoloPainikkeet() { const { mutate: reloadProjekti } = useProjektiRoute(); const [isFormSubmitting, setIsFormSubmitting] = useState(false); const { showSuccessMessage, showErrorMessage } = useSnackbars(); - const { handleSubmit } = useFormContext(); + const { handleSubmit } = useFormContext(); - const saveSuunnitteluvaihe = async (formData: any) => { + const saveSuunnitteluvaihe = async (formData: TallennaProjektiInput) => { await api.tallennaProjekti(formData); if (reloadProjekti) await reloadProjekti(); }; - const saveDraft = async (formData: any) => { + const saveDraft = async (formData: NahtavilleAsetettavatAineistotFormValues) => { setIsFormSubmitting(true); try { - await saveSuunnitteluvaihe(formData); + await saveSuunnitteluvaihe(mapFormValuesToTallennaProjektiInput(formData)); showSuccessMessage("Tallennus onnistui!"); } catch (e) { log.error("OnSubmit Error", e); diff --git a/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavilleAsetettavatAineistot.tsx b/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavilleAsetettavatAineistot.tsx index 0008a182d..b820f0772 100644 --- a/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavilleAsetettavatAineistot.tsx +++ b/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/NahtavilleAsetettavatAineistot.tsx @@ -1,5 +1,5 @@ import { yupResolver } from "@hookform/resolvers/yup"; -import { TallennaProjektiInput } from "@services/api"; +import { Aineisto, AineistoInput, TallennaProjektiInput } from "@services/api"; import React, { useEffect } from "react"; import { UseFormProps, useForm, FormProvider } from "react-hook-form"; import { useProjektiRoute } from "src/hooks/useProjektiRoute"; @@ -7,13 +7,50 @@ import { nahtavillaoloAineistotSchema } from "src/schemas/nahtavillaoloAineistot import NahtavillaoloPainikkeet from "./NahtavillaoloPainikkeet"; import LausuntopyyntoonLiitettavaLisaaineisto from "./LausuntopyyntoonLiitettavaLisaaineisto"; import SuunnitelmatJaAineistot from "./SuunnitelmatJaAineistot"; +import { ProjektiLisatiedolla } from "src/hooks/useProjekti"; +import { AineistoKategoria, aineistoKategoriat } from "common/aineistoKategoriat"; -type Props = {}; +type AineistoNahtavilla = { + aineistoNahtavilla: { + [kategoriaId: string]: AineistoInput[]; + }; +}; + +export type NahtavilleAsetettavatAineistotFormValues = Pick & AineistoNahtavilla; -export type NahtavilleAsetettavatAineistotFormValues = Pick; +const addAineistoKategoria = ( + aineistoNahtavilla: AineistoNahtavilla["aineistoNahtavilla"], + kategoria: AineistoKategoria, + allAineisto: Aineisto[] | undefined | null +) => { + const aineisto: Aineisto[] = allAineisto?.filter(({ kategoriaId }) => kategoriaId === kategoria.id) || []; + aineistoNahtavilla[kategoria.id] = aineisto.map(({ dokumenttiOid, jarjestys, nimi, kategoriaId }) => ({ + dokumenttiOid, + nimi, + jarjestys, + kategoriaId, + })); + // Uncomment to add alakategoriat recursively + // kategoria.alaKategoriat?.forEach((k) => addAineistoKategoria(aineistoNahtavilla, k)); +}; -export default function NahtavilleAsetettavatAineistot({}: Props) { +function defaultValues(projekti: ProjektiLisatiedolla): NahtavilleAsetettavatAineistotFormValues { + const aineistoNahtavilla = aineistoKategoriat + .listKategoriat() + .reduce((aineistot, currentKategoria) => { + addAineistoKategoria(aineistot, currentKategoria, projekti.nahtavillaoloVaihe?.aineistoNahtavilla); + return aineistot; + }, {}); + + return { + oid: projekti.oid, + aineistoNahtavilla, + }; +} + +export default function NahtavilleAsetettavatAineistot() { const { data: projekti } = useProjektiRoute(); + const formOptions: UseFormProps = { resolver: yupResolver(nahtavillaoloAineistotSchema, { abortEarly: false, recursive: true }), mode: "onChange", @@ -25,9 +62,8 @@ export default function NahtavilleAsetettavatAineistot({}: Props) { useEffect(() => { if (projekti?.oid) { - const tallentamisTiedot: NahtavilleAsetettavatAineistotFormValues = { - oid: projekti.oid, - }; + const tallentamisTiedot = defaultValues(projekti); + console.log({ tallentamisTiedot }); reset(tallentamisTiedot); } }, [projekti, reset]); diff --git a/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/SuunnitelmatJaAineistot.tsx b/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/SuunnitelmatJaAineistot.tsx index cfb5124df..d8fe35748 100644 --- a/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/SuunnitelmatJaAineistot.tsx +++ b/src/components/projekti/nahtavillaolo/nahtavilleAsetettavatAineistot/SuunnitelmatJaAineistot.tsx @@ -1,10 +1,36 @@ +import Button from "@components/button/Button"; +import IconButton from "@components/button/IconButton"; +import Select, { SelectOption } from "@components/form/Select"; +import HassuAccordion from "@components/HassuAccordion"; +import HassuTable from "@components/HassuTable"; import Section from "@components/layout/Section"; +import SectionContent from "@components/layout/SectionContent"; import Notification, { NotificationType } from "@components/notification/Notification"; -import React from "react"; +import AineistoNimiExtLink from "@components/projekti/AineistoNimiExtLink"; +import AineistojenValitseminenDialog from "@components/projekti/suunnitteluvaihe/AineistojenValitseminenDialog"; +import { VuorovaikutusFormValues } from "@components/projekti/suunnitteluvaihe/SuunnitteluvaiheenVuorovaikuttaminen"; +import { Aineisto } from "@services/api"; +import { AineistoKategoria, aineistoKategoriat } from "common/aineistoKategoriat"; +import { find } from "lodash"; +import React, { useMemo, useState } from "react"; +import { FieldArrayWithId, useFieldArray, useFormContext } from "react-hook-form"; +import { Column } from "react-table"; +import { useHassuTable } from "src/hooks/useHassuTable"; +import { useProjektiRoute } from "src/hooks/useProjektiRoute"; +import { formatDateTime } from "src/util/dateUtils"; +import { NahtavilleAsetettavatAineistotFormValues } from "./NahtavilleAsetettavatAineistot"; -type Props = {}; +const kategoriaInfoText: Record = { + T1xx: "Selostusosan alle tuodaan A- tai T100 -kansioiden aineistot.", + T2xx: "Pääpiirustusten alle tuodaan B- tai T200 -kansioiden aineistot.", + T3xx: "Informatiivisen aineiston alle tuodaan C- tai T300 -kansioiden aineistot.", +}; -export default function SuunnitelmatJaAineistot({}: Props) { +export default function SuunnitelmatJaAineistot() { + const { watch } = useFormContext(); + const some = watch("aineistoNahtavilla"); + + console.log(aineistoKategoriat.listKategoriat()); return (

Suunnitelmat ja aineistot

@@ -15,6 +41,169 @@ export default function SuunnitelmatJaAineistot({}: Props) { Huomioithan, että suunnitelma-aineistojen tulee täyttää saavutettavuusvaatimukset. + ({ + title: ( + {`${paakategoria.nimi} (${some?.[paakategoria.id].length || 0})`} + ), + content: ( + + + + ), + }))} + />
); } + +interface SuunnitelmaAineistoKategoriaContentProps { + paakategoria: AineistoKategoria; +} + +const SuunnitelmaAineistoKategoriaContent = (props: SuunnitelmaAineistoKategoriaContentProps) => { + const { data: projekti } = useProjektiRoute(); + const [some, setSome] = useState(false); + const { setValue, watch } = useFormContext(); + + const aineistoRoute: `aineistoNahtavilla.${string}` = `aineistoNahtavilla.${props.paakategoria.id}`; + + const aineistot = watch(aineistoRoute); + + return ( + <> +

{kategoriaInfoText[props.paakategoria.id]}

+ {!!projekti?.oid && !!aineistot?.length && ( + + )} + + setSome(false)} + onSubmit={(newAineistot) => { + const value = aineistot || []; + newAineistot.forEach((aineisto) => { + if (!find(value, { dokumenttiOid: aineisto.dokumenttiOid })) { + value.push({ ...aineisto, kategoriaId: props.paakategoria.id, jarjestys: value.length }); + } + }); + setValue(aineistoRoute, value); + }} + /> + + ); +}; + +type FormAineisto = FieldArrayWithId< + VuorovaikutusFormValues, + "suunnitteluVaihe.vuorovaikutus.esittelyaineistot", + "id" +> & + Pick; + +interface AineistoTableProps { + kategoriaId: string; + projektiOid: string; +} + +const AineistoTable = (props: AineistoTableProps) => { + const { control, formState, register, getValues, setValue } = + useFormContext(); + const aineistoRoute: `aineistoNahtavilla.${string}` = `aineistoNahtavilla.${props.kategoriaId}`; + const { fields, remove } = useFieldArray({ name: aineistoRoute, control }); + + const columns = useMemo[]>( + () => [ + { + Header: "Aineisto", + width: 250, + accessor: (aineisto) => { + const index = fields.findIndex((row) => row.dokumenttiOid === aineisto.dokumenttiOid); + const errorpath = props.kategoriaId; + const errorMessage = (formState.errors.aineistoNahtavilla?.[errorpath]?.[index] as any | undefined)?.message; + return ( + <> + + {errorMessage &&

{errorMessage}

} + + + + ); + }, + }, + { + Header: "Tuotu", + accessor: (aineisto) => (aineisto.tuotu ? formatDateTime(aineisto.tuotu) : undefined), + }, + { + Header: "Kategoria", + accessor: (aineisto) => { + const options = aineistoKategoriat + .listKategoriat() + .reduce((accumulatedOptions, paakategoria) => { + accumulatedOptions.push({ label: paakategoria.nimi, value: paakategoria.id }); + return accumulatedOptions; + }, []); + console.log(options); + return ( +