diff --git a/src/app/admin/surveys/[surveyId]/responses/test/_components/DeleteButton.tsx b/src/app/admin/surveys/[surveyId]/responses/test/_components/DeleteButton.tsx index 22186f74..fc2ba7c5 100644 --- a/src/app/admin/surveys/[surveyId]/responses/test/_components/DeleteButton.tsx +++ b/src/app/admin/surveys/[surveyId]/responses/test/_components/DeleteButton.tsx @@ -3,7 +3,7 @@ import { blueButtonStyles } from "@/src/core/components/links" import { AllowedSurveySlugs } from "@/src/survey-public/utils/allowedSurveySlugs" import deleteTestSurveyResponses from "@/src/survey-responses/mutations/deleteTestSurveyResponses" import { useMutation } from "@blitzjs/rpc" -import clsx from "clsx" +import { clsx } from "clsx" import { useRouter } from "next/navigation" type DeleteButtonProps = { diff --git a/src/core/components/forms/LabeledCheckbox.tsx b/src/core/components/forms/LabeledCheckbox.tsx index 2e389232..ba67946d 100644 --- a/src/core/components/forms/LabeledCheckbox.tsx +++ b/src/core/components/forms/LabeledCheckbox.tsx @@ -41,6 +41,7 @@ export const LabeledCheckbox = forwardRef
void } -export const Email: React.FC = ({ email, onClickMore }) => { +export const Email = ({ email, onClickMore }: Props) => { const { description, button, title, mailjetWidgetUrl, homeUrl } = email return ( diff --git a/src/survey-public/components/More.tsx b/src/survey-public/components/More.tsx index 1d7f7f5d..662e77fa 100644 --- a/src/survey-public/components/More.tsx +++ b/src/survey-public/components/More.tsx @@ -10,7 +10,7 @@ type Props = { more: TMore } -export const More: React.FC = ({ more, onClickMore, onClickFinish }) => { +export const More = ({ more, onClickMore, onClickFinish }: Props) => { useAlertBeforeUnload() const { title, description, questionText, buttons } = more diff --git a/src/survey-public/components/Page.tsx b/src/survey-public/components/Page.tsx index 92312b34..9dd89601 100644 --- a/src/survey-public/components/Page.tsx +++ b/src/survey-public/components/Page.tsx @@ -1,20 +1,27 @@ import { SurveyScreenHeader } from "@/src/survey-public/components/core/layout/SurveyScreenHeader" import type { TPage } from "@/src/survey-public/components/types" +import { useFormContext } from "react-hook-form" +import { getFormfieldNamesByQuestions } from "../utils/getFormfieldNames" import { Question } from "./Question" -import { SurveyP } from "./core/Text" import { SurveyButtonWithAction } from "./core/buttons/SurveyButtonWithAction" import { SurveyButtonWrapper } from "./core/buttons/SurveyButtonWrapper" +import { SurveyFormErrorsBox } from "./core/form/SurveyFormErrorsBox" type Props = { page: TPage buttonActions: any - completed: boolean } -export const Page = ({ page, buttonActions, completed }: Props) => { +export const Page = ({ page, buttonActions }: Props) => { + const { + formState: { errors, isSubmitting }, + } = useFormContext() + if (!page) return null const { id: pageId, title, description, questions, buttons } = page + const relevantQuestionNames = getFormfieldNamesByQuestions(questions!) + return (
@@ -23,26 +30,20 @@ export const Page = ({ page, buttonActions, completed }: Props) => { questions.map((question) => ( ))} + {buttons?.map((button) => { - let disabled = false - if (["nextPage", "submit"].includes(button.onClick.action)) { - disabled = !completed - } return ( ) })} - - * Pflichtfelder
- Um fortzufahren, bitte alle Pflichtfelder ausfüllen. -
) } diff --git a/src/survey-public/components/Question.tsx b/src/survey-public/components/Question.tsx index 397c818e..824d3cd2 100644 --- a/src/survey-public/components/Question.tsx +++ b/src/survey-public/components/Question.tsx @@ -1,9 +1,12 @@ import { TFeedbackQuestion, TQuestion, + TReadOnlyProps, TSingleOrMultiResponseProps, - TTextProps, + TTextareaProps, + TTextfieldProps, } from "@/src/survey-public/components/types" +import { getFormfieldName } from "../utils/getFormfieldNames" import { SurveyH2 } from "./core/Text" import { SurveyLabeledCheckboxGroup } from "./core/form/SurveyLabeledCheckboxGroup" import { SurveyLabeledRadiobuttonGroup } from "./core/form/SurveyLabeledRadiobuttonGroup" @@ -13,15 +16,17 @@ import { SurveyLabeledTextareaField } from "./core/form/SurveyLabeledTextareaFie type TSingleOrMultuResponseComponentProps = { id: number + component: TQuestion["component"] | TFeedbackQuestion["component"] } & TSingleOrMultiResponseProps -const SingleResponseComponent: React.FC = ({ +const SingleResponseComponent = ({ id, responses, -}) => ( + component, +}: TSingleOrMultuResponseComponentProps) => ( ({ - scope: `single-${id}`, + scope: getFormfieldName(component, id), name: `${id}-${item.id}`, label: item.text.de, help: item?.help?.de, @@ -30,58 +35,79 @@ const SingleResponseComponent: React.FC = /> ) -const MultipleResponseComponent: React.FC = ({ +const MultipleResponseComponent = ({ id, responses, -}) => ( + component, +}: TSingleOrMultuResponseComponentProps) => ( ({ - name: `multi-${id}-${item.id}`, + name: `${getFormfieldName(component, id)}-${item.id}`, label: item.text.de, help: item?.help?.de, }))} /> ) -type TTextResponseComponentProps = { +type TTextfieldResponseComponentProps = { id: number -} & TTextProps + component: TQuestion["component"] | TFeedbackQuestion["component"] +} & TTextfieldProps + +type TTextareaResponseComponentProps = { + id: number + component: TQuestion["component"] | TFeedbackQuestion["component"] +} & TTextareaProps + type TReadOnlyResponseComponentProps = { id: number - queryId: string -} & TTextProps + component: TQuestion["component"] | TFeedbackQuestion["component"] +} & TReadOnlyProps -const TextResponseComponent: React.FC = ({ +const TextResponseComponent = ({ id, placeholder, caption, - maxLength, -}) => ( + validation, + component, +}: TTextareaResponseComponentProps) => ( <> ) -const TextFieldResponseComponent: React.FC = ({ id, placeholder }) => ( +const TextFieldResponseComponent = ({ + id, + placeholder, + component, +}: TTextfieldResponseComponentProps) => ( <> - + ) -const ReadOnlyResponseComponent: React.FC = ({ id, queryId }) => ( +const ReadOnlyResponseComponent = ({ id, queryId, component }: TReadOnlyResponseComponentProps) => ( <> - + ) -// TODO type const CustomComponent = (props: any) => (
@@ -101,16 +127,22 @@ const components = { type Props = { question: TQuestion | TFeedbackQuestion; className?: string } -export const Question: React.FC = ({ question, className }) => { +export const Question = ({ question, className }: Props) => { const { id, help, label, component, props } = question // @ts-expect-error const Component = components[component] || null + + // todo validation: atm multipleResponse is always optional - we have to change this in the future + // @ts-expect-error + const isOptional = component === "multipleResponse" || props?.validation?.optional return (
- {label.de} * + + {label.de} {isOptional && " (optional)"} + {help &&
{help.de}
} {/* @ts-ignore */} - {Component && } + {Component && }
) } diff --git a/src/survey-public/components/Start.tsx b/src/survey-public/components/Start.tsx index 93af5204..7df140cf 100644 --- a/src/survey-public/components/Start.tsx +++ b/src/survey-public/components/Start.tsx @@ -4,7 +4,7 @@ import { SurveyButtonWrapper } from "./core/buttons/SurveyButtonWrapper" type Props = { onStartClick: () => void; startContent: React.ReactNode; disabled: boolean } -export const Start: React.FC = ({ onStartClick, startContent, disabled }) => { +export const Start = ({ onStartClick, startContent, disabled }: Props) => { useAlertBeforeUnload() return ( diff --git a/src/survey-public/components/Survey.tsx b/src/survey-public/components/Survey.tsx index d26169d0..728a97d0 100644 --- a/src/survey-public/components/Survey.tsx +++ b/src/survey-public/components/Survey.tsx @@ -10,7 +10,6 @@ import { useAlertBeforeUnload } from "../utils/useAlertBeforeUnload" type Props = { survey: TSurvey - isPageCompleted: boolean setStage: (value: SetStateAction<"SURVEY" | "MORE" | "FEEDBACK" | "EMAIL" | "START">) => void surveyPageProgressProps: { surveyPageProgress: number @@ -18,18 +17,19 @@ type Props = { } } -export const Survey: React.FC = ({ +export const Survey = ({ survey, setStage, - isPageCompleted, surveyPageProgressProps: { surveyPageProgress, setSurveyPageProgress }, -}) => { +}: Props) => { const { setProgress } = useContext(ProgressContext) useAlertBeforeUnload() - // for debugging - const { getValues } = useFormContext() + const { + // for debugging + getValues, + } = useFormContext() const responsesForDebugging = getValues() const handleNextPage = () => { @@ -66,7 +66,7 @@ export const Survey: React.FC = ({
{JSON.stringify(responsesForDebugging, null, 2)}
- {page && } + {page && } ) } diff --git a/src/survey-public/components/SurveyInactivePage.tsx b/src/survey-public/components/SurveyInactivePage.tsx index 2bcc5a9e..7f0a52f1 100644 --- a/src/survey-public/components/SurveyInactivePage.tsx +++ b/src/survey-public/components/SurveyInactivePage.tsx @@ -1,5 +1,4 @@ import { SurveyLayout } from "@/src/survey-public/components/core/layout/SurveyLayout" -import { BlitzPage } from "@blitzjs/next" import { useEffect } from "react" import { SurveyScreenHeader } from "./core/layout/SurveyScreenHeader" import { SurveyLink } from "./core/links/SurveyLink" @@ -7,7 +6,7 @@ import { TSurvey } from "./types" type Props = { surveyDefinition: TSurvey } -const SurveyInactivePage: BlitzPage = ({ surveyDefinition }) => { +const SurveyInactivePage = ({ surveyDefinition }: Props) => { useEffect(() => { const root = document.documentElement root.style.setProperty("--survey-primary-color", surveyDefinition.primaryColor) diff --git a/src/survey-public/components/SurveyMainPage.tsx b/src/survey-public/components/SurveyMainPage.tsx index 908b5e63..4a927a43 100644 --- a/src/survey-public/components/SurveyMainPage.tsx +++ b/src/survey-public/components/SurveyMainPage.tsx @@ -13,9 +13,11 @@ import surveyFeedbackEmail from "@/src/survey-responses/mutations/surveyFeedback import createSurveySession from "@/src/survey-sessions/mutations/createSurveySession" import { useParam } from "@blitzjs/next" import { useMutation } from "@blitzjs/rpc" -import { useCallback, useEffect, useState } from "react" -import { getCompletedQuestionIds } from "../utils/getCompletedQuestionIds" +import { useEffect, useState } from "react" +import { createSurveySchema } from "../utils/createSurveySchema" import { getBackendConfigBySurveySlug } from "../utils/getConfigBySurveySlug" +import { getFormfieldName } from "../utils/getFormfieldNames" +import { getQuestionsAsArray } from "../utils/getQuestionsAsArray" import PublicSurveyForm from "./core/form/PublicSurveyForm" import { TEmail, @@ -42,7 +44,7 @@ type Props = { institutionsBboxes?: TInstitutionsBboxes } -export const SurveyMainPage: React.FC = ({ +export const SurveyMainPage = ({ startContent, isStartDisabled = false, emailDefinition, @@ -53,7 +55,7 @@ export const SurveyMainPage: React.FC = ({ responseConfig, surveyId, institutionsBboxes, -}) => { +}: Props) => { const [stage, setStage] = useState<"START" | "SURVEY" | "MORE" | "FEEDBACK" | "EMAIL">( process.env.NEXT_PUBLIC_PUBLIC_SURVEY_START_STAGE || "START", ) @@ -68,11 +70,28 @@ export const SurveyMainPage: React.FC = ({ const [createSurveyResponseMutation] = useMutation(createSurveyResponse) const [surveyFeedbackEmailMutation] = useMutation(surveyFeedbackEmail) const [surveyPageProgress, setSurveyPageProgress] = useState(0) - const [isSurveyPageCompleted, setIsSurveyPageCompleted] = useState(false) - const [isFirstPageCompleted, setIsFirstPageCompleted] = useState(false) - const [isSecondPageCompleted, setIsSecondPageCompleted] = useState(false) const [isMapDirty, setIsMapDirty] = useState(false) + const surveyFormSchema = createSurveySchema( + getQuestionsAsArray({ definition: surveyDefinition, surveyPart: "survey" }), + ) + + const { evaluationRefs } = responseConfig + const feedbackCategoryId = evaluationRefs["feedback-category"] + const feedbackLocationId = evaluationRefs["feedback-location"] + const feedbackFirstPageQuestions = feedbackDefinition.pages.find((p) => p.id === 1)!.questions + let feedbackSecondPageQuestions = feedbackDefinition.pages + .find((p) => p.id === 2)! + .questions.filter((q) => q.id !== feedbackLocationId) + const feedbackQuestions = [ + feedbackFirstPageQuestions.find((q) => q.id === feedbackCategoryId)!, + // todo clean up or refactor after survey BB + // for BB we have a the map for line selection on the first page - so we manually add it here for validation + feedbackFirstPageQuestions.find((q) => q.id === 21)!, + ...feedbackSecondPageQuestions, + ] + const feedbackFormSchema = createSurveySchema(feedbackQuestions) + useEffect(() => { const root = document.documentElement root.style.setProperty("--survey-primary-color", surveyDefinition.primaryColor) @@ -90,9 +109,6 @@ export const SurveyMainPage: React.FC = ({ } } - const feedbackFirstPageQuestionIds = feedbackDefinition.pages[0]?.questions.map((q) => q.id) - const secondPageQuestionIds = feedbackDefinition.pages[1]?.questions.map((q) => q.id) - const isUserLocationQuestionId = responseConfig.evaluationRefs["is-feedback-location"] const userLocationQuestionId = responseConfig.evaluationRefs["feedback-location"] @@ -135,8 +151,9 @@ export const SurveyMainPage: React.FC = ({ } const handleFeedbackSubmit = async (values: Record, submitterId?: string) => { - if (values[`single-${isUserLocationQuestionId}`] === "2") - values[`map-${userLocationQuestionId}`] = "" // if "no location" is chosen, set location value to empty string + // if "no location" ("2") is chosen, set location value to empty string + if (values[getFormfieldName("singleResponse", isUserLocationQuestionId)] === "2") + values[getFormfieldName("map", userLocationQuestionId)] = "" values = transformValues(values) delete values[isUserLocationQuestionId!] // delete map ja/nein response // await handleSubmitFeedback({ ...values }, submitterId) @@ -180,50 +197,6 @@ export const SurveyMainPage: React.FC = ({ scrollToTopWithDelay() } - const handleSurveyChange = useCallback( - (values: Record) => { - const { pages } = surveyDefinition - const questions = pages[surveyPageProgress]!.questions - const pageQuestionIds = questions?.map((q) => q.id) - - const completedQuestionIds = getCompletedQuestionIds(values) - - if (!questions || !questions.length) { - setIsSurveyPageCompleted(true) - } else { - // check if all questions from page have been answered; compare arrays - setIsSurveyPageCompleted( - pageQuestionIds!.every((val) => completedQuestionIds.includes(val)), - ) - } - }, - [surveyDefinition, surveyPageProgress], - ) - - const handleFeedbackChange = useCallback( - (values: Record) => { - const completedQuestionIds = getCompletedQuestionIds(values) - // check if all questions from page 1 and 2 have been answered; compare arrays - setIsFirstPageCompleted( - feedbackFirstPageQuestionIds!.every((val) => completedQuestionIds.includes(val)), - ) - setIsSecondPageCompleted( - values[`single-${isUserLocationQuestionId}`] === "1" - ? secondPageQuestionIds!.every((val) => completedQuestionIds.includes(val)) && isMapDirty - : secondPageQuestionIds! - .filter((id) => id !== userLocationQuestionId) - .every((val) => completedQuestionIds.includes(val)), - ) - }, - [ - isMapDirty, - isUserLocationQuestionId, - feedbackFirstPageQuestionIds, - secondPageQuestionIds, - userLocationQuestionId, - ], - ) - const handleEmailToFeedback = () => { setFeedbackKey(feedbackKey + 1) setStage("FEEDBACK") @@ -249,7 +222,6 @@ export const SurveyMainPage: React.FC = ({ ) @@ -272,8 +244,6 @@ export const SurveyMainPage: React.FC = ({ feedback={feedbackDefinition} responseConfig={responseConfig} onBackClick={handleFeedbackToMore} - isFirstPageCompletedProps={{ isFirstPageCompleted, setIsFirstPageCompleted }} - isSecondPageCompletedProps={{ isSecondPageCompleted, setIsSecondPageCompleted }} setIsMapDirty={setIsMapDirty} /> ) @@ -315,14 +285,14 @@ export const SurveyMainPage: React.FC = ({ - stage: {stage}{" "} + stage: {stage}
{stage === "START" || stage === "SURVEY" ? ( {component} @@ -331,8 +301,8 @@ export const SurveyMainPage: React.FC = ({ {component} diff --git a/src/survey-public/components/core/Debug.tsx b/src/survey-public/components/core/Debug.tsx index e8a12389..ecf4a40b 100644 --- a/src/survey-public/components/core/Debug.tsx +++ b/src/survey-public/components/core/Debug.tsx @@ -4,7 +4,7 @@ type Props = { children: React.ReactNode } & React.ButtonHTMLAttributes -export const Debug: React.FC = ({ children, ...props }) => { +export const Debug = ({ children, ...props }: Props) => { if (isDev) return
{children}
return null } diff --git a/src/survey-public/components/core/SurveyMarkdown.tsx b/src/survey-public/components/core/SurveyMarkdown.tsx index dc1bb8b2..48d20e15 100644 --- a/src/survey-public/components/core/SurveyMarkdown.tsx +++ b/src/survey-public/components/core/SurveyMarkdown.tsx @@ -37,7 +37,7 @@ const proseClassesSurvey = clsx( "prose-h2:mb-4 prose-h2:mt-8 prose-h2:text-lg prose-h2:font-bold prose-h2:text-gray-900 prose-ol:text-base prose-h2:sm:text-xl prose-ol:sm:text-lg", ) -export const SurveyMarkdown: React.FC = ({ markdown, className }) => { +export const SurveyMarkdown = ({ markdown, className }: Props) => { if (!markdown) return null return ( diff --git a/src/survey-public/components/core/SurveyMetaTags.tsx b/src/survey-public/components/core/SurveyMetaTags.tsx index 03ef6d04..14e360dd 100644 --- a/src/survey-public/components/core/SurveyMetaTags.tsx +++ b/src/survey-public/components/core/SurveyMetaTags.tsx @@ -5,7 +5,7 @@ type Props = { canonicalUrl?: string } -export const SurveyMetaTags: React.FC = ({ title, canonicalUrl }) => { +export const SurveyMetaTags = ({ title, canonicalUrl }: Props) => { return ( {title} diff --git a/src/survey-public/components/core/buttons/SurveyButton.tsx b/src/survey-public/components/core/buttons/SurveyButton.tsx index 22268b88..b22ffd24 100644 --- a/src/survey-public/components/core/buttons/SurveyButton.tsx +++ b/src/survey-public/components/core/buttons/SurveyButton.tsx @@ -4,11 +4,10 @@ import { surveyPrimaryColorButtonStyles, surveyWhiteButtonStyles } from "../link type Props = { color?: string - disabled?: boolean children: string | ReactNode } & React.ButtonHTMLAttributes -export const SurveyButton: React.FC = ({ disabled, color, children, ...props }) => { +export const SurveyButton = ({ color, children, ...props }: Props) => { let colorClass: string switch (color) { case "white": @@ -22,7 +21,7 @@ export const SurveyButton: React.FC = ({ disabled, color, children, ...pr } return ( - ) diff --git a/src/survey-public/components/core/buttons/SurveyButtonWithAction.tsx b/src/survey-public/components/core/buttons/SurveyButtonWithAction.tsx index 92b27105..a9c99320 100644 --- a/src/survey-public/components/core/buttons/SurveyButtonWithAction.tsx +++ b/src/survey-public/components/core/buttons/SurveyButtonWithAction.tsx @@ -1,35 +1,53 @@ import { TButtonWithAction } from "@/src/survey-public/components/types" +import { useFormContext } from "react-hook-form" import { SurveyButton } from "./SurveyButton" type Props = { button: TButtonWithAction buttonActions: { next: () => void; back: () => void } - disabled?: boolean -} + relevantQuestionNames: string[] +} & React.ButtonHTMLAttributes -export const SurveyButtonWithAction: React.FC = ({ disabled, button, buttonActions }) => { +export const SurveyButtonWithAction = ({ + button, + relevantQuestionNames, + buttonActions, + ...props +}: Props) => { + const { trigger, clearErrors } = useFormContext() const { label, color, onClick } = button - if (onClick.action === "submit") + const handleNextClick = async () => { + const isValid = await trigger(relevantQuestionNames) + if (isValid) { + buttonActions.next() + } + } + const handleBackClick = async () => { + clearErrors() + buttonActions.back() + } + + if (onClick.action === "previousPage") { return ( - + {label.de} ) + } - let buttonActionSelect: any - switch (onClick.action) { - case "nextPage": - buttonActionSelect = buttonActions.next - break - case "previousPage": - buttonActionSelect = buttonActions.back - break + if (onClick.action === "nextPage") { + return ( + + {label.de} + + ) } - return ( - - {label.de} - - ) + if (onClick.action === "submit") + return ( + + {label.de} + + ) } diff --git a/src/survey-public/components/core/buttons/SurveyButtonWrapper.tsx b/src/survey-public/components/core/buttons/SurveyButtonWrapper.tsx index 9be1e339..94eea21b 100644 --- a/src/survey-public/components/core/buttons/SurveyButtonWrapper.tsx +++ b/src/survey-public/components/core/buttons/SurveyButtonWrapper.tsx @@ -4,7 +4,7 @@ type Props = { children: ReactNode } -export const SurveyButtonWrapper: React.FC = ({ children }) => { +export const SurveyButtonWrapper = ({ children }: Props) => { return (
{children} diff --git a/src/survey-public/components/core/form/PublicSurveyForm.tsx b/src/survey-public/components/core/form/PublicSurveyForm.tsx index 38bc2349..3f0a36c6 100644 --- a/src/survey-public/components/core/form/PublicSurveyForm.tsx +++ b/src/survey-public/components/core/form/PublicSurveyForm.tsx @@ -28,6 +28,8 @@ export function PublicSurveyForm>({ mode: "onBlur", resolver: schema ? zodResolver(schema) : undefined, defaultValues: initialValues, + reValidateMode: "onChange", + criteriaMode: "all", }) useEffect(() => { if (onChangeValues) { diff --git a/src/survey-public/components/core/form/SurveyFormErrorsBox.tsx b/src/survey-public/components/core/form/SurveyFormErrorsBox.tsx new file mode 100644 index 00000000..90017750 --- /dev/null +++ b/src/survey-public/components/core/form/SurveyFormErrorsBox.tsx @@ -0,0 +1,59 @@ +import { + getFeedbackDefinitionBySurveySlug, + getSurveyDefinitionBySurveySlug, +} from "@/src/survey-public/utils/getConfigBySurveySlug" +import { getQuestionsAsArray } from "@/src/survey-public/utils/getQuestionsAsArray" +import { useParam } from "@blitzjs/next" +import { XCircleIcon } from "@heroicons/react/20/solid" +import { FieldErrors, FieldValues } from "react-hook-form" + +type Props = { + formErrors: FieldErrors + customErrors?: { [key: string]: { message: string } } + surveyPart: "survey" | "feedback" +} + +export const SurveyFormErrorsBox = ({ formErrors, surveyPart, customErrors }: Props) => { + const surveySlug = useParam("surveySlug", "string") + if (!Object.keys(formErrors).length && !customErrors) return null + + const definition = + surveyPart === "survey" + ? // @ts-expect-error + getSurveyDefinitionBySurveySlug(surveySlug!) + : // @ts-expect-error + getFeedbackDefinitionBySurveySlug(surveySlug!) + + const questions = getQuestionsAsArray({ definition, surveyPart }) + + return ( +
+ +
    + {customErrors && + Object.entries(customErrors)?.map(([key, error]) => { + const questionText = questions.find( + (question) => question?.id == Number(key.split("-")[1]), + )?.label.de + return ( +
  • + {questionText}: {error.message} +
  • + ) + })} + {Object.entries(formErrors)?.map(([key, error]) => { + const questionText = questions.find( + (question) => question?.id == Number(key.split("-")[1]), + )?.label.de + return ( + // @ts-expect-error +
  • + {/* @ts-expect-error */} + {questionText}: {error.message} +
  • + ) + })} +
+
+ ) +} diff --git a/src/survey-public/components/core/form/SurveyLabeledCheckbox.tsx b/src/survey-public/components/core/form/SurveyLabeledCheckbox.tsx index 810106cf..31fc2f5c 100644 --- a/src/survey-public/components/core/form/SurveyLabeledCheckbox.tsx +++ b/src/survey-public/components/core/form/SurveyLabeledCheckbox.tsx @@ -36,7 +36,7 @@ export const SurveyLabeledCheckbox = forwardRef @@ -53,7 +53,7 @@ export const SurveyLabeledCheckbox = forwardRef{help}
} ( -

+

{message}

)} diff --git a/src/survey-public/components/core/form/SurveyLabeledCheckboxGroup.tsx b/src/survey-public/components/core/form/SurveyLabeledCheckboxGroup.tsx index c46d9cd7..c3de4893 100644 --- a/src/survey-public/components/core/form/SurveyLabeledCheckboxGroup.tsx +++ b/src/survey-public/components/core/form/SurveyLabeledCheckboxGroup.tsx @@ -6,7 +6,7 @@ type Props = { className?: string } -export const SurveyLabeledCheckboxGroup: React.FC = ({ items, className }) => { +export const SurveyLabeledCheckboxGroup = ({ items, className }: Props) => { return (
{items.map((item, index) => { diff --git a/src/survey-public/components/core/form/SurveyLabeledRadiobutton.tsx b/src/survey-public/components/core/form/SurveyLabeledRadiobutton.tsx index 72fc64d2..fd0130c7 100644 --- a/src/survey-public/components/core/form/SurveyLabeledRadiobutton.tsx +++ b/src/survey-public/components/core/form/SurveyLabeledRadiobutton.tsx @@ -35,6 +35,7 @@ export const SurveyLabeledRadiobutton = forwardRef
@@ -58,7 +59,7 @@ export const SurveyLabeledRadiobutton = forwardRef{help}
} ( -

+

{message}

)} diff --git a/src/survey-public/components/core/form/SurveyLabeledRadiobuttonGroup.tsx b/src/survey-public/components/core/form/SurveyLabeledRadiobuttonGroup.tsx index 9f57155d..3a7bb7da 100644 --- a/src/survey-public/components/core/form/SurveyLabeledRadiobuttonGroup.tsx +++ b/src/survey-public/components/core/form/SurveyLabeledRadiobuttonGroup.tsx @@ -1,3 +1,5 @@ +import { clsx } from "clsx" +import { useFormContext } from "react-hook-form" import { SurveyLabeledRadiobutton, SurveyLabeledRadiobuttonProps } from "./SurveyLabeledRadiobutton" type Props = { @@ -5,9 +7,14 @@ type Props = { className?: string } -export const SurveyLabeledRadiobuttonGroup: React.FC = ({ items, className }) => { +export const SurveyLabeledRadiobuttonGroup = ({ items, className }: Props) => { + const { + formState: { errors }, + } = useFormContext() + // @ts-expect-error + const groupHasError = Boolean(errors[items[0].scope]) return ( -
+
{items.map((item) => { return })} diff --git a/src/survey-public/components/core/form/SurveyLabeledTextField.tsx b/src/survey-public/components/core/form/SurveyLabeledTextField.tsx index e16d55a3..7ed9138e 100644 --- a/src/survey-public/components/core/form/SurveyLabeledTextField.tsx +++ b/src/survey-public/components/core/form/SurveyLabeledTextField.tsx @@ -63,7 +63,7 @@ export const SurveyLabeledTextField = forwardRef @@ -71,7 +71,7 @@ export const SurveyLabeledTextField = forwardRef ( -
+
{message}
)} diff --git a/src/survey-public/components/core/form/SurveyLabeledTextareaField.tsx b/src/survey-public/components/core/form/SurveyLabeledTextareaField.tsx index 75789bf3..beba3eed 100644 --- a/src/survey-public/components/core/form/SurveyLabeledTextareaField.tsx +++ b/src/survey-public/components/core/form/SurveyLabeledTextareaField.tsx @@ -59,7 +59,7 @@ export const SurveyLabeledTextareaField = forwardRef< textareaClasName, "mt-1 block h-52 w-full rounded-md shadow-sm sm:text-sm", hasError - ? "border-red-800 shadow-red-200 focus:border-red-800 focus:ring-red-800" + ? "border-red-500 shadow-red-200 focus:border-red-500 focus:ring-red-500" : "border-gray-300 focus:border-[var(--survey-primary-color)] focus:ring-[var(--survey-dark-color)]", )} /> @@ -67,7 +67,7 @@ export const SurveyLabeledTextareaField = forwardRef< ( -
+
{message}
)} diff --git a/src/survey-public/components/core/layout/SurveyFooterLinkList.tsx b/src/survey-public/components/core/layout/SurveyFooterLinkList.tsx index 8e0a13c2..66060409 100644 --- a/src/survey-public/components/core/layout/SurveyFooterLinkList.tsx +++ b/src/survey-public/components/core/layout/SurveyFooterLinkList.tsx @@ -13,7 +13,7 @@ type Props = { className?: string } -export const SurveyFooterLinkList: React.FC = ({ linkList, className }) => { +export const SurveyFooterLinkList = ({ linkList, className }: Props) => { return (

RECHTLICHES

diff --git a/src/survey-public/components/core/layout/SurveyHeader.tsx b/src/survey-public/components/core/layout/SurveyHeader.tsx index b8c916e1..42b45dbb 100644 --- a/src/survey-public/components/core/layout/SurveyHeader.tsx +++ b/src/survey-public/components/core/layout/SurveyHeader.tsx @@ -8,7 +8,7 @@ type Props = { landingPageUrl: string } -export const SurveyHeader: React.FC = ({ logoSrc, landingPageUrl }) => { +export const SurveyHeader = ({ logoSrc, landingPageUrl }: Props) => { return (