mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-23 04:26:57 +00:00
Add information about first notification email
This commit is contained in:
@@ -9,7 +9,7 @@ import { FormProvider, useForm } from "react-hook-form";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { clearMessage } from "@/components/InfoMessage";
|
import { InfoMessageType } from "@/components/InfoMessage";
|
||||||
import InfoMessage from "@/components/InfoMessage";
|
import InfoMessage from "@/components/InfoMessage";
|
||||||
import LoadingMap from "@/components/LoadingMap";
|
import LoadingMap from "@/components/LoadingMap";
|
||||||
import { DateField } from "@/components/form/DateField";
|
import { DateField } from "@/components/form/DateField";
|
||||||
@@ -32,10 +32,10 @@ const TournamentForm = () => {
|
|||||||
const t = useTranslations("AddTournament");
|
const t = useTranslations("AddTournament");
|
||||||
const at = useTranslations("App");
|
const at = useTranslations("App");
|
||||||
|
|
||||||
const [responseMessage, setResponseMessage] = useState({
|
const [responseMessage, setResponseMessage] = useState<{
|
||||||
isSuccessful: false,
|
type: InfoMessageType;
|
||||||
message: "",
|
message: string;
|
||||||
});
|
} | null>(null);
|
||||||
|
|
||||||
const form = useForm<TournamentFormValues>({
|
const form = useForm<TournamentFormValues>({
|
||||||
resolver: zodResolver(addTournamentSchema),
|
resolver: zodResolver(addTournamentSchema),
|
||||||
@@ -51,20 +51,18 @@ const TournamentForm = () => {
|
|||||||
try {
|
try {
|
||||||
await addTournament(data);
|
await addTournament(data);
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: true,
|
type: "success",
|
||||||
message: t("success"),
|
message: t("success"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
form.reset();
|
form.reset();
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: false,
|
type: "error",
|
||||||
message: t("failure"),
|
message: t("failure"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -220,7 +218,14 @@ const TournamentForm = () => {
|
|||||||
{t("clearForm")}
|
{t("clearForm")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<InfoMessage responseMessage={responseMessage} />
|
|
||||||
|
{responseMessage && (
|
||||||
|
<InfoMessage
|
||||||
|
message={responseMessage.message}
|
||||||
|
type={responseMessage.type}
|
||||||
|
onDismiss={() => setResponseMessage(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const ClubTable = () => {
|
|||||||
<div className="flex flex-col gap-0 text-gray-900 dark:text-white"></div>
|
<div className="flex flex-col gap-0 text-gray-900 dark:text-white"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="overflow-x-scroll bg-red-50">
|
<div className="overflow-x-scroll">
|
||||||
<table className="relative min-w-full table-fixed text-center text-xs lg:min-w-full">
|
<table className="relative min-w-full table-fixed text-center text-xs lg:min-w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import InfoMessage from "@/components/InfoMessage";
|
import InfoMessage from "@/components/InfoMessage";
|
||||||
import { clearMessage } from "@/components/InfoMessage";
|
import { InfoMessageType } from "@/components/InfoMessage";
|
||||||
import { TextAreaField } from "@/components/form/TextAreaField";
|
import { TextAreaField } from "@/components/form/TextAreaField";
|
||||||
import { TextField } from "@/components/form/TextField";
|
import { TextField } from "@/components/form/TextField";
|
||||||
import { contactUsSchema } from "@/schemas";
|
import { contactUsSchema } from "@/schemas";
|
||||||
@@ -20,10 +20,10 @@ type TournamentFormValues = z.infer<typeof contactUsSchema>;
|
|||||||
const ContactForm = () => {
|
const ContactForm = () => {
|
||||||
const t = useTranslations("Contact");
|
const t = useTranslations("Contact");
|
||||||
|
|
||||||
const [responseMessage, setResponseMessage] = useState({
|
const [responseMessage, setResponseMessage] = useState<{
|
||||||
isSuccessful: false,
|
type: InfoMessageType;
|
||||||
message: "",
|
message: string;
|
||||||
});
|
} | null>(null);
|
||||||
|
|
||||||
const form = useForm<TournamentFormValues>({
|
const form = useForm<TournamentFormValues>({
|
||||||
resolver: zodResolver(contactUsSchema),
|
resolver: zodResolver(contactUsSchema),
|
||||||
@@ -33,20 +33,17 @@ const ContactForm = () => {
|
|||||||
try {
|
try {
|
||||||
await contactUs(data);
|
await contactUs(data);
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: true,
|
type: "success",
|
||||||
message: t("success"),
|
message: t("success"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
form.reset();
|
form.reset();
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: false,
|
type: "error",
|
||||||
message: t("failure"),
|
message: t("failure"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,7 +79,13 @@ const ContactForm = () => {
|
|||||||
{form.formState.isSubmitting ? t("sending") : t("sendButton")}
|
{form.formState.isSubmitting ? t("sending") : t("sendButton")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<InfoMessage responseMessage={responseMessage} />
|
{responseMessage && (
|
||||||
|
<InfoMessage
|
||||||
|
message={responseMessage.message}
|
||||||
|
type={responseMessage.type}
|
||||||
|
onDismiss={() => setResponseMessage(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -50,10 +50,7 @@ export default function Contact() {
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{deleted ? (
|
{deleted ? (
|
||||||
<InfoMessage
|
<InfoMessage className="mb-8" type="success" message={t("success")} />
|
||||||
className="text-center"
|
|
||||||
responseMessage={{ isSuccessful: true, message: t("success") }}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className="mb-8 text-center font-light text-gray-500 dark:text-gray-400 sm:text-xl lg:mb-16">
|
<p className="mb-8 text-center font-light text-gray-500 dark:text-gray-400 sm:text-xl lg:mb-16">
|
||||||
@@ -62,8 +59,9 @@ export default function Contact() {
|
|||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<InfoMessage
|
<InfoMessage
|
||||||
className="mb-8 text-center"
|
className="mb-8"
|
||||||
responseMessage={{ isSuccessful: false, message: t("failure") }}
|
type="error"
|
||||||
|
message={t("failure")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { FormProvider, useForm } from "react-hook-form";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
|
import InfoMessage from "@/components/InfoMessage";
|
||||||
import { InlineSwitchField } from "@/components/form/InlineSwitchField";
|
import { InlineSwitchField } from "@/components/form/InlineSwitchField";
|
||||||
import { Label } from "@/components/form/Label";
|
import { Label } from "@/components/form/Label";
|
||||||
import { TextField } from "@/components/form/TextField";
|
import { TextField } from "@/components/form/TextField";
|
||||||
@@ -20,6 +21,7 @@ type ZoneFormProps = {
|
|||||||
zone?: Zone;
|
zone?: Zone;
|
||||||
onSubmit: (data: ZoneFormValues) => Promise<void>;
|
onSubmit: (data: ZoneFormValues) => Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
withInfo?: boolean;
|
||||||
submitTitle?: string;
|
submitTitle?: string;
|
||||||
cancelTitle?: string;
|
cancelTitle?: string;
|
||||||
};
|
};
|
||||||
@@ -28,6 +30,7 @@ export const ZoneForm = ({
|
|||||||
zone,
|
zone,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
withInfo,
|
||||||
submitTitle,
|
submitTitle,
|
||||||
cancelTitle,
|
cancelTitle,
|
||||||
}: ZoneFormProps) => {
|
}: ZoneFormProps) => {
|
||||||
@@ -58,8 +61,14 @@ export const ZoneForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const features = form.watch("features");
|
const [cn, rn, bn, on] = form.watch([
|
||||||
console.log(features);
|
"classicNotifications",
|
||||||
|
"rapidNotifications",
|
||||||
|
"blitzNotifications",
|
||||||
|
"otherNotifications",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const requestingNotifications = cn || rn || bn || on;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
@@ -89,22 +98,34 @@ export const ZoneForm = ({
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
label={at("timeControlEnum", { tc: TimeControl.Classic })}
|
label={at("timeControlEnum", { tc: TimeControl.Classic })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InlineSwitchField
|
<InlineSwitchField
|
||||||
name="rapidNotifications"
|
name="rapidNotifications"
|
||||||
control={form.control}
|
control={form.control}
|
||||||
label={at("timeControlEnum", { tc: TimeControl.Rapid })}
|
label={at("timeControlEnum", { tc: TimeControl.Rapid })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InlineSwitchField
|
<InlineSwitchField
|
||||||
name="blitzNotifications"
|
name="blitzNotifications"
|
||||||
control={form.control}
|
control={form.control}
|
||||||
label={at("timeControlEnum", { tc: TimeControl.Blitz })}
|
label={at("timeControlEnum", { tc: TimeControl.Blitz })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InlineSwitchField
|
<InlineSwitchField
|
||||||
name="otherNotifications"
|
name="otherNotifications"
|
||||||
control={form.control}
|
control={form.control}
|
||||||
label={at("timeControlEnum", { tc: TimeControl.Other })}
|
label={at("timeControlEnum", { tc: TimeControl.Other })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{withInfo && requestingNotifications && (
|
||||||
|
<div className="col-span-4">
|
||||||
|
<InfoMessage
|
||||||
|
type="info"
|
||||||
|
message={t.rich("firstNotificationInfo", { br: () => <br /> })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<section id="map" className="z-0 col-span-4 flex h-auto">
|
<section id="map" className="z-0 col-span-4 flex h-auto">
|
||||||
<ZoneEditorField name="features" control={form.control} />
|
<ZoneEditorField name="features" control={form.control} />
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useState } from "react";
|
|||||||
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
import InfoMessage, { clearMessage } from "@/components/InfoMessage";
|
import InfoMessage, { InfoMessageType } from "@/components/InfoMessage";
|
||||||
import { useZones } from "@/hooks/useZones";
|
import { useZones } from "@/hooks/useZones";
|
||||||
import { createZone } from "@/server/createZone";
|
import { createZone } from "@/server/createZone";
|
||||||
import { useRouter } from "@/utils/routing";
|
import { useRouter } from "@/utils/routing";
|
||||||
@@ -15,10 +15,10 @@ const CreateZone = () => {
|
|||||||
const t = useTranslations("Zones");
|
const t = useTranslations("Zones");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [responseMessage, setResponseMessage] = useState({
|
const [responseMessage, setResponseMessage] = useState<{
|
||||||
isSuccessful: false,
|
type: InfoMessageType;
|
||||||
message: "",
|
message: string;
|
||||||
});
|
} | null>(null);
|
||||||
|
|
||||||
const { refetch } = useZones();
|
const { refetch } = useZones();
|
||||||
|
|
||||||
@@ -31,11 +31,9 @@ const CreateZone = () => {
|
|||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: false,
|
type: "error",
|
||||||
message: t("createFailure"),
|
message: t("createFailure"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,8 +46,19 @@ const CreateZone = () => {
|
|||||||
{t("createTitle")}
|
{t("createTitle")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<ZoneForm onSubmit={onSubmit} onCancel={() => router.push("/zones")} />
|
<ZoneForm
|
||||||
<InfoMessage responseMessage={responseMessage} />
|
withInfo
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
onCancel={() => router.push("/zones")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{responseMessage && (
|
||||||
|
<InfoMessage
|
||||||
|
message={responseMessage.message}
|
||||||
|
type={responseMessage.type}
|
||||||
|
onDismiss={() => setResponseMessage(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useState } from "react";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
import InfoMessage, { clearMessage } from "@/components/InfoMessage";
|
import InfoMessage, { InfoMessageType } from "@/components/InfoMessage";
|
||||||
import { useZones } from "@/hooks/useZones";
|
import { useZones } from "@/hooks/useZones";
|
||||||
import { editZone } from "@/server/editZone";
|
import { editZone } from "@/server/editZone";
|
||||||
import { useRouter } from "@/utils/routing";
|
import { useRouter } from "@/utils/routing";
|
||||||
@@ -17,10 +17,10 @@ const EditZone = () => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
const [responseMessage, setResponseMessage] = useState({
|
const [responseMessage, setResponseMessage] = useState<{
|
||||||
isSuccessful: false,
|
type: InfoMessageType;
|
||||||
message: "",
|
message: string;
|
||||||
});
|
} | null>(null);
|
||||||
|
|
||||||
const { zones, isFetching, refetch } = useZones();
|
const { zones, isFetching, refetch } = useZones();
|
||||||
|
|
||||||
@@ -51,11 +51,9 @@ const EditZone = () => {
|
|||||||
router.push("/zones");
|
router.push("/zones");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: false,
|
type: "error",
|
||||||
message: t("createFailure"),
|
message: t("createFailure"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -74,7 +72,13 @@ const EditZone = () => {
|
|||||||
zone={zone}
|
zone={zone}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InfoMessage responseMessage={responseMessage} />
|
{responseMessage && (
|
||||||
|
<InfoMessage
|
||||||
|
message={responseMessage.message}
|
||||||
|
type={responseMessage.type}
|
||||||
|
onDismiss={() => setResponseMessage(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import dynamic from "next/dynamic";
|
|||||||
import { IoAdd } from "react-icons/io5";
|
import { IoAdd } from "react-icons/io5";
|
||||||
|
|
||||||
import { Button, buttonVariants } from "@/components/Button";
|
import { Button, buttonVariants } from "@/components/Button";
|
||||||
import InfoMessage, { clearMessage } from "@/components/InfoMessage";
|
import InfoMessage, { InfoMessageType } from "@/components/InfoMessage";
|
||||||
import { Modal } from "@/components/Modal";
|
import { Modal } from "@/components/Modal";
|
||||||
import { Spinner } from "@/components/Spinner";
|
import { Spinner } from "@/components/Spinner";
|
||||||
import { useZones } from "@/hooks/useZones";
|
import { useZones } from "@/hooks/useZones";
|
||||||
@@ -27,11 +27,10 @@ const Zones = () => {
|
|||||||
const format = useFormatter();
|
const format = useFormatter();
|
||||||
|
|
||||||
const [deletingZoneId, setDeletingZoneId] = useState<string | null>(null);
|
const [deletingZoneId, setDeletingZoneId] = useState<string | null>(null);
|
||||||
const [responseMessage, setResponseMessage] = useState({
|
const [responseMessage, setResponseMessage] = useState<{
|
||||||
isSuccessful: false,
|
type: InfoMessageType;
|
||||||
message: "",
|
message: string;
|
||||||
});
|
} | null>(null);
|
||||||
|
|
||||||
const { zones, isFetching, refetch } = useZones();
|
const { zones, isFetching, refetch } = useZones();
|
||||||
|
|
||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
@@ -46,11 +45,9 @@ const Zones = () => {
|
|||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: false,
|
type: "error",
|
||||||
message: t("createFailure"),
|
message: t("createFailure"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -154,7 +151,13 @@ const Zones = () => {
|
|||||||
title={t("deleteZoneTitle")}
|
title={t("deleteZoneTitle")}
|
||||||
subTitle={t("deleteZoneInfo")}
|
subTitle={t("deleteZoneInfo")}
|
||||||
>
|
>
|
||||||
<InfoMessage responseMessage={responseMessage} />
|
{responseMessage && (
|
||||||
|
<InfoMessage
|
||||||
|
message={responseMessage.message}
|
||||||
|
type={responseMessage.type}
|
||||||
|
onDismiss={() => setResponseMessage(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-between space-x-4 text-sm font-bold">
|
<div className="flex items-center justify-between space-x-4 text-sm font-bold">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,37 +1,85 @@
|
|||||||
import { Dispatch, SetStateAction } from "react";
|
import {
|
||||||
|
HiCheckCircle,
|
||||||
|
HiInformationCircle,
|
||||||
|
HiXCircle,
|
||||||
|
HiXMark,
|
||||||
|
} from "react-icons/hi2";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { ResponseMessage } from "@/types";
|
export type InfoMessageType = "success" | "error" | "info";
|
||||||
|
|
||||||
const InfoMessage = ({
|
const InfoMessage = ({
|
||||||
responseMessage,
|
message,
|
||||||
|
type,
|
||||||
className,
|
className,
|
||||||
|
onDismiss,
|
||||||
}: {
|
}: {
|
||||||
responseMessage: ResponseMessage;
|
message: React.ReactNode;
|
||||||
|
type: "success" | "error" | "info";
|
||||||
|
onDismiss?: () => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) => (
|
}) => {
|
||||||
<p
|
const icons = {
|
||||||
className={twMerge(
|
success: HiCheckCircle,
|
||||||
"italic",
|
error: HiXCircle,
|
||||||
responseMessage.isSuccessful ? "text-green-600" : "text-red-600",
|
info: HiInformationCircle,
|
||||||
className,
|
};
|
||||||
)}
|
|
||||||
data-test="info-message"
|
|
||||||
>
|
|
||||||
{responseMessage.message}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const clearMessage = (
|
const colorClasses = {
|
||||||
setResponseMessage: Dispatch<SetStateAction<ResponseMessage>>,
|
success: {
|
||||||
) => {
|
text: "text-success",
|
||||||
setTimeout(() => {
|
bg: "bg-success/30",
|
||||||
setResponseMessage({
|
button:
|
||||||
isSuccessful: false,
|
"bg-transparent hover:bg-success hover:text-white focus:ring-success focus:ring-offset-success/50",
|
||||||
message: "",
|
},
|
||||||
});
|
error: {
|
||||||
}, 10000);
|
text: "text-error",
|
||||||
|
bg: "bg-error/30",
|
||||||
|
button:
|
||||||
|
"bg-transparent hover:bg-error hover:text-white focus:ring-error focus:ring-offset-error/50",
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
text: "text-primary",
|
||||||
|
bg: "bg-primary/30",
|
||||||
|
button:
|
||||||
|
"bg-transparent hover:bg-primary-800 hover:text-white focus:ring-primary-800 focus:ring-offset-primary-50",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Icon = icons[type];
|
||||||
|
const { text, bg, button } = colorClasses[type];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={twMerge("rounded-md p-4", bg, className)}>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<Icon aria-hidden="true" className={twMerge("h-5 w-5", text)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ml-3">
|
||||||
|
<p className={twMerge("text-sm font-medium", text)}>{message}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{onDismiss && (
|
||||||
|
<div className="ml-auto pl-3">
|
||||||
|
<div className="-mx-1.5 -my-1.5">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={twMerge(
|
||||||
|
"inline-flex rounded-md border-0 p-1.5 focus:outline-none focus:ring-2 focus:ring-offset-2",
|
||||||
|
text,
|
||||||
|
bg,
|
||||||
|
button,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<HiXMark aria-hidden="true" className="h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InfoMessage;
|
export default InfoMessage;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { usePathname as useRawPathname } from "next/navigation";
|
|||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { clearMessage } from "@/components/InfoMessage";
|
import { InfoMessageType } from "@/components/InfoMessage";
|
||||||
import InfoMessage from "@/components/InfoMessage";
|
import InfoMessage from "@/components/InfoMessage";
|
||||||
import { TextField } from "@/components/form/TextField";
|
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
|
// We use the version from next/navigation to get the raw pathname in the current locale
|
||||||
const path = useRawPathname();
|
const path = useRawPathname();
|
||||||
|
|
||||||
const [responseMessage, setResponseMessage] = useState({
|
const [responseMessage, setResponseMessage] = useState<{
|
||||||
isSuccessful: false,
|
type: InfoMessageType;
|
||||||
message: "",
|
message: string;
|
||||||
});
|
} | null>(null);
|
||||||
|
|
||||||
const form = useForm<SignInFormValues>({
|
const form = useForm<SignInFormValues>({
|
||||||
resolver: zodResolver(signInSchema),
|
resolver: zodResolver(signInSchema),
|
||||||
@@ -44,13 +44,13 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => {
|
|||||||
// Clear the response message when the email field changes
|
// Clear the response message when the email field changes
|
||||||
const subscription = form.watch((value, { name, type }) => {
|
const subscription = form.watch((value, { name, type }) => {
|
||||||
if (type === "change") {
|
if (type === "change") {
|
||||||
if (name === "email" && responseMessage.message === "") {
|
if (name === "email" && responseMessage !== null) {
|
||||||
setResponseMessage({ isSuccessful: true, message: "" });
|
setResponseMessage(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => subscription.unsubscribe();
|
return () => subscription.unsubscribe();
|
||||||
}, [form, form.watch, responseMessage.message]);
|
}, [form, form.watch, responseMessage]);
|
||||||
|
|
||||||
const onSubmit = async ({ email }: SignInFormValues) => {
|
const onSubmit = async ({ email }: SignInFormValues) => {
|
||||||
try {
|
try {
|
||||||
@@ -64,7 +64,7 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => {
|
|||||||
throw new Error(result.error);
|
throw new Error(result.error);
|
||||||
} else if (result?.ok) {
|
} else if (result?.ok) {
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: true,
|
type: "success",
|
||||||
message: t("checkEmail"),
|
message: t("checkEmail"),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,11 +74,9 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => {
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
||||||
setResponseMessage({
|
setResponseMessage({
|
||||||
isSuccessful: false,
|
type: "error",
|
||||||
message: t("failure"),
|
message: t("failure"),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearMessage(setResponseMessage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,6 +85,7 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => {
|
|||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<TextField
|
<TextField
|
||||||
name="email"
|
name="email"
|
||||||
|
type="email"
|
||||||
control={form.control}
|
control={form.control}
|
||||||
label={t("emailLabel")}
|
label={t("emailLabel")}
|
||||||
placeholder={t("emailPlaceholder")}
|
placeholder={t("emailPlaceholder")}
|
||||||
@@ -97,10 +96,12 @@ export const SignInForm = ({ callbackPath }: SignInFormProps) => {
|
|||||||
{t("signInButton")}
|
{t("signInButton")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<InfoMessage
|
{responseMessage && (
|
||||||
className="text-center"
|
<InfoMessage
|
||||||
responseMessage={responseMessage}
|
message={responseMessage.message}
|
||||||
/>
|
type={responseMessage.type}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -237,7 +237,7 @@
|
|||||||
"zoneNamePlaceholder": "Name for your zone",
|
"zoneNamePlaceholder": "Name for your zone",
|
||||||
"notificationsLabel": "Notifications",
|
"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.",
|
"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.<br></br>Subsequent notifications will only list new tournaments.",
|
||||||
"createFailure": "Oops, something went wrong. Please try again later.",
|
"createFailure": "Oops, something went wrong. Please try again later.",
|
||||||
|
|
||||||
"deleteZoneTitle": "Delete Zone",
|
"deleteZoneTitle": "Delete Zone",
|
||||||
|
|||||||
@@ -238,6 +238,7 @@
|
|||||||
"zoneNamePlaceholder": "Votre nom pour cette région",
|
"zoneNamePlaceholder": "Votre nom pour cette région",
|
||||||
"notificationsLabel": "Notifications",
|
"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.",
|
"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.<br></br>Les notifications suivantes ne listeront que les nouveaux tournois.",
|
||||||
|
|
||||||
"createFailure": "Oups, quelque chose s'est mal passé. Veuillez réessayer.",
|
"createFailure": "Oups, quelque chose s'est mal passé. Veuillez réessayer.",
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user