From e228f3270d208a0668cd92f2422031c33867c39c Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Mon, 5 Aug 2024 03:57:51 +0200 Subject: [PATCH] React hook form --- package.json | 5 +- pnpm-lock.yaml | 27 ++++++ .../Kontakt/KontaktContent.component.tsx | 87 +++++++++---------- src/components/UI/InputField.component.tsx | 31 ++++--- 4 files changed, 91 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index a308c669..4bde6f00 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@emailjs/browser": "^4.4.1", + "@hookform/resolvers": "^3.9.0", "@portabletext/react": "^3.1.0", "@sanity/client": "^6.21.1", "@sanity/image-url": "^1.0.2", @@ -28,10 +29,12 @@ "next-sanity": "^9.4.3", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-hook-form": "^7.52.2", "react-icons": "^5.2.1", "react-use": "^17.5.1", "sanity": "^3.52.4", - "sitemap": "^8.0.0" + "sitemap": "^8.0.0", + "zod": "^3.23.8" }, "devDependencies": { "@ladle/react": "^4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4052640..85f1a11f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@emailjs/browser': specifier: ^4.4.1 version: 4.4.1 + '@hookform/resolvers': + specifier: ^3.9.0 + version: 3.9.0(react-hook-form@7.52.2) '@portabletext/react': specifier: ^3.1.0 version: 3.1.0(react@18.3.1) @@ -41,6 +44,9 @@ dependencies: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-hook-form: + specifier: ^7.52.2 + version: 7.52.2(react@18.3.1) react-icons: specifier: ^5.2.1 version: 5.2.1(react@18.3.1) @@ -53,6 +59,9 @@ dependencies: sitemap: specifier: ^8.0.0 version: 8.0.0 + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: '@ladle/react': @@ -2098,6 +2107,14 @@ packages: resolution: {integrity: sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==} dev: false + /@hookform/resolvers@3.9.0(react-hook-form@7.52.2): + resolution: {integrity: sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==} + peerDependencies: + react-hook-form: ^7.0.0 + dependencies: + react-hook-form: 7.52.2(react@18.3.1) + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -5186,6 +5203,7 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 dev: false + bundledDependencies: false /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -10664,6 +10682,15 @@ packages: use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1) dev: false + /react-hook-form@7.52.2(react@18.3.1): + resolution: {integrity: sha512-pqfPEbERnxxiNMPd0bzmt1tuaPcVccywFDpyk2uV5xCIBphHV5T8SVnX9/o3kplPE1zzKt77+YIoq+EMwJp56A==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + dependencies: + react: 18.3.1 + dev: false + /react-hotkeys-hook@4.5.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug==} peerDependencies: diff --git a/src/components/Kontakt/KontaktContent.component.tsx b/src/components/Kontakt/KontaktContent.component.tsx index c00a3a06..1965f8e1 100644 --- a/src/components/Kontakt/KontaktContent.component.tsx +++ b/src/components/Kontakt/KontaktContent.component.tsx @@ -1,58 +1,52 @@ "use client"; -import { useState, useRef } from "react"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; import emailjs from "@emailjs/browser"; import Button from "@/components/UI/Button.component"; import PageHeader from "@/components/UI/PageHeader.component"; import InputField from "@/components/UI/InputField.component"; -interface IEvent { - preventDefault: () => void; -} +const formSchema = z.object({ + navn: z + .string() + .min(1, "Navn er påkrevd") + .regex(/^[a-zA-ZæøåÆØÅ ]+$/, "Vennligst bruk norske bokstaver"), + telefon: z + .string() + .min(8, "Telefonnummer må være minst 8 siffer") + .regex(/^[0-9]+$/, "Vennligst bruk bare tall"), + tekst: z.string().min(1, "Melding er påkrevd"), +}); -/** - * Renders contact form. Uses EmailJS to send the emails. - * @function KontaktContent - * @returns {JSX.Element} - Rendered component - */ +type FormData = z.infer; const KontaktContent = () => { - const formRef = useRef(null); - const [serverResponse, setServerResponse] = useState(""); - const [submitting, setSubmitting] = useState(false); + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(formSchema), + }); - /** - * Handles the form submission and sends an email using the provided API keys. - * - * @param {IEvent} event - The event object representing the form submission event. - * @return {void} No return value. - */ - const handleSubmit = (event: IEvent) => { + const onSubmit = async (data: FormData) => { const EMAIL_API_KEY = process.env.NEXT_PUBLIC_EMAIL_API_KEY ?? "changeme"; const TEMPLATE_KEY = process.env.NEXT_PUBLIC_EMAIL_TEMPLATE_KEY ?? "changeme"; const SERVICE_KEY = process.env.NEXT_PUBLIC_EMAIL_SERVICE_KEY ?? "changeme"; - // Disable button - setSubmitting(true); - - event.preventDefault(); - - if (!formRef.current) { - return; + try { + emailjs.init(EMAIL_API_KEY); + await emailjs.send(SERVICE_KEY, TEMPLATE_KEY, data); + setServerResponse("Takk for din beskjed"); + } catch (error) { + setServerResponse("Feil under sending av skjema"); } - - emailjs.init(EMAIL_API_KEY); - emailjs.sendForm(SERVICE_KEY, TEMPLATE_KEY, formRef.current).then( - () => { - setServerResponse("Takk for din beskjed"); - }, - () => { - setServerResponse("Feil under sending av skjema"); - }, - ); }; return ( @@ -71,8 +65,7 @@ const KontaktContent = () => {
{ inputName="navn" label="Fullt navn" htmlFor="navn" - inputPattern="[a-zA-ZæøåÆØÅ ]+" - title="Vennligst bruk norske bokstaver" - isRequired + register={register} + error={errors.navn} + isRequired={true} />

{ type="textarea" label="Hva ønsker du å si?" htmlFor="tekst" - isRequired + register={register} + error={errors.tekst} + isRequired={true} />
- +
)} diff --git a/src/components/UI/InputField.component.tsx b/src/components/UI/InputField.component.tsx index c6d811b3..a862bd09 100644 --- a/src/components/UI/InputField.component.tsx +++ b/src/components/UI/InputField.component.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { UseFormRegister, FieldError } from "react-hook-form"; interface IInputProps { inputName: string; @@ -8,9 +9,11 @@ interface IInputProps { inputPattern?: string; title?: string; type?: "input" | "textarea"; + register: UseFormRegister; + error?: FieldError | undefined; } -const InputField = ({ +const InputField: React.FC = ({ inputName, label, inputPattern, @@ -18,33 +21,34 @@ const InputField = ({ htmlFor, title, type = "input", + register, + error, ...props -}: IInputProps) => { +}) => { const sharedClasses = "cursor-pointer peer block text-xl w-64 p-2 bg-gray-800 text-slate-200 border-gray-500 border rounded border-opacity-50 outline-none focus:border-slate-200 placeholder-gray-300 placeholder-opacity-0 transition duration-200"; return ( -
+
{type === "input" ? ( ) : ( )} @@ -57,8 +61,11 @@ const InputField = ({ {label}
+ {error && ( +

{error.message}

+ )}
); }; -export default InputField; +export default InputField; \ No newline at end of file