From 3dfbde0ef9416ca9870d42d8c35833d26192b43d Mon Sep 17 00:00:00 2001 From: Timothy Armes Date: Mon, 21 Oct 2024 15:16:38 +0200 Subject: [PATCH] Add information about first notification email --- .../(main)/add-tournament/TournamentForm.tsx | 27 +++-- src/app/[locale]/(main)/clubs/ClubTable.tsx | 2 +- .../(main)/contact-us/ContactForm.tsx | 25 +++-- .../[locale]/(main)/delete-account/page.tsx | 10 +- .../(main)/zones/components/ZoneForm.tsx | 25 ++++- src/app/[locale]/(main)/zones/create/page.tsx | 29 +++-- .../[locale]/(main)/zones/edit/[id]/page.tsx | 22 ++-- src/app/[locale]/(main)/zones/page.tsx | 23 ++-- src/components/InfoMessage.tsx | 100 +++++++++++++----- src/components/SignInForm.tsx | 33 +++--- src/messages/en.json | 2 +- src/messages/fr.json | 1 + 12 files changed, 196 insertions(+), 103 deletions(-) diff --git a/src/app/[locale]/(main)/add-tournament/TournamentForm.tsx b/src/app/[locale]/(main)/add-tournament/TournamentForm.tsx index d14a869..b684f62 100644 --- a/src/app/[locale]/(main)/add-tournament/TournamentForm.tsx +++ b/src/app/[locale]/(main)/add-tournament/TournamentForm.tsx @@ -9,7 +9,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { z } from "zod"; import { Button } from "@/components/Button"; -import { clearMessage } from "@/components/InfoMessage"; +import { InfoMessageType } from "@/components/InfoMessage"; import InfoMessage from "@/components/InfoMessage"; import LoadingMap from "@/components/LoadingMap"; import { DateField } from "@/components/form/DateField"; @@ -32,10 +32,10 @@ const TournamentForm = () => { const t = useTranslations("AddTournament"); const at = useTranslations("App"); - const [responseMessage, setResponseMessage] = useState({ - isSuccessful: false, - message: "", - }); + const [responseMessage, setResponseMessage] = useState<{ + type: InfoMessageType; + message: string; + } | null>(null); const form = useForm({ resolver: zodResolver(addTournamentSchema), @@ -51,20 +51,18 @@ const TournamentForm = () => { try { await addTournament(data); setResponseMessage({ - isSuccessful: true, + type: "success", message: t("success"), }); - clearMessage(setResponseMessage); form.reset(); } catch (err: unknown) { console.log(err); + setResponseMessage({ - isSuccessful: false, + type: "error", message: t("failure"), }); - - clearMessage(setResponseMessage); } }; @@ -220,7 +218,14 @@ const TournamentForm = () => { {t("clearForm")} - + + {responseMessage && ( + setResponseMessage(null)} + /> + )} ); diff --git a/src/app/[locale]/(main)/clubs/ClubTable.tsx b/src/app/[locale]/(main)/clubs/ClubTable.tsx index 8e3abce..b206523 100644 --- a/src/app/[locale]/(main)/clubs/ClubTable.tsx +++ b/src/app/[locale]/(main)/clubs/ClubTable.tsx @@ -52,7 +52,7 @@ const ClubTable = () => {
-
+
diff --git a/src/app/[locale]/(main)/contact-us/ContactForm.tsx b/src/app/[locale]/(main)/contact-us/ContactForm.tsx index bbcd210..58a09c8 100644 --- a/src/app/[locale]/(main)/contact-us/ContactForm.tsx +++ b/src/app/[locale]/(main)/contact-us/ContactForm.tsx @@ -9,7 +9,7 @@ import { z } from "zod"; import { Button } from "@/components/Button"; import InfoMessage from "@/components/InfoMessage"; -import { clearMessage } from "@/components/InfoMessage"; +import { InfoMessageType } from "@/components/InfoMessage"; import { TextAreaField } from "@/components/form/TextAreaField"; import { TextField } from "@/components/form/TextField"; import { contactUsSchema } from "@/schemas"; @@ -20,10 +20,10 @@ type TournamentFormValues = z.infer; const ContactForm = () => { const t = useTranslations("Contact"); - const [responseMessage, setResponseMessage] = useState({ - isSuccessful: false, - message: "", - }); + const [responseMessage, setResponseMessage] = useState<{ + type: InfoMessageType; + message: string; + } | null>(null); const form = useForm({ resolver: zodResolver(contactUsSchema), @@ -33,20 +33,17 @@ const ContactForm = () => { try { await contactUs(data); setResponseMessage({ - isSuccessful: true, + type: "success", message: t("success"), }); - clearMessage(setResponseMessage); form.reset(); } catch (err: unknown) { console.log(err); setResponseMessage({ - isSuccessful: false, + type: "error", message: t("failure"), }); - - clearMessage(setResponseMessage); } }; @@ -82,7 +79,13 @@ const ContactForm = () => { {form.formState.isSubmitting ? t("sending") : t("sendButton")} - + {responseMessage && ( + setResponseMessage(null)} + /> + )} ); diff --git a/src/app/[locale]/(main)/delete-account/page.tsx b/src/app/[locale]/(main)/delete-account/page.tsx index f390ffd..aa84776 100644 --- a/src/app/[locale]/(main)/delete-account/page.tsx +++ b/src/app/[locale]/(main)/delete-account/page.tsx @@ -50,10 +50,7 @@ export default function Contact() { {deleted ? ( - + ) : ( <>

@@ -62,8 +59,9 @@ export default function Contact() { {error && ( )} diff --git a/src/app/[locale]/(main)/zones/components/ZoneForm.tsx b/src/app/[locale]/(main)/zones/components/ZoneForm.tsx index 02ddf79..e546a13 100644 --- a/src/app/[locale]/(main)/zones/components/ZoneForm.tsx +++ b/src/app/[locale]/(main)/zones/components/ZoneForm.tsx @@ -6,6 +6,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { z } from "zod"; import { Button } from "@/components/Button"; +import InfoMessage from "@/components/InfoMessage"; import { InlineSwitchField } from "@/components/form/InlineSwitchField"; import { Label } from "@/components/form/Label"; import { TextField } from "@/components/form/TextField"; @@ -20,6 +21,7 @@ type ZoneFormProps = { zone?: Zone; onSubmit: (data: ZoneFormValues) => Promise; onCancel: () => void; + withInfo?: boolean; submitTitle?: string; cancelTitle?: string; }; @@ -28,6 +30,7 @@ export const ZoneForm = ({ zone, onSubmit, onCancel, + withInfo, submitTitle, cancelTitle, }: ZoneFormProps) => { @@ -58,8 +61,14 @@ export const ZoneForm = ({ }, }); - const features = form.watch("features"); - console.log(features); + const [cn, rn, bn, on] = form.watch([ + "classicNotifications", + "rapidNotifications", + "blitzNotifications", + "otherNotifications", + ]); + + const requestingNotifications = cn || rn || bn || on; return ( @@ -89,22 +98,34 @@ export const ZoneForm = ({ control={form.control} label={at("timeControlEnum", { tc: TimeControl.Classic })} /> + + + + {withInfo && requestingNotifications && ( +

+
})} + /> +
+ )} +
diff --git a/src/app/[locale]/(main)/zones/create/page.tsx b/src/app/[locale]/(main)/zones/create/page.tsx index 592e540..32ac7da 100644 --- a/src/app/[locale]/(main)/zones/create/page.tsx +++ b/src/app/[locale]/(main)/zones/create/page.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import { useTranslations } from "next-intl"; -import InfoMessage, { clearMessage } from "@/components/InfoMessage"; +import InfoMessage, { InfoMessageType } from "@/components/InfoMessage"; import { useZones } from "@/hooks/useZones"; import { createZone } from "@/server/createZone"; import { useRouter } from "@/utils/routing"; @@ -15,10 +15,10 @@ const CreateZone = () => { const t = useTranslations("Zones"); const router = useRouter(); - const [responseMessage, setResponseMessage] = useState({ - isSuccessful: false, - message: "", - }); + const [responseMessage, setResponseMessage] = useState<{ + type: InfoMessageType; + message: string; + } | null>(null); const { refetch } = useZones(); @@ -31,11 +31,9 @@ const CreateZone = () => { } catch (err: unknown) { console.log(err); setResponseMessage({ - isSuccessful: false, + type: "error", message: t("createFailure"), }); - - clearMessage(setResponseMessage); } }; @@ -48,8 +46,19 @@ const CreateZone = () => { {t("createTitle")} - router.push("/zones")} /> - + router.push("/zones")} + /> + + {responseMessage && ( + setResponseMessage(null)} + /> + )} ); }; diff --git a/src/app/[locale]/(main)/zones/edit/[id]/page.tsx b/src/app/[locale]/(main)/zones/edit/[id]/page.tsx index 1c15ab5..35f817e 100644 --- a/src/app/[locale]/(main)/zones/edit/[id]/page.tsx +++ b/src/app/[locale]/(main)/zones/edit/[id]/page.tsx @@ -5,7 +5,7 @@ import { useState } from "react"; import { useTranslations } from "next-intl"; import { useParams } from "next/navigation"; -import InfoMessage, { clearMessage } from "@/components/InfoMessage"; +import InfoMessage, { InfoMessageType } from "@/components/InfoMessage"; import { useZones } from "@/hooks/useZones"; import { editZone } from "@/server/editZone"; import { useRouter } from "@/utils/routing"; @@ -17,10 +17,10 @@ const EditZone = () => { const router = useRouter(); const params = useParams(); - const [responseMessage, setResponseMessage] = useState({ - isSuccessful: false, - message: "", - }); + const [responseMessage, setResponseMessage] = useState<{ + type: InfoMessageType; + message: string; + } | null>(null); const { zones, isFetching, refetch } = useZones(); @@ -51,11 +51,9 @@ const EditZone = () => { router.push("/zones"); } catch (error) { setResponseMessage({ - isSuccessful: false, + type: "error", message: t("createFailure"), }); - - clearMessage(setResponseMessage); } }; @@ -74,7 +72,13 @@ const EditZone = () => { zone={zone} /> - + {responseMessage && ( + setResponseMessage(null)} + /> + )} ); }; diff --git a/src/app/[locale]/(main)/zones/page.tsx b/src/app/[locale]/(main)/zones/page.tsx index cd5d7a4..378c23d 100644 --- a/src/app/[locale]/(main)/zones/page.tsx +++ b/src/app/[locale]/(main)/zones/page.tsx @@ -7,7 +7,7 @@ import dynamic from "next/dynamic"; import { IoAdd } from "react-icons/io5"; import { Button, buttonVariants } from "@/components/Button"; -import InfoMessage, { clearMessage } from "@/components/InfoMessage"; +import InfoMessage, { InfoMessageType } from "@/components/InfoMessage"; import { Modal } from "@/components/Modal"; import { Spinner } from "@/components/Spinner"; import { useZones } from "@/hooks/useZones"; @@ -27,11 +27,10 @@ const Zones = () => { const format = useFormatter(); const [deletingZoneId, setDeletingZoneId] = useState(null); - const [responseMessage, setResponseMessage] = useState({ - isSuccessful: false, - message: "", - }); - + const [responseMessage, setResponseMessage] = useState<{ + type: InfoMessageType; + message: string; + } | null>(null); const { zones, isFetching, refetch } = useZones(); const onDelete = async () => { @@ -46,11 +45,9 @@ const Zones = () => { } catch (err: unknown) { console.log(err); setResponseMessage({ - isSuccessful: false, + type: "error", message: t("createFailure"), }); - - clearMessage(setResponseMessage); } }; @@ -154,7 +151,13 @@ const Zones = () => { title={t("deleteZoneTitle")} subTitle={t("deleteZoneInfo")} > - + {responseMessage && ( + setResponseMessage(null)} + /> + )}
+
+ + )} + + + ); }; export default InfoMessage; diff --git a/src/components/SignInForm.tsx b/src/components/SignInForm.tsx index 02a1c57..f736f6c 100644 --- a/src/components/SignInForm.tsx +++ b/src/components/SignInForm.tsx @@ -9,7 +9,7 @@ import { usePathname as useRawPathname } from "next/navigation"; import { FormProvider, useForm } from "react-hook-form"; import { z } from "zod"; -import { clearMessage } from "@/components/InfoMessage"; +import { InfoMessageType } from "@/components/InfoMessage"; import InfoMessage from "@/components/InfoMessage"; import { TextField } from "@/components/form/TextField"; @@ -31,10 +31,10 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => { // We use the version from next/navigation to get the raw pathname in the current locale const path = useRawPathname(); - const [responseMessage, setResponseMessage] = useState({ - isSuccessful: false, - message: "", - }); + const [responseMessage, setResponseMessage] = useState<{ + type: InfoMessageType; + message: string; + } | null>(null); const form = useForm({ resolver: zodResolver(signInSchema), @@ -44,13 +44,13 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => { // Clear the response message when the email field changes const subscription = form.watch((value, { name, type }) => { if (type === "change") { - if (name === "email" && responseMessage.message === "") { - setResponseMessage({ isSuccessful: true, message: "" }); + if (name === "email" && responseMessage !== null) { + setResponseMessage(null); } } }); return () => subscription.unsubscribe(); - }, [form, form.watch, responseMessage.message]); + }, [form, form.watch, responseMessage]); const onSubmit = async ({ email }: SignInFormValues) => { try { @@ -64,7 +64,7 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => { throw new Error(result.error); } else if (result?.ok) { setResponseMessage({ - isSuccessful: true, + type: "success", message: t("checkEmail"), }); @@ -74,11 +74,9 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => { console.log(err); setResponseMessage({ - isSuccessful: false, + type: "error", message: t("failure"), }); - - clearMessage(setResponseMessage); } }; @@ -87,6 +85,7 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => {
{ {t("signInButton")} - + {responseMessage && ( + + )} ); diff --git a/src/messages/en.json b/src/messages/en.json index da8f24f..2cecba2 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -237,7 +237,7 @@ "zoneNamePlaceholder": "Name for your zone", "notificationsLabel": "Notifications", "notificationsInfo": "You can choose to turn on notifications for the time controls that interest you, and will receive an email when a new tournament is added to this zone.", - + "firstNotificationInfo": "The first email you receive will list all the current tournaments in this zone.

Subsequent notifications will only list new tournaments.", "createFailure": "Oops, something went wrong. Please try again later.", "deleteZoneTitle": "Delete Zone", diff --git a/src/messages/fr.json b/src/messages/fr.json index 003588f..e5e6797 100644 --- a/src/messages/fr.json +++ b/src/messages/fr.json @@ -238,6 +238,7 @@ "zoneNamePlaceholder": "Votre nom pour cette région", "notificationsLabel": "Notifications", "notificationsInfo": "Vous pouvez choisir d'activer les notifications pour les cadences qui vous intéressent, et vous recevrez un e-mail lorsqu'un nouveau tournoi est ajouté dans cette région.", + "firstNotificationInfo": "Le premier email que vous recevrez listera tous les tournois actuels dans cette zone.

Les notifications suivantes ne listeront que les nouveaux tournois.", "createFailure": "Oups, quelque chose s'est mal passé. Veuillez réessayer.",