diff --git a/src/components/Accordion/FormAccordionsWithComments.tsx b/src/components/Accordion/FormAccordionsWithComments.tsx index 9c0d48e778..bc08cfabb3 100644 --- a/src/components/Accordion/FormAccordionsWithComments.tsx +++ b/src/components/Accordion/FormAccordionsWithComments.tsx @@ -20,7 +20,7 @@ import OpenAllButton from "./OpenAllButton"; import { ARCHIVED, PUBLISHED, STORED_HIDE_COMMENTS, UNPUBLISHED } from "../../constants"; import CommentSection, { COMMENT_WIDTH, SPACING_COMMENT } from "../../containers/ArticlePage/components/CommentSection"; import { MainContent } from "../../containers/ArticlePage/styles"; -import { RevisionMetaFormType } from "../../containers/FormikForm/AddRevisionDateField"; +import { ArticleFormType } from "../../containers/FormikForm/articleFormHooks"; import { useLocalStorageBooleanState } from "../../containers/WelcomePage/hooks/storedFilterHooks"; import QualityEvaluation from "../QualityEvaluation/QualityEvaluation"; import { useWideArticle } from "../WideArticleEditorProvider"; @@ -77,7 +77,7 @@ const FormControls = styled(MainContent)` const FormAccordionsWithComments = ({ defaultOpen, children, article, taxonomy, updateNotes }: Props) => { const { t } = useTranslation(); const { toggleWideArticles, isWideArticle } = useWideArticle(); - const [revisionMetaField, , revisionMetaHelpers] = useField("revisionMeta"); + const [revisionMetaField, , revisionMetaHelpers] = useField("revisionMeta"); const [openAccordions, setOpenAccordions] = useState(defaultOpen); const [hideComments, setHideComments] = useLocalStorageBooleanState(STORED_HIDE_COMMENTS); diff --git a/src/components/QualityEvaluation/QualityEvaluation.tsx b/src/components/QualityEvaluation/QualityEvaluation.tsx index 34346f13c2..6f649a7e61 100644 --- a/src/components/QualityEvaluation/QualityEvaluation.tsx +++ b/src/components/QualityEvaluation/QualityEvaluation.tsx @@ -16,7 +16,7 @@ import { Node } from "@ndla/types-taxonomy"; import { Text } from "@ndla/typography"; import { gradeItemStyles, qualityEvaluationOptions } from "./QualityEvaluationForm"; import QualityEvaluationModal from "./QualityEvaluationModal"; -import { RevisionMetaFormType } from "../../containers/FormikForm/AddRevisionDateField"; +import { ArticleFormType } from "../../containers/FormikForm/articleFormHooks"; import SmallQualityEvaluationGrade from "../../containers/StructurePage/resourceComponents/QualityEvaluationGrade"; const FlexWrapper = styled.div` @@ -40,8 +40,8 @@ interface Props { article?: IArticle; taxonomy?: Node[]; iconButtonColor?: "light" | "primary"; - revisionMetaField?: FieldInputProps; - revisionMetaHelpers?: FieldHelperProps; + revisionMetaField?: FieldInputProps; + revisionMetaHelpers?: FieldHelperProps; gradeVariant?: "small" | "large"; updateNotes?: (art: IUpdatedArticle) => Promise; } diff --git a/src/components/QualityEvaluation/QualityEvaluationForm.tsx b/src/components/QualityEvaluation/QualityEvaluationForm.tsx index ca25f7fed7..c2d2a31a69 100644 --- a/src/components/QualityEvaluation/QualityEvaluationForm.tsx +++ b/src/components/QualityEvaluation/QualityEvaluationForm.tsx @@ -18,7 +18,7 @@ import { colors, spacing, fonts, misc } from "@ndla/core"; import { FieldErrorMessage, Fieldset, InputV3, Label, Legend, RadioButtonGroup } from "@ndla/forms"; import { IArticle, IUpdatedArticle } from "@ndla/types-backend/draft-api"; import { Grade, Node } from "@ndla/types-taxonomy"; -import { RevisionMetaFormType } from "../../containers/FormikForm/AddRevisionDateField"; +import { ArticleFormType } from "../../containers/FormikForm/articleFormHooks"; import { useTaxonomyVersion } from "../../containers/StructureVersion/TaxonomyVersionProvider"; import { draftQueryKeys } from "../../modules/draft/draftQueries"; import { usePutNodeMutation } from "../../modules/nodes/nodeMutations"; @@ -101,8 +101,8 @@ const StyledFieldWarning = styled(FieldWarning)` interface Props { setOpen: (open: boolean) => void; taxonomy: Node[]; - revisionMetaField?: FieldInputProps; - revisionMetaHelpers?: FieldHelperProps; + revisionMetaField?: FieldInputProps; + revisionMetaHelpers?: FieldHelperProps; updateNotes?: (art: IUpdatedArticle) => Promise; article?: IArticle; } diff --git a/src/components/QualityEvaluation/QualityEvaluationModal.tsx b/src/components/QualityEvaluation/QualityEvaluationModal.tsx index b732e2932e..a84dfe377f 100644 --- a/src/components/QualityEvaluation/QualityEvaluationModal.tsx +++ b/src/components/QualityEvaluation/QualityEvaluationModal.tsx @@ -18,7 +18,7 @@ import { IArticle, IUpdatedArticle } from "@ndla/types-backend/draft-api"; import { Node } from "@ndla/types-taxonomy"; import { Text } from "@ndla/typography"; import QualityEvaluationForm from "./QualityEvaluationForm"; -import { RevisionMetaFormType } from "../../containers/FormikForm/AddRevisionDateField"; +import { ArticleFormType } from "../../containers/FormikForm/articleFormHooks"; const StyledModalBody = styled(ModalBody)` display: flex; @@ -32,8 +32,8 @@ interface Props { article?: IArticle; taxonomy?: Node[]; iconButtonColor?: "light" | "primary"; - revisionMetaField?: FieldInputProps; - revisionMetaHelpers?: FieldHelperProps; + revisionMetaField?: FieldInputProps; + revisionMetaHelpers?: FieldHelperProps; updateNotes?: (art: IUpdatedArticle) => Promise; } diff --git a/src/containers/ArticlePage/components/RevisionNotes.tsx b/src/containers/ArticlePage/components/RevisionNotes.tsx index f376fd9799..c087ad4332 100644 --- a/src/containers/ArticlePage/components/RevisionNotes.tsx +++ b/src/containers/ArticlePage/components/RevisionNotes.tsx @@ -5,41 +5,156 @@ * LICENSE file in the root directory of this source tree. * */ + +import { addYears } from "date-fns"; +import { FastField, FieldArray, FieldProps, useField } from "formik"; import { memo } from "react"; import { useTranslation } from "react-i18next"; -import styled from "@emotion/styled"; -import { colors } from "@ndla/core"; -import FormikField from "../../../components/FormikField/FormikField"; -import AddRevisionDateField from "../../FormikForm/AddRevisionDateField"; +import { DeleteBinLine } from "@ndla/icons/action"; +import { + Button, + FieldErrorMessage, + FieldInput, + FieldLabel, + FieldRoot, + FieldsetHelper, + FieldsetLegend, + FieldsetRoot, + IconButton, + SwitchControl, + SwitchHiddenInput, + SwitchLabel, + SwitchRoot, + SwitchThumb, +} from "@ndla/primitives"; +import { styled } from "@ndla/styled-system/jsx"; +import { Revision } from "../../../constants"; +import { formatDateForBackend } from "../../../util/formatDate"; +import { ArticleFormType } from "../../FormikForm/articleFormHooks"; +import InlineDatePicker from "../../FormikForm/components/InlineDatePicker"; +import { useMessages } from "../../Messages/MessagesProvider"; + +export type RevisionMetaFormType = ArticleFormType["revisionMeta"]; + +const FieldWrapper = styled(FieldsetRoot, { + base: { + display: "flex", + flexDirection: "row", + gap: "xsmall", + width: "100%", + alignItems: "center", + }, +}); -const ErrorField = styled.p` - color: ${colors.support.red}; -`; +const StyledFieldRoot = styled(FieldRoot, { + base: { + flex: "1", + }, +}); const RevisionNotes = () => { const { t } = useTranslation(); + const [_, { value }] = useField("revisionMeta"); + + const { createMessage } = useMessages(); + return ( - - {({ field, form: { errors } }) => { - return ( - <> - - {errors.revisionError} - - ); - }} - + render={(arrayHelpers) => ( + + {t("form.name.revisions")} + {t("form.revisions.description")} + {value.map((_, index) => ( + + {t("form.revisions.revisionNumber", { number: index + 1 })} + + + {({ field, meta }: FieldProps) => ( + + {t("form.name.note")} + {meta.error} + + + )} + + + {({ field, meta, form }: FieldProps) => ( + + {t("form.name.date")} + {meta.error} + { + form.setFieldValue(field.name, evt.target.value, true); + }} + title={t("form.revisions.datePickerTooltip")} + /> + + )} + + + {({ field, meta, form }: FieldProps) => ( + + { + const status = details.checked ? Revision.revised : Revision.needsRevision; + form.setFieldValue(field.name, status); + if (status === Revision.revised) { + createMessage({ + translationKey: "form.revisions.reminder", + severity: "info", + timeToLive: 0, + }); + } + }} + > + {t("form.revisions.switchTooltip")} + + + + + + + )} + + arrayHelpers.remove(index)} + > + + + + + ))} + + + )} + /> ); }; diff --git a/src/containers/FormikForm/AddRevisionDateField.tsx b/src/containers/FormikForm/AddRevisionDateField.tsx deleted file mode 100644 index 0474358551..0000000000 --- a/src/containers/FormikForm/AddRevisionDateField.tsx +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Copyright (c) 2022-present, NDLA. - * - * This source code is licensed under the GPLv3 license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -import addYears from "date-fns/addYears"; -import { FieldInputProps } from "formik"; -import { useTranslation } from "react-i18next"; -import styled from "@emotion/styled"; -import { ButtonV2 } from "@ndla/button"; -import { colors, spacing } from "@ndla/core"; -import { InputV3, FieldErrorMessage } from "@ndla/forms"; -import { SwitchControl, SwitchHiddenInput, SwitchLabel, SwitchRoot, SwitchThumb } from "@ndla/primitives"; -import { ArticleFormType } from "./articleFormHooks"; -import InlineDatePicker from "./components/InlineDatePicker"; -import FieldRemoveButton from "../../components/Field/FieldRemoveButton"; -import { FormControl } from "../../components/FormField"; -import { Revision } from "../../constants"; -import { formatDateForBackend } from "../../util/formatDate"; -import { useMessages } from "../Messages/MessagesProvider"; - -export type RevisionMetaFormType = ArticleFormType["revisionMeta"]; - -interface Props { - formikField: FieldInputProps; - name: string; - placeholder?: string; - onChange: Function; - value: string[]; - showError?: boolean; -} - -const Wrapper = styled.div` - margin-bottom: ${spacing.small}; - align-items: baseline; - display: flex; - > div { - &:first-of-type { - + div { - padding-left: ${spacing.normal}; - } - } - } - &:empty { - margin: 0; - } -`; - -const InputWrapper = styled.div` - flex: 1; -`; - -const StyledDatePickerWrapper = styled.div` - height: ${spacing.large}; -`; - -const StyledRemoveButton = styled(FieldRemoveButton)` - height: 100%; - padding-top: 0; - [data-icon] { - fill: ${colors.support.red}; - } -`; - -const VerticalCenter = styled.div` - display: flex; - align-items: center; - gap: ${spacing.xsmall}; -`; - -const AddRevisionDateField = ({ formikField, showError }: Props) => { - const { t } = useTranslation(); - type RevisionMetaType = (typeof formikField.value)[number]; - const onRevisionChange = (newMetas: RevisionMetaFormType) => { - formikField.onChange({ - target: { - value: newMetas, - name: formikField.name, - }, - }); - }; - const addRevision = () => { - onRevisionChange([ - ...formikField.value, - { - note: "", - revisionDate: formatDateForBackend(addYears(new Date(), 5)), - status: "needs-revision", - new: true, - }, - ]); - }; - - const removeRevision = (idx: number) => { - const withoutIdx = formikField.value.filter((_, index) => index !== idx); - onRevisionChange(withoutIdx); - }; - - const { createMessage } = useMessages(); - - return ( - <> - {formikField.value.map((revisionMeta, index) => { - const editRevision = (editFunction: (old: RevisionMetaType) => RevisionMetaType) => { - const newRevisions = [...formikField.value]; - newRevisions[index] = editFunction(newRevisions[index]); - onRevisionChange(newRevisions); - }; - return ( -
- - - - { - editRevision((old) => ({ - ...old, - note: e.currentTarget.value, - })); - }} - /> - {t("validation.noEmptyRevision")} - - - - - - editRevision((old) => ({ - ...old, - revisionDate: date.currentTarget.value, - })) - } - title={t("form.revisions.datePickerTooltip")} - /> - - { - const status = details.checked ? Revision.revised : Revision.needsRevision; - editRevision((old) => ({ ...old, status })); - if (status === Revision.revised) { - createMessage({ - translationKey: "form.revisions.reminder", - severity: "info", - timeToLive: 0, - }); - } - }} - > - {t("form.revisions.switchTooltip")} - - - - - - removeRevision(index)} - title={t("form.revisions.deleteTooltip")} - /> - - -
- ); - })} - - {t("form.revisions.add")} - - - ); -}; - -export default AddRevisionDateField; diff --git a/src/phrases/phrases-en.ts b/src/phrases/phrases-en.ts index df49e3aabe..74f0286b09 100644 --- a/src/phrases/phrases-en.ts +++ b/src/phrases/phrases-en.ts @@ -1536,6 +1536,7 @@ const phrases = { revisions: { add: "New revision", remove: "Remove revision", + revisionNumber: "Revision {{number}}", description: "Revisions requires a description and an expiration date for the article. The switch marks whether a revision is performed or not. Remember that a revised article must be republished.", datePickerTooltip: "The date the article expires if the revision is not marked as revised.", diff --git a/src/phrases/phrases-nb.ts b/src/phrases/phrases-nb.ts index 7a69f8ad12..5e9ff843f0 100644 --- a/src/phrases/phrases-nb.ts +++ b/src/phrases/phrases-nb.ts @@ -1534,6 +1534,7 @@ const phrases = { revisions: { add: "Ny revisjon", remove: "Fjern revisjon", + revisionNumber: "Revisjon {{number}}", description: "Revisjoner krever en beskrivelse og en dato artikkelen utløper på. Bryteren markerer hvorvidt en revisjon er utført eller ikke. Husk at en revidert artikkel må republiseres.", datePickerTooltip: "Dato artikkelen utløper dersom revisjonen ikke blir markert som revidert.", diff --git a/src/phrases/phrases-nn.ts b/src/phrases/phrases-nn.ts index 2b15b5da82..a88b1d0e3e 100644 --- a/src/phrases/phrases-nn.ts +++ b/src/phrases/phrases-nn.ts @@ -1534,6 +1534,7 @@ const phrases = { revisions: { add: "Ny revisjon", remove: "Fjern revisjon", + revisionNumber: "Revisjon {{number}}", description: "Revisjonar krev ei skildring og ein dato artikkelen går ut på. Bryteren markerar i kva grad ein revisjon er utført eller ikkje. Hugs at ein revidert artikkel må republiserast.", datePickerTooltip: "Dato artikkelen går ut dersom revisjonen ikkje blir markert som revidert.",