feat: add conditional signup values
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import { useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { privacyPolicy } from "@/constants/currentWebHrefs"
|
||||
|
||||
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
|
||||
import CheckboxCard from "@/components/TempDesignSystem/Form/ChoiceCard/Checkbox"
|
||||
import DateSelect from "@/components/TempDesignSystem/Form/Date"
|
||||
import Input from "@/components/TempDesignSystem/Form/Input"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import styles from "./signup.module.css"
|
||||
|
||||
export default function Signup({ name }: { name: string }) {
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
|
||||
const [isJoinChecked, setIsJoinChecked] = useState(false)
|
||||
|
||||
const joinValue = useWatch({ name })
|
||||
|
||||
useEffect(() => {
|
||||
// In order to avoid hydration errors the state needs to be set as side effect,
|
||||
// since the join value can come from search params
|
||||
setIsJoinChecked(joinValue)
|
||||
}, [joinValue])
|
||||
|
||||
const list = [
|
||||
{ title: intl.formatMessage({ id: "Earn bonus nights & points" }) },
|
||||
{ title: intl.formatMessage({ id: "Get member benefits & offers" }) },
|
||||
{ title: intl.formatMessage({ id: "Join at no cost" }) },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<CheckboxCard
|
||||
highlightSubtitle
|
||||
list={list}
|
||||
name={name}
|
||||
subtitle={intl.formatMessage(
|
||||
{
|
||||
id: "{difference}{amount} {currency}",
|
||||
},
|
||||
{
|
||||
amount: "491",
|
||||
currency: "SEK",
|
||||
difference: "-",
|
||||
}
|
||||
)}
|
||||
title={intl.formatMessage({ id: "Join Scandic Friends" })}
|
||||
/>
|
||||
{isJoinChecked ? (
|
||||
<div className={styles.additionalFormData}>
|
||||
<div className={styles.dateField}>
|
||||
<header>
|
||||
<Caption type="bold">
|
||||
{intl.formatMessage({ id: "Birth date" })} *
|
||||
</Caption>
|
||||
</header>
|
||||
<DateSelect
|
||||
name="dateOfBirth"
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
<Input
|
||||
name="zipCode"
|
||||
label={intl.formatMessage({ id: "Zip code" })}
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox name="termsAccepted" registerOptions={{ required: true }}>
|
||||
<Body>
|
||||
{intl.formatMessage({
|
||||
id: "Yes, I accept the Terms and conditions for Scandic Friends and understand that Scandic will process my personal data in accordance with",
|
||||
})}{" "}
|
||||
<Link
|
||||
variant="underscored"
|
||||
color="peach80"
|
||||
target="_blank"
|
||||
href={privacyPolicy[lang]}
|
||||
>
|
||||
{intl.formatMessage({ id: "Scandic's Privacy Policy." })}
|
||||
</Link>
|
||||
</Body>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
.container {
|
||||
display: grid;
|
||||
grid-column: 1/-1;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.additionalFormData {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.dateField {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
@@ -6,14 +6,16 @@ import { useIntl } from "react-intl"
|
||||
|
||||
import { useEnterDetailsStore } from "@/stores/enter-details"
|
||||
|
||||
import { registerUserBookingFlow } from "@/actions/registerUserBookingFlow"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import CheckboxCard from "@/components/TempDesignSystem/Form/ChoiceCard/Checkbox"
|
||||
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
|
||||
import Input from "@/components/TempDesignSystem/Form/Input"
|
||||
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
|
||||
import { detailsSchema, signedInDetailsSchema } from "./schema"
|
||||
import Signup from "./Signup"
|
||||
|
||||
import styles from "./details.module.css"
|
||||
|
||||
@@ -25,28 +27,30 @@ import type {
|
||||
const formID = "enter-details"
|
||||
export default function Details({ user }: DetailsProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const list = [
|
||||
{ title: intl.formatMessage({ id: "Earn bonus nights & points" }) },
|
||||
{ title: intl.formatMessage({ id: "Get member benefits & offers" }) },
|
||||
{ title: intl.formatMessage({ id: "Join at no cost" }) },
|
||||
]
|
||||
|
||||
const initialData = useEnterDetailsStore((state) => ({
|
||||
countryCode: state.data.countryCode,
|
||||
email: state.data.email,
|
||||
firstname: state.data.firstname,
|
||||
lastname: state.data.lastname,
|
||||
firstName: state.data.firstName,
|
||||
lastName: state.data.lastName,
|
||||
phoneNumber: state.data.phoneNumber,
|
||||
join: state.data.join,
|
||||
dateOfBirth: state.data.dateOfBirth,
|
||||
zipCode: state.data.zipCode,
|
||||
termsAccepted: state.data.termsAccepted,
|
||||
}))
|
||||
|
||||
const methods = useForm<DetailsSchema>({
|
||||
defaultValues: {
|
||||
countryCode: user?.address?.countryCode ?? initialData.countryCode,
|
||||
email: user?.email ?? initialData.email,
|
||||
firstname: user?.firstName ?? initialData.firstname,
|
||||
lastname: user?.lastName ?? initialData.lastname,
|
||||
firstName: user?.firstName ?? initialData.firstName,
|
||||
lastName: user?.lastName ?? initialData.lastName,
|
||||
phoneNumber: user?.phoneNumber ?? initialData.phoneNumber,
|
||||
//@ts-expect-error: We use a literal for join to be true or false, which does not convert to a boolean
|
||||
join: initialData.join,
|
||||
dateOfBirth: initialData.dateOfBirth,
|
||||
zipCode: initialData.zipCode,
|
||||
termsAccepted: initialData.termsAccepted,
|
||||
},
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
@@ -56,10 +60,39 @@ export default function Details({ user }: DetailsProps) {
|
||||
|
||||
const completeStep = useEnterDetailsStore((state) => state.completeStep)
|
||||
|
||||
// const errorMessage = intl.formatMessage({
|
||||
// id: "An error occurred. Please try again.",
|
||||
// })
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(values: DetailsSchema) => {
|
||||
async function (values: DetailsSchema) {
|
||||
if (values.join) {
|
||||
const signupVals = {
|
||||
firstName: values.firstName,
|
||||
lastName: values.lastName,
|
||||
email: values.email,
|
||||
phoneNumber: values.phoneNumber,
|
||||
address: {
|
||||
zipCode: values.zipCode,
|
||||
countryCode: values.countryCode,
|
||||
},
|
||||
dateOfBirth: values.dateOfBirth,
|
||||
}
|
||||
|
||||
const res = await registerUserBookingFlow(signupVals)
|
||||
if (!res.success) {
|
||||
// if (res.error) {
|
||||
// toast.error(res.error)
|
||||
// } else {
|
||||
// toast.error(errorMessage)
|
||||
// }
|
||||
return
|
||||
}
|
||||
console.log("Signed up user: ", res)
|
||||
}
|
||||
completeStep(values)
|
||||
},
|
||||
|
||||
[completeStep]
|
||||
)
|
||||
|
||||
@@ -77,14 +110,14 @@ export default function Details({ user }: DetailsProps) {
|
||||
onSubmit={methods.handleSubmit(onSubmit)}
|
||||
>
|
||||
<Input
|
||||
label={intl.formatMessage({ id: "Firstname" })}
|
||||
name="firstname"
|
||||
label={intl.formatMessage({ id: "First name" })}
|
||||
name="firstName"
|
||||
readOnly={!!user}
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
<Input
|
||||
label={intl.formatMessage({ id: "Lastname" })}
|
||||
name="lastname"
|
||||
label={intl.formatMessage({ id: "Last name" })}
|
||||
name="lastName"
|
||||
readOnly={!!user}
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
@@ -109,26 +142,9 @@ export default function Details({ user }: DetailsProps) {
|
||||
readOnly={!!user}
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
{user ? null : <Signup name="join" />}
|
||||
</form>
|
||||
<footer className={styles.footer}>
|
||||
{user ? null : (
|
||||
<CheckboxCard
|
||||
highlightSubtitle
|
||||
list={list}
|
||||
name="join"
|
||||
subtitle={intl.formatMessage(
|
||||
{
|
||||
id: "{difference}{amount} {currency}",
|
||||
},
|
||||
{
|
||||
amount: "491",
|
||||
currency: "SEK",
|
||||
difference: "-",
|
||||
}
|
||||
)}
|
||||
title={intl.formatMessage({ id: "Join Scandic Friends" })}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
disabled={!methods.formState.isValid}
|
||||
form={formID}
|
||||
|
||||
@@ -2,18 +2,49 @@ import { z } from "zod"
|
||||
|
||||
import { phoneValidator } from "@/utils/phoneValidator"
|
||||
|
||||
export const detailsSchema = z.object({
|
||||
export const baseDetailsSchema = z.object({
|
||||
countryCode: z.string(),
|
||||
email: z.string().email(),
|
||||
firstname: z.string(),
|
||||
lastname: z.string(),
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
phoneNumber: phoneValidator(),
|
||||
})
|
||||
|
||||
export const notJoinDetailsSchema = baseDetailsSchema.merge(
|
||||
z.object({
|
||||
join: z.literal(false),
|
||||
zipCode: z.string().optional(),
|
||||
dateOfBirth: z.string().optional(),
|
||||
termsAccepted: z.boolean().default(false),
|
||||
})
|
||||
)
|
||||
|
||||
export const joinDetailsSchema = baseDetailsSchema.merge(
|
||||
z.object({
|
||||
join: z.literal(true),
|
||||
zipCode: z.string().min(1, { message: "Zip code is required" }),
|
||||
dateOfBirth: z.string(),
|
||||
termsAccepted: z.literal(true, {
|
||||
errorMap: (err, ctx) => {
|
||||
switch (err.code) {
|
||||
case "invalid_literal":
|
||||
return { message: "You must accept the terms and conditions" }
|
||||
}
|
||||
return { message: ctx.defaultError }
|
||||
},
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
export const detailsSchema = z.discriminatedUnion("join", [
|
||||
notJoinDetailsSchema,
|
||||
joinDetailsSchema,
|
||||
])
|
||||
|
||||
export const signedInDetailsSchema = z.object({
|
||||
countryCode: z.string().optional(),
|
||||
email: z.string().email().optional(),
|
||||
firstname: z.string().optional(),
|
||||
lastname: z.string().optional(),
|
||||
firstName: z.string().optional(),
|
||||
lastName: z.string().optional(),
|
||||
phoneNumber: phoneValidator().optional(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user