From d84efcbb0f4a2bb06ec78d882b46e56ababe723e Mon Sep 17 00:00:00 2001 From: Simon Emanuelsson Date: Tue, 18 Jun 2024 08:15:57 +0200 Subject: [PATCH] feat(WEB-162): final design edit profile page --- actions/editProfile.ts | 4 +- .../my-pages/profile/@communication/page.tsx | 10 +- .../my-pages/profile/@creditCards/page.tsx | 10 +- .../profile/@profile/edit/default.tsx | 2 +- .../my-pages/profile/@profile/edit/page.tsx | 12 +- .../profile/@profile/layout.module.css | 7 + .../my-pages/profile/@profile/layout.tsx | 7 + .../my-pages/profile/@profile/page.module.css | 19 - .../my-pages/profile/@profile/page.tsx | 29 +- .../my-pages/profile/edit/page.tsx | 5 + .../@languageSwitcher/[...paths]/page.tsx | 17 - .../(live)/@languageSwitcher/default.tsx | 3 - app/api/web/auth/[...nextauth]/route.ts | 2 - app/globals.css | 8 - .../FormContent/formContent.module.css | 12 + .../Forms/Edit/Profile/FormContent/index.tsx | 104 ++++ components/Forms/Edit/Profile/form.module.css | 10 + components/Forms/Edit/Profile/index.tsx | 94 ++++ components/Forms/Edit/Profile/schema.ts | 24 + components/Icons/ChevronDown.tsx | 20 +- components/Icons/InfoCircle.tsx | 40 ++ components/Icons/icon.module.css | 8 +- components/Icons/index.tsx | 1 + components/Icons/variants.ts | 1 + .../DynamicContent/OverviewTable/index.tsx | 2 +- .../Blocks/Points/EarnAndBurn/Client.tsx | 4 +- .../MyPages/Blocks/Stays/Previous/Client.tsx | 12 +- .../MyProfile/Profile/Edit/Form/Content.tsx | 27 - .../Profile/Edit/Form/form.module.css | 5 - .../MyProfile/Profile/Edit/Form/index.tsx | 54 -- .../MyProfile/Profile/Edit/Form/schema.ts | 18 - components/MyProfile/Profile/Edit/index.tsx | 11 - components/Profile/Header/header.module.css | 6 + components/Profile/Header/index.tsx | 5 + .../TempDesignSystem/Button/button.module.css | 470 +++++++++++++----- .../TempDesignSystem/Button/variants.ts | 110 +++- .../CardGrid/cardGrid.module.css | 55 -- .../Form/Country/country.module.css | 61 ++- .../TempDesignSystem/Form/Country/country.ts | 5 + .../TempDesignSystem/Form/Country/index.tsx | 34 +- .../Form/Date/date.module.css | 17 +- components/TempDesignSystem/Form/Date/date.ts | 9 +- .../TempDesignSystem/Form/Date/index.tsx | 10 +- .../Form/ErrorMessage/error.module.css | 7 +- .../Form/ErrorMessage/index.tsx | 8 +- .../TempDesignSystem/Form/Input/index.tsx | 50 +- .../Form/Input/input.module.css | 50 +- .../TempDesignSystem/Form/Input/input.ts | 10 +- .../TempDesignSystem/Form/Label/index.tsx | 20 + .../Form/Label/label.module.css | 30 ++ .../TempDesignSystem/Form/Label/label.ts | 9 + .../TempDesignSystem/Form/Label/variants.ts | 15 + .../TempDesignSystem/Form/Phone/index.tsx | 164 +++--- .../Form/Phone/phone.module.css | 140 +++++- .../TempDesignSystem/Form/Phone/phone.ts | 4 +- .../TempDesignSystem/Form/Select/index.tsx | 103 +--- .../TempDesignSystem/Form/Select/select.ts | 19 +- .../Form/SelectChevron/index.tsx | 2 +- .../TempDesignSystem/Link/link.module.css | 2 +- components/TempDesignSystem/Link/variants.ts | 1 + components/TempDesignSystem/Select/index.tsx | 98 ++++ .../{Form => }/Select/select.module.css | 8 +- components/TempDesignSystem/Select/select.ts | 15 + .../Text/BiroScript/biroScript.module.css | 20 +- .../Text/Body/body.module.css | 3 +- .../Text/Caption/caption.module.css | 3 +- .../Text/Footnote/footnote.module.css | 3 +- .../Text/Subtitle/subtitle.module.css | 4 + .../Text/Subtitle/variants.ts | 1 + .../Text/Title/title.module.css | 6 +- .../TempDesignSystem/Text/Title/variants.ts | 1 + i18n/dictionaries/da.json | 8 +- i18n/dictionaries/de.json | 8 +- i18n/dictionaries/en.json | 10 +- i18n/dictionaries/fi.json | 8 +- i18n/dictionaries/no.json | 8 +- i18n/dictionaries/sv.json | 8 +- server/routers/user/output.ts | 1 + server/routers/user/query.ts | 9 +- stores/edit-profile.ts | 23 - types/components/myPages/myProfile/edit.ts | 6 - 81 files changed, 1538 insertions(+), 711 deletions(-) create mode 100644 app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.module.css create mode 100644 app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.tsx create mode 100644 app/[lang]/(live)/(protected)/my-pages/profile/edit/page.tsx delete mode 100644 app/[lang]/(live)/@languageSwitcher/[...paths]/page.tsx delete mode 100644 app/[lang]/(live)/@languageSwitcher/default.tsx create mode 100644 components/Forms/Edit/Profile/FormContent/formContent.module.css create mode 100644 components/Forms/Edit/Profile/FormContent/index.tsx create mode 100644 components/Forms/Edit/Profile/form.module.css create mode 100644 components/Forms/Edit/Profile/index.tsx create mode 100644 components/Forms/Edit/Profile/schema.ts create mode 100644 components/Icons/InfoCircle.tsx delete mode 100644 components/MyProfile/Profile/Edit/Form/Content.tsx delete mode 100644 components/MyProfile/Profile/Edit/Form/form.module.css delete mode 100644 components/MyProfile/Profile/Edit/Form/index.tsx delete mode 100644 components/MyProfile/Profile/Edit/Form/schema.ts delete mode 100644 components/MyProfile/Profile/Edit/index.tsx create mode 100644 components/Profile/Header/header.module.css create mode 100644 components/Profile/Header/index.tsx delete mode 100644 components/TempDesignSystem/CardGrid/cardGrid.module.css create mode 100644 components/TempDesignSystem/Form/Label/index.tsx create mode 100644 components/TempDesignSystem/Form/Label/label.module.css create mode 100644 components/TempDesignSystem/Form/Label/label.ts create mode 100644 components/TempDesignSystem/Form/Label/variants.ts create mode 100644 components/TempDesignSystem/Select/index.tsx rename components/TempDesignSystem/{Form => }/Select/select.module.css (95%) create mode 100644 components/TempDesignSystem/Select/select.ts delete mode 100644 stores/edit-profile.ts diff --git a/actions/editProfile.ts b/actions/editProfile.ts index 812c9a6e5..b1d10ea60 100644 --- a/actions/editProfile.ts +++ b/actions/editProfile.ts @@ -1,13 +1,13 @@ "use server" -import { editProfileSchema } from "@/components/MyProfile/Profile/Edit/Form/schema" +// import { editProfileSchema } from "@/components/Forms/Edit/Profile/schema" import { ZodError } from "zod" import { type State, Status } from "@/types/components/myPages/myProfile/edit" export async function editProfile(_prevState: State, values: FormData) { try { - const data = editProfileSchema.parse(Object.fromEntries(values.entries())) + const data = Object.fromEntries(values.entries()) /** * TODO: Update profile data when endpoint from diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx index 6798f65ed..e8dbe1d87 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx @@ -11,19 +11,19 @@ export default async function CommunicationSlot() { return (
- + {formatMessage({ id: "My communication preferences" })} - + {formatMessage({ id: "Tell us what information and updates you'd like to receive, and how, by clicking the link below.", })}
- - - {formatMessage({ id: "Add new card" })} + + + {formatMessage({ id: "Manage preferences" })}
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx index c3d256388..9c650c8bc 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx @@ -11,16 +11,18 @@ export default async function CreditCardSlot() { return (
- {formatMessage({ id: "My credit cards" })} - + + {formatMessage({ id: "My credit cards" })} + + {formatMessage({ id: "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.", })}
- - + + {formatMessage({ id: "Add new card" })} diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/default.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/default.tsx index 86b9e9a38..b214932df 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/default.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/default.tsx @@ -1,3 +1,3 @@ -export default function Default() { +export default function DefaultEditProfileSlot() { return null } diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/page.tsx index 87dd80061..f9977baf0 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/edit/page.tsx @@ -1,5 +1,11 @@ -import EditProfile from "@/components/MyProfile/Profile/Edit" +import { serverClient } from "@/lib/trpc/server" -export default function EditProfileSlot() { - return +import Form from "@/components/Forms/Edit/Profile" + +export default async function EditProfileSlot() { + const user = await serverClient().user.get({ mask: false }) + if (!user) { + return null + } + return
} diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.module.css b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.module.css new file mode 100644 index 000000000..5b48eefba --- /dev/null +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.module.css @@ -0,0 +1,7 @@ +.container { + background-color: var(--Scandic-Brand-Pale-Peach); + border-radius: var(--Corner-radius-Large); + display: grid; + gap: var(--Spacing-x3); + padding: var(--Spacing-x3); +} diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.tsx new file mode 100644 index 000000000..90c657db2 --- /dev/null +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/layout.tsx @@ -0,0 +1,7 @@ +import styles from "./layout.module.css" + +export default function ProfileSlotLayout({ + children, +}: React.PropsWithChildren) { + return
{children}
+} diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.module.css b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.module.css index 9f0ac135e..02313e66b 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.module.css +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.module.css @@ -1,22 +1,3 @@ -.container { - background-color: var(--Scandic-Brand-Warm-White); - border-radius: var(--Corner-radius-Large); - display: grid; - gap: var(--Spacing-x3); - padding: var(--Spacing-x3); -} - -.header { - align-items: center; - display: flex; - gap: var(--Spacing-x2); - justify-content: space-between; -} - -button.btn { - border: 1px solid var(--Base-Border-Subtle); -} - .profile { display: flex; gap: var(--Spacing-x2); diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.tsx index 620da9bc8..bd87d0469 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@profile/page.tsx @@ -1,4 +1,4 @@ -// import { dt } from "@/lib/dt" +import { profileEdit } from "@/constants/routes/myPages" import { serverClient } from "@/lib/trpc/server" import { @@ -8,36 +8,41 @@ import { LockIcon, PhoneIcon, } from "@/components/Icons" +import Header from "@/components/Profile/Header" import Button from "@/components/TempDesignSystem/Button" import Divider from "@/components/TempDesignSystem/Divider" +import Link from "@/components/TempDesignSystem/Link" import Body from "@/components/TempDesignSystem/Text/Body" import Title from "@/components/TempDesignSystem/Text/Title" import { getIntl } from "@/i18n" import styles from "./page.module.css" -export default async function Profile() { +import type { LangParams, PageArgs } from "@/types/params" + +export default async function Profile({ params }: PageArgs) { const { formatMessage } = await getIntl() const user = await serverClient().user.get() if (!user) { return null } - // const dob = dt(user.dateOfBirth).format("DD/MM/YYYY") return ( -
-
+ <> +
- - {user.name} + <Title as="h4" color="peach80" level="h1"> + {formatMessage({ id: "Welcome" })} - {user.dateOfBirth} + {user.name}
- -
+
@@ -91,6 +96,6 @@ export default async function Profile() {
-
+ ) } diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/edit/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/edit/page.tsx new file mode 100644 index 000000000..496477ced --- /dev/null +++ b/app/[lang]/(live)/(protected)/my-pages/profile/edit/page.tsx @@ -0,0 +1,5 @@ +import "../profileLayout.css" + +export default function EditProfilePage() { + return null +} diff --git a/app/[lang]/(live)/@languageSwitcher/[...paths]/page.tsx b/app/[lang]/(live)/@languageSwitcher/[...paths]/page.tsx deleted file mode 100644 index e68f13082..000000000 --- a/app/[lang]/(live)/@languageSwitcher/[...paths]/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { serverClient } from "@/lib/trpc/server" - -import Desktop from "@/components/Current/Header/LanguageSwitcher/Desktop" -import Mobile from "@/components/Current/Header/LanguageSwitcher/Mobile" - -export default async function LanguageSwitcher() { - const data = await serverClient().contentstack.languageSwitcher.get() - if (!data) { - return null - } - return ( - <> - - - - ) -} diff --git a/app/[lang]/(live)/@languageSwitcher/default.tsx b/app/[lang]/(live)/@languageSwitcher/default.tsx deleted file mode 100644 index f41067bc5..000000000 --- a/app/[lang]/(live)/@languageSwitcher/default.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function DefaultLanguageSwitcher() { - return null -} diff --git a/app/api/web/auth/[...nextauth]/route.ts b/app/api/web/auth/[...nextauth]/route.ts index c82d0773c..b44a389af 100644 --- a/app/api/web/auth/[...nextauth]/route.ts +++ b/app/api/web/auth/[...nextauth]/route.ts @@ -1,3 +1 @@ export { GET, POST } from "@/auth" - -export const runtime = "edge" diff --git a/app/globals.css b/app/globals.css index b234c821d..99216e464 100644 --- a/app/globals.css +++ b/app/globals.css @@ -72,14 +72,6 @@ src: url(/_static/fonts/fira-sans/medium.woff2) format("woff2"); } -@font-face { - font-display: swap; - font-family: "fira sans"; - font-style: normal; - font-weight: 500; - src: url(/_static/fonts/fira-sans/Medium.woff2) format("woff2"); -} - @font-face { font-display: swap; font-family: "fira sans"; diff --git a/components/Forms/Edit/Profile/FormContent/formContent.module.css b/components/Forms/Edit/Profile/FormContent/formContent.module.css new file mode 100644 index 000000000..3400070a0 --- /dev/null +++ b/components/Forms/Edit/Profile/FormContent/formContent.module.css @@ -0,0 +1,12 @@ +.password, +.user { + align-self: flex-start; + display: grid; + gap: var(--Spacing-x2); +} + +.container { + display: grid; + gap: var(--Spacing-x2); + grid-template-columns: max(164px) 1fr; +} diff --git a/components/Forms/Edit/Profile/FormContent/index.tsx b/components/Forms/Edit/Profile/FormContent/index.tsx new file mode 100644 index 000000000..75f25cc36 --- /dev/null +++ b/components/Forms/Edit/Profile/FormContent/index.tsx @@ -0,0 +1,104 @@ +"use client" +// import { useFormStatus } from "react-dom" +import { useIntl } from "react-intl" + +import CountrySelect from "@/components/TempDesignSystem/Form/Country" +import DateSelect from "@/components/TempDesignSystem/Form/Date" +import Input from "@/components/TempDesignSystem/Form/Input" +import Phone from "@/components/TempDesignSystem/Form/Phone" +import Select from "@/components/TempDesignSystem/Form/Select" +import Body from "@/components/TempDesignSystem/Text/Body" + +import styles from "./formContent.module.css" + +const languages = [ + { label: "Danish", value: "Da" }, + { label: "German", value: "De" }, + { label: "English", value: "En" }, + { label: "Finnish", value: "Fi" }, + { label: "Norwegian", value: "No" }, + { label: "Swedish", value: "Sv" }, +] + +export default function FormContent() { + const { formatMessage } = useIntl() + // const { pending } = useFormStatus() + + const city = formatMessage({ id: "City" }) + const country = formatMessage({ id: "Country" }) + const email = `${formatMessage({ id: "Email" })} ${formatMessage({ id: "Address" }).toLowerCase()}` + const street = formatMessage({ id: "Address" }) + const password = formatMessage({ id: "Current password" }) + const newPassword = formatMessage({ id: "New password" }) + const retypeNewPassword = formatMessage({ id: "Retype new password" }) + const zipCode = formatMessage({ id: "Zip code" }) + + return ( + <> +
+
+ + {formatMessage({ id: "User information" })} + +
+ + + +
+ + +
+ + + + + +
+ + ) +} diff --git a/components/Forms/Edit/Profile/form.module.css b/components/Forms/Edit/Profile/form.module.css new file mode 100644 index 000000000..e15f104ab --- /dev/null +++ b/components/Forms/Edit/Profile/form.module.css @@ -0,0 +1,10 @@ +.form { + display: grid; + gap: var(--Spacing-x5); + grid-template-columns: 1fr 1fr; +} + +.btns { + display: flex; + gap: var(--Spacing-x-half); +} diff --git a/components/Forms/Edit/Profile/index.tsx b/components/Forms/Edit/Profile/index.tsx new file mode 100644 index 000000000..f0b213da0 --- /dev/null +++ b/components/Forms/Edit/Profile/index.tsx @@ -0,0 +1,94 @@ +"use client" +import { zodResolver } from "@hookform/resolvers/zod" +import { useFormState as useReactFormState } from "react-dom" +import { FormProvider, useForm } from "react-hook-form" +import { useIntl } from "react-intl" + +import { editProfile } from "@/actions/editProfile" +import Header from "@/components/Profile/Header" +import Button from "@/components/TempDesignSystem/Button" +import Divider from "@/components/TempDesignSystem/Divider" +import Title from "@/components/TempDesignSystem/Text/Title" + +import FormContent from "./FormContent" +import { type EditProfileSchema, editProfileSchema } from "./schema" + +import styles from "./form.module.css" + +import type { + EditFormProps, + State, +} from "@/types/components/myPages/myProfile/edit" + +const formId = "edit-profile" + +export default function Form({ user }: EditFormProps) { + const { formatMessage } = useIntl() + /** + * like react, react-hook-form also exports a useFormState hook, + * we want to clearly keep them separate by naming. + */ + const [state, formAction] = useReactFormState( + editProfile, + null + ) + + const form = useForm({ + defaultValues: { + "address.city": user.address.city ?? "", + "address.countryCode": user.address.countryCode ?? "", + "address.streetAddress": user.address.streetAddress ?? "", + "address.zipCode": user.address.zipCode ?? "", + dateOfBirth: user.dateOfBirth, + email: user.email, + language: user.language, + phoneNumber: user.phoneNumber, + currentPassword: "", + newPassword: "", + retypeNewPassword: "", + }, + mode: "all", + resolver: zodResolver(editProfileSchema), + reValidateMode: "onChange", + }) + + return ( + +
+
+ + {formatMessage({ id: "Edit" })} + + + {user.name} + +
+
+ + +
+
+ + + + +
+ ) +} diff --git a/components/Forms/Edit/Profile/schema.ts b/components/Forms/Edit/Profile/schema.ts new file mode 100644 index 000000000..6c5fe9e2b --- /dev/null +++ b/components/Forms/Edit/Profile/schema.ts @@ -0,0 +1,24 @@ +import { z } from "zod" + +// import { phoneValidator } from "@/utils/phoneValidator" + +export const editProfileSchema = z.object({ + "address.city": z.string().optional(), + "address.countryCode": z.string().min(1), + "address.streetAddress": z.string().optional(), + "address.zipCode": z.string().min(1), + dateOfBirth: z.string().min(1), + email: z.string().email(), + language: z.string(), + phoneNumber: z.string(), + // phoneValidator( + // "Phone is required", + // "Please enter a valid phone number" + // ), + + currentPassword: z.string().optional(), + newPassword: z.string().optional(), + retypeNewPassword: z.string().optional(), +}) + +export type EditProfileSchema = z.infer diff --git a/components/Icons/ChevronDown.tsx b/components/Icons/ChevronDown.tsx index b9ce5544e..1bf541d43 100644 --- a/components/Icons/ChevronDown.tsx +++ b/components/Icons/ChevronDown.tsx @@ -12,27 +12,27 @@ export default function ChevronDownIcon({ - + - + diff --git a/components/Icons/InfoCircle.tsx b/components/Icons/InfoCircle.tsx new file mode 100644 index 000000000..f87f5d663 --- /dev/null +++ b/components/Icons/InfoCircle.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function InfoCircleIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/icon.module.css b/components/Icons/icon.module.css index a13e28e21..58c5db266 100644 --- a/components/Icons/icon.module.css +++ b/components/Icons/icon.module.css @@ -1,6 +1,5 @@ .icon { - height: 20px; - width: 20px; + margin: 0; } .black, @@ -13,6 +12,11 @@ fill: var(--Scandic-Brand-Burgundy); } +.grey80, +.grey80 * { + fill: var(--UI-Grey-80); +} + .pale, .pale * { fill: var(--Scandic-Brand-Pale-Peach); diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx index 0448d8ffc..686798aff 100644 --- a/components/Icons/index.tsx +++ b/components/Icons/index.tsx @@ -9,6 +9,7 @@ export { default as ChevronRightIcon } from "./ChevronRight" export { default as EmailIcon } from "./Email" export { default as GlobeIcon } from "./Globe" export { default as HouseIcon } from "./House" +export { default as InfoCircleIcon } from "./InfoCircle" export { default as LocationIcon } from "./Location" export { default as LockIcon } from "./Lock" export { default as PhoneIcon } from "./Phone" diff --git a/components/Icons/variants.ts b/components/Icons/variants.ts index 173d9bfd8..a8534eb20 100644 --- a/components/Icons/variants.ts +++ b/components/Icons/variants.ts @@ -7,6 +7,7 @@ const config = { color: { black: styles.black, burgundy: styles.burgundy, + grey80: styles.grey80, pale: styles.pale, peach80: styles.peach80, primaryLightOnSurfaceAccent: styles.plosa, diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx index de46ee3d5..7a8dc9df9 100644 --- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx @@ -8,7 +8,7 @@ import { Lang } from "@/constants/languages" import { membershipLevels } from "@/constants/membershipLevels" import Image from "@/components/Image" -import Select from "@/components/TempDesignSystem/Form/Select" +import Select from "@/components/TempDesignSystem/Select" import { getMembership } from "@/utils/user" import levelsData from "./data/EN.json" diff --git a/components/MyPages/Blocks/Points/EarnAndBurn/Client.tsx b/components/MyPages/Blocks/Points/EarnAndBurn/Client.tsx index 494357be6..3041bff55 100644 --- a/components/MyPages/Blocks/Points/EarnAndBurn/Client.tsx +++ b/components/MyPages/Blocks/Points/EarnAndBurn/Client.tsx @@ -7,7 +7,7 @@ import MobileTable from "./Mobile" import type { ClientEarnAndBurnProps, - TransactionsObject, + TransactionsNonNullResponseObject, } from "@/types/components/myPages/myPage/earnAndBurn" export default function ClientEarnAndBurn({ @@ -34,7 +34,7 @@ export default function ClientEarnAndBurn({ // later on when we handle errors appropriately. const filteredTransactions = (data?.pages.filter( (page) => page && page.data - ) ?? []) as unknown as TransactionsObject[] + ) ?? []) as unknown as TransactionsNonNullResponseObject[] const transactions = filteredTransactions.flatMap((page) => page.data) return ( <> diff --git a/components/MyPages/Blocks/Stays/Previous/Client.tsx b/components/MyPages/Blocks/Stays/Previous/Client.tsx index 156bc2e35..11f919fcd 100644 --- a/components/MyPages/Blocks/Stays/Previous/Client.tsx +++ b/components/MyPages/Blocks/Stays/Previous/Client.tsx @@ -56,13 +56,11 @@ export default function ClientPreviousStays({ stay={stay} /> ))} - - { - hasNextPage ? ( - - ) : null - } - + + {hasNextPage ? ( + + ) : null} + ) : ( ) diff --git a/components/MyProfile/Profile/Edit/Form/Content.tsx b/components/MyProfile/Profile/Edit/Form/Content.tsx deleted file mode 100644 index 46a5bda23..000000000 --- a/components/MyProfile/Profile/Edit/Form/Content.tsx +++ /dev/null @@ -1,27 +0,0 @@ -"use client" -import { useEffect } from "react" -import { useFormStatus } from "react-dom" - -// import { useWatch } from "react-hook-form" -// import { useIntl } from "react-intl" -import { useProfileStore } from "@/stores/edit-profile" - -import type { EditFormContentProps } from "@/types/components/myPages/myProfile/edit" - -export default function FormContent({ control }: EditFormContentProps) { - // const { formatMessage } = useIntl() - const { pending } = useFormStatus() - const setIsPending = useProfileStore((store) => store.setIsPending) - // const country = useWatch({ name: "address.country" }) - - useEffect(() => { - setIsPending(pending) - }, [pending, setIsPending]) - - // const city = formatMessage({ id: "City" }) - // const email = formatMessage({ id: "Email" }) - // const street = formatMessage({ id: "Street" }) - // const zipCode = formatMessage({ id: "Zip code" }) - - return <> -} diff --git a/components/MyProfile/Profile/Edit/Form/form.module.css b/components/MyProfile/Profile/Edit/Form/form.module.css deleted file mode 100644 index ee74df2f3..000000000 --- a/components/MyProfile/Profile/Edit/Form/form.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.form { - display: grid; - gap: var(--Spacing-x2); - grid-template-columns: 1fr 1fr; -} diff --git a/components/MyProfile/Profile/Edit/Form/index.tsx b/components/MyProfile/Profile/Edit/Form/index.tsx deleted file mode 100644 index 82d41b56c..000000000 --- a/components/MyProfile/Profile/Edit/Form/index.tsx +++ /dev/null @@ -1,54 +0,0 @@ -"use client" -import { zodResolver } from "@hookform/resolvers/zod" -import { useEffect } from "react" -import { useFormState as useReactFormState } from "react-dom" -import { FormProvider, useForm } from "react-hook-form" - -import { useProfileStore } from "@/stores/edit-profile" - -import { editProfile } from "@/actions/editProfile" - -import FormContent from "./Content" -import { type EditProfileSchema, editProfileSchema } from "./schema" - -import styles from "./form.module.css" - -import { - type EditFormProps, - type State, -} from "@/types/components/myPages/myProfile/edit" - -export default function Form({ user }: EditFormProps) { - const isValid = useProfileStore((store) => store.valid) - const setValid = useProfileStore((store) => store.setValid) - /** - * like react, react-hook-form also exports a useFormState hook, - * we want to clearly keep them separate by naming. - */ - const [state, formAction] = useReactFormState( - editProfile, - null - ) - - const form = useForm({ - defaultValues: user, - criteriaMode: "all", - mode: "onTouched", - resolver: zodResolver(editProfileSchema), - reValidateMode: "onChange", - }) - - useEffect(() => { - if (isValid !== form.formState.isValid) { - setValid(form.formState.isValid) - } - }, [form.formState.isValid, isValid, setValid]) - - return ( - -
- - -
- ) -} diff --git a/components/MyProfile/Profile/Edit/Form/schema.ts b/components/MyProfile/Profile/Edit/Form/schema.ts deleted file mode 100644 index 4648c31af..000000000 --- a/components/MyProfile/Profile/Edit/Form/schema.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod" - -import { phoneValidator } from "@/utils/phoneValidator" - -export const editProfileSchema = z.object({ - "address.country": z.string().min(1), - "address.city": z.string().optional(), - "address.streetAddress": z.string().optional(), - "address.zipCode": z.string().min(1), - dateOfBirth: z.string().min(1), - email: z.string().email(), - phoneNumber: phoneValidator( - "Phone is required", - "Please enter a valid phone number" - ), -}) - -export type EditProfileSchema = z.infer diff --git a/components/MyProfile/Profile/Edit/index.tsx b/components/MyProfile/Profile/Edit/index.tsx deleted file mode 100644 index 4d8a3cf07..000000000 --- a/components/MyProfile/Profile/Edit/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { serverClient } from "@/lib/trpc/server" - -import Form from "./Form" - -export default async function EditProfile() { - const user = await serverClient().user.get() - if (!user) { - return null - } - return
-} diff --git a/components/Profile/Header/header.module.css b/components/Profile/Header/header.module.css new file mode 100644 index 000000000..ea008fc93 --- /dev/null +++ b/components/Profile/Header/header.module.css @@ -0,0 +1,6 @@ +.header { + align-items: center; + display: flex; + gap: var(--Spacing-x2); + justify-content: space-between; +} diff --git a/components/Profile/Header/index.tsx b/components/Profile/Header/index.tsx new file mode 100644 index 000000000..c74b833e2 --- /dev/null +++ b/components/Profile/Header/index.tsx @@ -0,0 +1,5 @@ +import styles from "./header.module.css" + +export default function Header({ children }: React.PropsWithChildren) { + return
{children}
+} diff --git a/components/TempDesignSystem/Button/button.module.css b/components/TempDesignSystem/Button/button.module.css index 0cc4fb920..0de3dbc29 100644 --- a/components/TempDesignSystem/Button/button.module.css +++ b/components/TempDesignSystem/Button/button.module.css @@ -1,167 +1,411 @@ .btn { background: none; - border: none; + /* No variable yet for radius 50px */ + border-radius: 50px; cursor: pointer; - /* TODO: Waiting for variables for buttons from Design team */ - font-family: var(--typography-Body-Regular-fontFamily); - font-weight: 600; - line-height: 150%; - letter-spacing: 1%; margin: 0; padding: 0; text-align: center; + transition: + background-color 300ms ease, + color 300ms ease; + + /* TODO: Waiting for variables for buttons from Design team */ + font-family: var(--typography-Body-Bold-fontFamily); + font-size: var(--typography-Body-Bold-fontSize); + font-weight: var(--typography-Body-Bold-fontWeight); + line-height: 24px; + letter-spacing: 0.6%; } +/* INTENT */ .primary, a.primary { - background-color: var(--background-color); - color: var(--font-color); -} - -.primary:hover, -a.primary:hover, -.primary:active, -a.primary:active, -.primary:focus, -a.primary:focus { - background-color: var(--hover-background); - color: var(--hover-color); + border: none; } .secondary, a.secondary { - background-color: transparent; - border: 1px solid var(--background-color); - color: var(--background-color); + background: none; + border-style: solid; + border-width: 2px; } -.secondary:hover, -a.secondary:hover, -.secondary:active, -a.secondary:active, -.secondary:focus, -a.secondary:focus { - border: 1px solid var(--hover-color); - color: var(--hover-color); +.tertiary, +a.tertiary { + border: none; } +.inverted, +a.inverted { + border: none; +} + +/* VARIANTS */ .default, a.default { align-items: center; - border-radius: 50px; - /* No variable yet */ display: flex; gap: var(--Spacing-x1); } .icon { align-items: baseline; - font-size: 18px; } -/* Disabled styles */ +/* SIZES */ +.small { + gap: var(--Spacing-x-quarter); + height: 40px; + padding: var(--Spacing-x1) var(--Spacing-x2); +} + +.medium { + gap: var(--Spacing-x-quarter); + height: 48px; + padding: var(--Spacing-x-one-and-half) var(--Spacing-x2); +} + +.large { + gap: var(--Spacing-x-half); + height: 56px; + padding: var(--Spacing-x2) var(--Spacing-x3); +} + +/* DISABLED */ .btn:disabled { background-color: var(--disabled-background-color); color: var(--disabled-color); cursor: not-allowed; } -/* Sizes */ -.small { - font-size: 14px; - gap: var(--Spacing-x2); - height: 40px; - padding: var(--Spacing-x1) var(--Spacing-x2); +/* THEMES */ +.basePrimary { + background-color: var(--Base-Button-Primary-Fill-Normal); + color: var(--Base-Button-Primary-On-Fill-Normal); } -.medium { - font-size: 16px; - height: 30px; - padding: var(--Spacing-x-one-and-half) var(--Spacing-x2); +.basePrimary:active, +.basePrimary:focus, +.basePrimary:hover { + background-color: var(--Base-Button-Primary-Fill-Hover); + color: var(--Base-Button-Primary-On-Fill-Hover); } -.large { - font-size: 16px; - height: 50px; - gap: var(--Spacing-x-half); - padding: var(--Spacing-x2) var(--Spacing-x3); +.basePrimary:disabled { + background-color: var(--Base-Button-Primary-Fill-Disabled); + color: var(--Base-Button-Primary-On-Fill-Disabled); } -.primaryLight { - --font-color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Normal); - --background-color: var(--Theme-Primary-Light-Button-Primary-Fill-Normal); - --hover-background: var(--Theme-Primary-Light-Button-Primary-Fill-Hover); - --hover-color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Hover); - --disabled-background-color: var( - --Theme-Primary-Light-Button-Primary-Fill-Disabled - ); - --disabled-color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Disabled); +.baseSecondary { + background-color: var(--Base-Button-Secondary-Fill-Normal); + border-color: var(--Base-Button-Secondary-Border-Normal); + color: var(--Base-Button-Secondary-On-Fill-Normal); } -.primaryDark { - --font-color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Normal); - --background-color: var(--Theme-Primary-Dark-Button-Primary-Fill-Normal); - --hover-color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Hover); - --hover-background: var(--Theme-Primary-Dark-Button-Primary-Fill-Hover); - --disabled-background-color: var( - --Theme-Primary-Dark-Button-Primary-Fill-Disabled - ); - --disabled-color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Disabled); +.baseSecondary:active, +.baseSecondary:focus, +.baseSecondary:hover { + background-color: var(--Base-Button-Secondary-Fill-Hover); + border-color: var(--Base-Button-Secondary-Border-Hover); + color: var(--Base-Button-Secondary-On-Fill-Hover); } -.primaryStrong { - --background-color: var(--Theme-Primary-Strong-Button-Primary-Fill-Normal); - --disabled-background-color: var( - --Theme-Primary-Strong-Button-Primary-Fill-Disabled - ); - --disabled-color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Disabled); - --font-color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Normal); - --hover-background: var(--Theme-Primary-Strong-Button-Primary-Fill-Hover); - --hover-color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Hover); +.baseSecondary:disabled { + background-color: var(--Base-Button-Secondary-Fill-Disabled); + border-color: var(--Base-Button-Secondary-Border-Disabled); + color: var(--Base-Button-Secondary-On-Fill-Disabled); } -.secondaryLight { - --font-color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Normal); - --background-color: var(--Theme-Secondary-Light-Button-Primary-Fill-Normal); - --hover-color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Hover); - --hover-background: var(--Theme-Secondary-Light-Button-Primary-Fill-Hover); - --disabled-background-color: var( - --Theme-Secondary-Light-Button-Primary-Fill-Disabled - ); - --disabled-color: var( - --Theme-Secondary-Light-Button-Primary-On-Fill-Disabled - ); +.baseTertiary { + background-color: var(--Base-Button-Tertiary-Fill-Normal); + color: var(--Base-Button-Tertiary-On-Fill-Normal); } -.secondaryDark { - --font-color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Normal); - --background-color: var(--Theme-Secondary-Dark-Button-Primary-Fill-Normal); - --hover-color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Hover); - --hover-background: var(--Theme-Secondary-Dark-Button-Primary-Fill-Hover); - --disabled-background-color: var( - --Theme-Secondary-Dark-Button-Primary-Fill-Disabled - ); - --disabled-color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Disabled); +.baseTertiary:active, +.baseTertiary:focus, +.baseTertiary:hover { + background-color: var(--Base-Button-Tertiary-Fill-Hover); + color: var(--Base-Button-Tertiary-On-Fill-Hover); } -.tertiaryLight { - --font-color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Normal); - --background-color: var(--Theme-Tertiary-Light-Button-Primary-Fill-Normal); - --hover-color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Hover); - --hover-background: var(--Theme-Tertiary-Light-Button-Primary-Fill-Hover); - --disabled-background-color: var( - --Theme-Tertiary-Light-Button-Primary-Fill-Disabled - ); - --disabled-color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Disabled); +.baseTertiary:disabled { + background-color: var(--Base-Button-Tertiary-Fill-Disabled); + color: var(--Base-Button-Tertiary-On-Fill-Disabled); } -.tertiaryDark { - --font-color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Normal); - --background-color: var(--Theme-Tertiary-Dark-Button-Primary-Fill-Normal); - --hover-color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Hover); - --hover-background: var(--Theme-Tertiary-Dark-Button-Primary-Fill-Hover); - --disabled-background-color: var( - --Theme-Tertiary-Dark-Button-Primary-Fill-Disabled - ); - --disabled-color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Disabled); +.baseInverted { + background-color: var(--Base-Button-Inverted-Fill-Normal); + color: var(--Base-Button-Inverted-On-Fill-Normal); +} + +.baseInverted:active, +.baseInverted:focus, +.baseInverted:hover { + background-color: var(--Base-Button-Inverted-Fill-Hover); + color: var(--Base-Button-Inverted-On-Fill-Hover); +} + +.baseInverted:disabled { + background-color: var(--Base-Button-Inverted-Fill-Disabled); + color: var(--Base-Button-Inverted-On-Fill-Disabled); +} + +.primaryStrongPrimary { + background-color: var(--Theme-Primary-Strong-Button-Primary-Fill-Normal); + color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Normal); +} + +.primaryStrongPrimary:active, +.primaryStrongPrimary:focus, +.primaryStrongPrimary:hover { + background-color: var(--Theme-Primary-Strong-Button-Primary-Fill-Hover); + color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Hover); +} + +.primaryStrongPrimary:disabled { + background-color: var(--Theme-Primary-Strong-Button-Primary-Fill-Disabled); + color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Disabled); +} + +.primaryStrongSecondary { + background-color: var(--Theme-Primary-Strong-Button-Secondary-Fill-Normal); + border-color: var(--Theme-Primary-Strong-Button-Secondary-Border-Normal); + color: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Normal); +} + +.primaryStrongSecondary:active, +.primaryStrongSecondary:focus, +.primaryStrongSecondary:hover { + background-color: var(--Theme-Primary-Strong-Button-Secondary-Fill-Hover); + border-color: var(--Theme-Primary-Strong-Button-Secondary-Border-Hover); + color: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Hover); +} + +.primaryStrongSecondary:disabled { + background-color: var(--Theme-Primary-Strong-Button-Secondary-Fill-Disabled); + border-color: var(--Theme-Primary-Strong-Button-Secondary-Border-Disabled); + color: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Disabled); +} + +.primaryDarkPrimary { + background-color: var(--Theme-Primary-Dark-Button-Primary-Fill-Normal); + color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Normal); +} + +.primaryDarkPrimary:active, +.primaryDarkPrimary:focus, +.primaryDarkPrimary:hover { + background-color: var(--Theme-Primary-Dark-Button-Primary-Fill-Hover); + color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Hover); +} + +.primaryDarkPrimary:disabled { + background-color: var(--Theme-Primary-Dark-Button-Primary-Fill-Disabled); + color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Disabled); +} + +.primaryDarkSecondary { + background-color: var(--Theme-Primary-Dark-Button-Secondary-Fill-Normal); + border-color: var(--Theme-Primary-Dark-Button-Secondary-Border-Normal); + color: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Normal); +} + +.primaryDarkSecondary:active, +.primaryDarkSecondary:focus, +.primaryDarkSecondary:hover { + background-color: var(--Theme-Primary-Dark-Button-Secondary-Fill-Hover); + border-color: var(--Theme-Primary-Dark-Button-Secondary-Border-Hover); + color: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Hover); +} + +.primaryDarkSecondary:disabled { + background-color: var(--Theme-Primary-Dark-Button-Secondary-Fill-Disabled); + border-color: var(--Theme-Primary-Dark-Button-Secondary-Border-Disabled); + color: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Disabled); +} + +.primaryLightPrimary { + background-color: var(--Theme-Primary-Light-Button-Primary-Fill-Normal); + color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Normal); +} + +.primaryLightPrimary:active, +.primaryLightPrimary:focus, +.primaryLightPrimary:hover { + background-color: var(--Theme-Primary-Light-Button-Primary-Fill-Hover); + color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Hover); +} + +.primaryLightPrimary:disabled { + background-color: var(--Theme-Primary-Light-Button-Primary-Fill-Disabled); + color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Disabled); +} + +.primaryLightSecondary { + background-color: var(--Theme-Primary-Light-Button-Secondary-Fill-Normal); + border-color: var(--Theme-Primary-Light-Button-Secondary-Border-Normal); + color: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Normal); +} + +.primaryLightSecondary:active, +.primaryLightSecondary:focus, +.primaryLightSecondary:hover { + background-color: var(--Theme-Primary-Light-Button-Secondary-Fill-Hover); + border-color: var(--Theme-Primary-Light-Button-Secondary-Border-Hover); + color: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Hover); +} + +.primaryLightSecondary:disabled { + background-color: var(--Theme-Primary-Light-Button-Secondary-Fill-Disabled); + border-color: var(--Theme-Primary-Light-Button-Secondary-Border-Disabled); + color: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Disabled); +} + +.secondaryDarkPrimary { + background-color: var(--Theme-Secondary-Dark-Button-Primary-Fill-Normal); + color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Normal); +} + +.secondaryDarkPrimary:active, +.secondaryDarkPrimary:focus, +.secondaryDarkPrimary:hover { + background-color: var(--Theme-Secondary-Dark-Button-Primary-Fill-Hover); + color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Hover); +} + +.secondaryDarkPrimary:disabled { + background-color: var(--Theme-Secondary-Dark-Button-Primary-Fill-Disabled); + color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Disabled); +} + +.secondaryDarkSecondary { + background-color: var(--Theme-Secondary-Dark-Button-Secondary-Fill-Normal); + border-color: var(--Theme-Secondary-Dark-Button-Secondary-Border-Normal); + color: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Normal); +} + +.secondaryDarkSecondary:active, +.secondaryDarkSecondary:focus, +.secondaryDarkSecondary:hover { + background-color: var(--Theme-Secondary-Dark-Button-Secondary-Fill-Hover); + border-color: var(--Theme-Secondary-Dark-Button-Secondary-Border-Hover); + color: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Hover); +} + +.secondaryDarkSecondary:disabled { + background-color: var(--Theme-Secondary-Dark-Button-Secondary-Fill-Disabled); + border-color: var(--Theme-Secondary-Dark-Button-Secondary-Border-Disabled); + color: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Disabled); +} + +.secondaryLightPrimary { + background-color: var(--Theme-Secondary-Light-Button-Primary-Fill-Normal); + color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Normal); +} + +.secondaryLightPrimary:active, +.secondaryLightPrimary:focus, +.secondaryLightPrimary:hover { + background-color: var(--Theme-Secondary-Light-Button-Primary-Fill-Hover); + color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Hover); +} + +.secondaryLightPrimary:disabled { + background-color: var(--Theme-Secondary-Light-Button-Primary-Fill-Disabled); + color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Disabled); +} + +.secondaryLightSecondary { + background-color: var(--Theme-Secondary-Light-Button-Secondary-Fill-Normal); + border-color: var(--Theme-Secondary-Light-Button-Secondary-Border-Normal); + color: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Normal); +} + +.secondaryLightSecondary:active, +.secondaryLightSecondary:focus, +.secondaryLightSecondary:hover { + background-color: var(--Theme-Secondary-Light-Button-Secondary-Fill-Hover); + border-color: var(--Theme-Secondary-Light-Button-Secondary-Border-Hover); + color: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Hover); +} + +.secondaryLightSecondary:disabled { + background-color: var(--Theme-Secondary-Light-Button-Secondary-Fill-Disabled); + border-color: var(--Theme-Secondary-Light-Button-Secondary-Border-Disabled); + color: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Disabled); +} + +.tertiaryDarkPrimary { + background-color: var(--Theme-Tertiary-Dark-Button-Primary-Fill-Normal); + color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Normal); +} + +.tertiaryDarkPrimary:active, +.tertiaryDarkPrimary:focus, +.tertiaryDarkPrimary:hover { + background-color: var(--Theme-Tertiary-Dark-Button-Primary-Fill-Hover); + color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Hover); +} + +.tertiaryDarkPrimary:disabled { + background-color: var(--Theme-Tertiary-Dark-Button-Primary-Fill-Disabled); + color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Disabled); +} + +.tertiaryDarkSecondary { + background-color: var(--Theme-Tertiary-Dark-Button-Secondary-Fill-Normal); + border-color: var(--Theme-Tertiary-Dark-Button-Secondary-Border-Normal); + color: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Normal); +} + +.tertiaryDarkSecondary:active, +.tertiaryDarkSecondary:focus, +.tertiaryDarkSecondary:hover { + background-color: var(--Theme-Tertiary-Dark-Button-Secondary-Fill-Hover); + border-color: var(--Theme-Tertiary-Dark-Button-Secondary-Border-Hover); + color: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Hover); +} + +.tertiaryDarkSecondary:disabled { + background-color: var(--Theme-Tertiary-Dark-Button-Secondary-Fill-Disabled); + border-color: var(--Theme-Tertiary-Dark-Button-Secondary-Border-Disabled); + color: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Disabled); +} + +.tertiaryLightPrimary { + background-color: var(--Theme-Tertiary-Light-Button-Primary-Fill-Normal); + color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Normal); +} + +.tertiaryLightPrimary:active, +.tertiaryLightPrimary:focus, +.tertiaryLightPrimary:hover { + background-color: var(--Theme-Tertiary-Light-Button-Primary-Fill-Hover); + color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Hover); +} + +.tertiaryLightPrimary:disabled { + background-color: var(--Theme-Tertiary-Light-Button-Primary-Fill-Disabled); + color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Disabled); +} + +.tertiaryLightSecondary { + background-color: var(--Tertiary-Light-Button-Secondary-Fill-Normal); + border-color: var(--Tertiary-Light-Button-Secondary-Border-Normal); + color: var(--Tertiary-Light-Button-Secondary-On-Fill-Normal); +} + +.tertiaryLightSecondary:active, +.tertiaryLightSecondary:focus, +.tertiaryLightSecondary:hover { + background-color: var(--Tertiary-Light-Button-Secondary-Fill-Hover); + border-color: var(--Tertiary-Light-Button-Secondary-Border-Hover); + color: var(--Tertiary-Light-Button-Secondary-On-Fill-Hover); +} + +.tertiaryLightSecondary:disabled { + background-color: var(--Tertiary-Light-Button-Secondary-Fill-Disabled); + border-color: var(--Tertiary-Light-Button-Secondary-Border-Disabled); + color: var(--Tertiary-Light-Button-Secondary-On-Fill-Disabled); } diff --git a/components/TempDesignSystem/Button/variants.ts b/components/TempDesignSystem/Button/variants.ts index f6f6e4d8a..71757fcfe 100644 --- a/components/TempDesignSystem/Button/variants.ts +++ b/components/TempDesignSystem/Button/variants.ts @@ -5,8 +5,10 @@ import styles from "./button.module.css" export const buttonVariants = cva(styles.btn, { variants: { intent: { + inverted: styles.inverted, primary: styles.primary, secondary: styles.secondary, + tertiary: styles.tertiary, }, size: { small: styles.small, @@ -14,13 +16,14 @@ export const buttonVariants = cva(styles.btn, { large: styles.large, }, theme: { - primaryDark: styles.primaryDark, - primaryLight: styles.primaryLight, - primaryStrong: styles.primaryStrong, - secondaryLight: styles.secondaryLight, - secondaryDark: styles.secondaryDark, - tertiaryLight: styles.tertiaryLight, - tertiaryDark: styles.tertiaryDark, + base: "", + primaryDark: "", + primaryStrong: "", + primaryLight: "", + secondaryLight: "", + secondaryDark: "", + tertiaryLight: "", + tertiaryDark: "", }, variant: { default: styles.default, @@ -33,4 +36,97 @@ export const buttonVariants = cva(styles.btn, { theme: "primaryLight", variant: "default", }, + + compoundVariants: [ + { + className: styles.basePrimary, + intent: "primary", + theme: "base", + }, + { + className: styles.baseSecondary, + intent: "secondary", + theme: "base", + }, + { + className: styles.baseTertiary, + intent: "tertiary", + theme: "base", + }, + { + className: styles.baseInverted, + intent: "inverted", + theme: "base", + }, + { + className: styles.primaryDarkPrimary, + intent: "primary", + theme: "primaryDark", + }, + { + className: styles.primaryDarkSecondary, + intent: "secondary", + theme: "primaryDark", + }, + { + className: styles.primaryLightPrimary, + intent: "primary", + theme: "primaryLight", + }, + { + className: styles.primaryLightSecondary, + intent: "secondary", + theme: "primaryLight", + }, + { + className: styles.primaryStrongPrimary, + intent: "primary", + theme: "primaryStrong", + }, + { + className: styles.primaryStrongSecondary, + intent: "secondary", + theme: "primaryStrong", + }, + { + className: styles.secondaryDarkPrimary, + intent: "primary", + theme: "secondaryDark", + }, + { + className: styles.secondaryDarkSecondary, + intent: "secondary", + theme: "secondaryDark", + }, + { + className: styles.secondaryLightPrimary, + intent: "primary", + theme: "secondaryLight", + }, + { + className: styles.secondaryLightSecondary, + intent: "secondary", + theme: "secondaryLight", + }, + { + className: styles.tertiaryDarkPrimary, + intent: "primary", + theme: "tertiaryDark", + }, + { + className: styles.tertiaryDarkSecondary, + intent: "secondary", + theme: "tertiaryDark", + }, + { + className: styles.tertiaryLightPrimary, + intent: "primary", + theme: "tertiaryLight", + }, + { + className: styles.tertiaryLightSecondary, + intent: "secondary", + theme: "tertiaryLight", + }, + ], }) diff --git a/components/TempDesignSystem/CardGrid/cardGrid.module.css b/components/TempDesignSystem/CardGrid/cardGrid.module.css deleted file mode 100644 index b457bff79..000000000 --- a/components/TempDesignSystem/CardGrid/cardGrid.module.css +++ /dev/null @@ -1,55 +0,0 @@ -.gridContainer { - display: grid; - gap: var(--Spacing-x2); -} - -.carousel { - display: grid; - grid-auto-columns: 80dvw; - grid-auto-flow: column; - gap: var(--Spacing-x2); - margin-left: calc(0 - var(--Spacing-x2)); - margin-right: calc(0 - var(--Spacing-x2)); - padding-left: var(--Spacing-x2); - overflow-x: scroll; - scroll-padding-left: var(--Spacing-x2); - scroll-snap-type: x mandatory; - scrollbar-width: none; - /* Hide scrollbar IE and Edge */ - -ms-overflow-style: none; - /* Hide Scrollbar Firefox */ -} - -.carousel:last-child { - margin-right: var(--Spacing-x2); -} - -.carousel > * { - scroll-snap-align: start; -} - -/* Hide Scrollbar Chrome, Safari and Opera */ -.gridContainer::-webkit-scrollbar { - display: none; -} - -@media screen and (min-width: 1367px) { - .twoColumnGrid, - .twoPlusOne { - grid-template-columns: repeat(2, 1fr); - } - - .threeColumnGrid { - grid-template-columns: repeat(3, 1fr); - } - - .twoPlusOne > *:last-child { - grid-column: span 2; - } - - .carousel { - grid-auto-flow: unset; - margin: 0; - padding: 0; - } -} diff --git a/components/TempDesignSystem/Form/Country/country.module.css b/components/TempDesignSystem/Form/Country/country.module.css index d5f645878..082c648ba 100644 --- a/components/TempDesignSystem/Form/Country/country.module.css +++ b/components/TempDesignSystem/Form/Country/country.module.css @@ -1,35 +1,46 @@ .container { - --select-border: 2px solid var(--UI-Grey-60); - --select-width: min(28rem, 100%); + position: relative; } .comboBoxContainer { background-color: var(--Main-Grey-White); + border-color: var(--Scandic-Beige-40); + border-radius: var(--Corner-radius-Medium); + border-style: solid; + border-width: 1px; display: grid; - grid-template-areas: "content"; - width: var(--select-width); + gap: var(--Spacing-x-half); + grid-template-areas: + "label chevron" + "input chevron"; + grid-template-columns: 1fr auto; + grid-template-rows: auto auto; + height: 60px; + padding: var(--Spacing-x1) var(--Spacing-x2); +} + +.label { + grid-area: label; } .input { background-color: var(--Main-Grey-White); - border: var(--select-border); - border-radius: var(--Corner-radius-Small); - grid-area: content; - height: 40px; - padding: var(--Spacing-x1) var(--Spacing-x2); - width: var(--select-width); + border: none; + grid-area: input; + height: 18px; + padding: 0; } .input, .listBoxItem { - color: var(--UI-Grey-60); + color: var(--Main-Grey-100); } .button { background: none; border: none; cursor: pointer; - grid-area: content; + grid-area: chevron; height: 100%; justify-self: flex-end; padding-left: 0; @@ -38,13 +49,27 @@ .popover { background-color: var(--Main-Grey-White); - border: var(--select-border); - border-radius: var(--Corner-radius-Small); + border-color: var(--Scandic-Beige-40); + border-style: solid; + border-width: 1px; + border-radius: var(--Corner-radius-Medium); + left: 0px; + max-height: 400px; overflow: auto; - padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x1); - width: var(--select-width); + padding: var(--Spacing-x2); + top: calc(60px + var(--Spacing-x1)); + width: 100%; } .listBoxItem { - padding: 0 var(--Spacing-x1); -} \ No newline at end of file + padding: var(--Spacing-x1) var(--Spacing-x1) var(--Spacing-x1) + var(--Spacing-x2); +} + +.listBoxItem[data-selected="true"], +.listBoxItem:hover { + background-color: var(--Scandic-Blue-00); + border-radius: var(--Corner-radius-Medium); + cursor: pointer; + outline: none; +} diff --git a/components/TempDesignSystem/Form/Country/country.ts b/components/TempDesignSystem/Form/Country/country.ts index f8f4187d0..29211f0fb 100644 --- a/components/TempDesignSystem/Form/Country/country.ts +++ b/components/TempDesignSystem/Form/Country/country.ts @@ -1,6 +1,11 @@ import type { RegisterOptions } from "react-hook-form" export type CountryProps = { + label: string name?: string + placeholder?: string registerOptions?: RegisterOptions } + +export type CountryPortalContainer = HTMLDivElement | undefined +export type CountryPortalContainerArgs = HTMLDivElement | null diff --git a/components/TempDesignSystem/Form/Country/index.tsx b/components/TempDesignSystem/Form/Country/index.tsx index 2aca79f24..8a6bae687 100644 --- a/components/TempDesignSystem/Form/Country/index.tsx +++ b/components/TempDesignSystem/Form/Country/index.tsx @@ -1,6 +1,6 @@ "use client" import { ErrorMessage } from "@hookform/error-message" -import { useRef } from "react" +import { useState } from "react" import { Button, ComboBox, @@ -14,21 +14,33 @@ import { import { useController, useFormContext } from "react-hook-form" import { useIntl } from "react-intl" +import Label from "@/components/TempDesignSystem/Form/Label" +import SelectChevron from "@/components/TempDesignSystem/Form/SelectChevron" import Body from "@/components/TempDesignSystem/Text/Body" -import SelectChevron from "../SelectChevron" import { countries } from "./countries" import styles from "./country.module.css" -import type { CountryProps } from "./country" +import type { + CountryPortalContainer, + CountryPortalContainerArgs, + CountryProps, +} from "./country" export default function CountrySelect({ + label, name = "country", - registerOptions, + registerOptions = {}, }: CountryProps) { const { formatMessage } = useIntl() - const divRef = useRef(null) + const [rootDiv, setRootDiv] = useState(undefined) + + function setRef(node: CountryPortalContainerArgs) { + if (node) { + setRootDiv(node) + } + } const { control, setValue } = useFormContext() const { field } = useController({ control, @@ -43,7 +55,7 @@ export default function CountrySelect({ const selectCountryLabel = formatMessage({ id: "Select a country" }) return ( -
+
+ {countries.map((country, idx) => ( diff --git a/components/TempDesignSystem/Form/Date/date.module.css b/components/TempDesignSystem/Form/Date/date.module.css index aa7b4ce47..6c0549313 100644 --- a/components/TempDesignSystem/Form/Date/date.module.css +++ b/components/TempDesignSystem/Form/Date/date.module.css @@ -1,20 +1,9 @@ /* Leaving, will most likely get deleted */ .container { - --border: 2px solid var(--some-black-color, #757575); - --radius: 0.4rem; - --width: min(28rem, 100%); - - --width-day: 6rem; - --width-month: 6rem; - --width-year: 8rem; - display: grid; - gap: 0.8rem; - grid-template-areas: "day month year"; - grid-template-columns: min(--width-day, 1fr) min(--width-month, 1fr) min( - --width-year, - 2fr - ); + gap: var(--Spacing-x2); + grid-template-areas: "year month day"; + grid-template-columns: 1fr 1fr 1fr; width: var(--width); } diff --git a/components/TempDesignSystem/Form/Date/date.ts b/components/TempDesignSystem/Form/Date/date.ts index 22c13e160..4e20d03f0 100644 --- a/components/TempDesignSystem/Form/Date/date.ts +++ b/components/TempDesignSystem/Form/Date/date.ts @@ -1,6 +1,4 @@ -import type { Control, RegisterOptions } from "react-hook-form" - -import type { EditProfileSchema } from "@/components/MyProfile/Profile/Edit/Form/schema" +import type { RegisterOptions } from "react-hook-form" export const enum DateName { date = "date", @@ -9,7 +7,6 @@ export const enum DateName { } export interface DateProps extends React.SelectHTMLAttributes { - control: Control - name: keyof EditProfileSchema - registerOptions: RegisterOptions + name: string + registerOptions: RegisterOptions } diff --git a/components/TempDesignSystem/Form/Date/index.tsx b/components/TempDesignSystem/Form/Date/index.tsx index 894744dd0..289892417 100644 --- a/components/TempDesignSystem/Form/Date/index.tsx +++ b/components/TempDesignSystem/Form/Date/index.tsx @@ -11,9 +11,9 @@ import { useIntl } from "react-intl" import { dt } from "@/lib/dt" +import Select from "@/components/TempDesignSystem/Select" import { rangeArray } from "@/utils/rangeArray" -import Select from "../Select" import { DateName } from "./date" import styles from "./date.module.css" @@ -23,14 +23,10 @@ import type { Key } from "react-aria-components" import type { DateProps } from "./date" /** TODO: Get selecting with Enter-key to work */ -export default function DateSelect({ - control, - name, - registerOptions, -}: DateProps) { +export default function DateSelect({ name, registerOptions }: DateProps) { const { formatMessage } = useIntl() const d = useWatch({ name }) - const { setValue } = useFormContext() + const { control, setValue } = useFormContext() const { field } = useController({ control, name, diff --git a/components/TempDesignSystem/Form/ErrorMessage/error.module.css b/components/TempDesignSystem/Form/ErrorMessage/error.module.css index 77cdfc9f1..215e530ff 100644 --- a/components/TempDesignSystem/Form/ErrorMessage/error.module.css +++ b/components/TempDesignSystem/Form/ErrorMessage/error.module.css @@ -1,4 +1,7 @@ .message { + align-items: center; color: var(--Scandic-Red-60); - margin: var(--Spacing-x-half) 0 0; -} \ No newline at end of file + display: flex; + gap: var(--Spacing-x-half); + margin: var(--Spacing-x1) 0 0; +} diff --git a/components/TempDesignSystem/Form/ErrorMessage/index.tsx b/components/TempDesignSystem/Form/ErrorMessage/index.tsx index fb906cd31..b0491cb55 100644 --- a/components/TempDesignSystem/Form/ErrorMessage/index.tsx +++ b/components/TempDesignSystem/Form/ErrorMessage/index.tsx @@ -1,6 +1,7 @@ import { ErrorMessage as RHFErrorMessage } from "@hookform/error-message" -import Body from "@/components/TempDesignSystem/Text/Body" +import { InfoCircleIcon } from "@/components/Icons" +import Caption from "@/components/TempDesignSystem/Text/Caption" import styles from "./error.module.css" @@ -15,9 +16,10 @@ export default function ErrorMessage({ errors={errors} name={name} render={({ message }) => ( - + + {message} - + )} /> ) diff --git a/components/TempDesignSystem/Form/Input/index.tsx b/components/TempDesignSystem/Form/Input/index.tsx index 234120588..5c93f0027 100644 --- a/components/TempDesignSystem/Form/Input/index.tsx +++ b/components/TempDesignSystem/Form/Input/index.tsx @@ -1,8 +1,13 @@ "use client" -import { Input as AriaInput, TextField } from "react-aria-components" -import { useController } from "react-hook-form" +import { + Input as AriaInput, + Label as AriaLabel, + TextField, +} from "react-aria-components" +import { useController, useFormContext } from "react-hook-form" import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage" +import Label from "@/components/TempDesignSystem/Form/Label" import Body from "@/components/TempDesignSystem/Text/Body" import styles from "./input.module.css" @@ -11,17 +16,24 @@ import type { InputProps } from "./input" export default function Input({ "aria-label": ariaLabel, - control, disabled, + label, name, - placeholder, - registerOptions, + placeholder = "", + registerOptions = {}, + required = false, type = "text", }: InputProps) { + const { control } = useFormContext() + const rules = { + ...registerOptions, + required: + "required" in registerOptions ? !!registerOptions.required : required, + } const { field, fieldState, formState } = useController({ control, name, - rules: registerOptions, + rules, }) return ( @@ -32,18 +44,24 @@ export default function Input({ isInvalid={fieldState.invalid} isRequired={!!registerOptions?.required} name={field.name} - onBlur={field.onBlur} - onChange={field.onChange} type={type} > - - - - + + + + + + + ) } diff --git a/components/TempDesignSystem/Form/Input/input.module.css b/components/TempDesignSystem/Form/Input/input.module.css index eee9098d5..cd0b026f9 100644 --- a/components/TempDesignSystem/Form/Input/input.module.css +++ b/components/TempDesignSystem/Form/Input/input.module.css @@ -1,8 +1,44 @@ -.input { - border: 2px solid var(--UI-Grey-60); - border-radius: var(--Corner-radius-Small); - color: var(--UI-Grey-60); - height: 40px; +.container { + align-content: center; + background-color: var(--Main-Grey-White); + border-color: var(--Scandic-Beige-40); + border-style: solid; + border-width: 1px; + border-radius: var(--Corner-radius-Medium); + display: grid; + gap: var(--Spacing-x-half); + grid-template-rows: auto auto; + height: 60px; padding: var(--Spacing-x1) var(--Spacing-x2); - width: min(280px, 100%); -} \ No newline at end of file + transition: border-color 200ms ease; +} + +.container:has(.input:not(:focus):placeholder-shown) { + gap: 0; + grid-template-rows: 1fr; +} + +.container:has(.input:active, .input:focus) { + border-color: var(--Scandic-Blue-90); +} + +.input { + background: none; + border: none; + color: var(--Main-Grey-100); + height: 18px; + margin: 0; + order: 2; + overflow: visible; + padding: 0; +} + +.input:not(:active, :focus):placeholder-shown { + height: 0px; +} + +.input:focus, +.input:focus:placeholder-shown { + height: 18px; + outline: none; +} diff --git a/components/TempDesignSystem/Form/Input/input.ts b/components/TempDesignSystem/Form/Input/input.ts index 931e17265..54b00e01b 100644 --- a/components/TempDesignSystem/Form/Input/input.ts +++ b/components/TempDesignSystem/Form/Input/input.ts @@ -1,10 +1,8 @@ -import { Control, RegisterOptions } from "react-hook-form" - -import { EditProfileSchema } from "@/components/MyProfile/Profile/Edit/Form/schema" +import type { RegisterOptions } from "react-hook-form" export interface InputProps extends React.InputHTMLAttributes { - control: Control - name: keyof EditProfileSchema - registerOptions?: RegisterOptions + label: string + name: string + registerOptions?: RegisterOptions } diff --git a/components/TempDesignSystem/Form/Label/index.tsx b/components/TempDesignSystem/Form/Label/index.tsx new file mode 100644 index 000000000..f45770897 --- /dev/null +++ b/components/TempDesignSystem/Form/Label/index.tsx @@ -0,0 +1,20 @@ +import { labelVariants } from "./variants" + +import type { LabelProps } from "./label" + +export default function Label({ + children, + className, + required, + size, +}: LabelProps) { + const classNames = labelVariants({ + className, + size, + }) + return ( + + {children} {required ? "*" : ""} + + ) +} diff --git a/components/TempDesignSystem/Form/Label/label.module.css b/components/TempDesignSystem/Form/Label/label.module.css new file mode 100644 index 000000000..288d49019 --- /dev/null +++ b/components/TempDesignSystem/Form/Label/label.module.css @@ -0,0 +1,30 @@ +.label { + color: var(--UI-Grey-60); + font-family: "fira sans"; + font-weight: 400; + letter-spacing: 0.03px; + line-height: 120%; + text-align: left; +} + +span.small { + display: block; + font-size: 12px; +} + +span.regular { + font-size: 16px; + order: 1; + transition: font-size 200ms ease 100ms; +} + +input:focus ~ .label, +input:not(:placeholder-shown) ~ .label { + display: block; + font-size: 12px; +} + +input:placeholder-shown ~ .label { + align-self: center; + grid-row: 1/-1; +} diff --git a/components/TempDesignSystem/Form/Label/label.ts b/components/TempDesignSystem/Form/Label/label.ts new file mode 100644 index 000000000..d800294a9 --- /dev/null +++ b/components/TempDesignSystem/Form/Label/label.ts @@ -0,0 +1,9 @@ +import { labelVariants } from "./variants" + +import type { VariantProps } from "class-variance-authority" + +export interface LabelProps + extends React.PropsWithChildren>, + VariantProps { + required?: boolean +} diff --git a/components/TempDesignSystem/Form/Label/variants.ts b/components/TempDesignSystem/Form/Label/variants.ts new file mode 100644 index 000000000..3a6c3417c --- /dev/null +++ b/components/TempDesignSystem/Form/Label/variants.ts @@ -0,0 +1,15 @@ +import { cva } from "class-variance-authority" + +import styles from "./label.module.css" + +export const labelVariants = cva(styles.label, { + variants: { + size: { + small: styles.small, + regular: styles.regular, + }, + }, + defaultVariants: { + size: "regular", + }, +}) diff --git a/components/TempDesignSystem/Form/Phone/index.tsx b/components/TempDesignSystem/Form/Phone/index.tsx index a7d5e8975..19a2b037b 100644 --- a/components/TempDesignSystem/Form/Phone/index.tsx +++ b/components/TempDesignSystem/Form/Phone/index.tsx @@ -1,99 +1,137 @@ "use client" import "react-international-phone/style.css" -import { useCallback, useEffect, useRef } from "react" +import { parsePhoneNumber } from "libphonenumber-js" +import { + Input as AriaInput, + Label as AriaLabel, + TextField, +} from "react-aria-components" import { useController, useFormContext, useWatch } from "react-hook-form" import { - defaultCountries, - getCountry, - PhoneInput, - type PhoneInputRefType, + CountrySelector, + DialCodePreview, + ParsedCountry, + usePhoneInput, } from "react-international-phone" +import { useIntl } from "react-intl" +import { ChevronDownIcon } from "@/components/Icons" import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage" +import Label from "@/components/TempDesignSystem/Form/Label" +import Body from "@/components/TempDesignSystem/Text/Body" import styles from "./phone.module.css" +import type { ChangeEvent } from "react" + import type { PhoneProps } from "./phone" export default function Phone({ - countrySelectName = "country", + ariaLabel = "Phone number input", + disabled = false, + label, name = "phoneNumber", placeholder = "", registerOptions = { required: true, }, }: PhoneProps) { - const phoneRef = useRef(null) - const { control, formState } = useFormContext() - const countryValue = useWatch({ name: countrySelectName }) - const defaultCountry = getCountry({ - countries: defaultCountries, - field: "iso2", - value: String(countryValue).toLowerCase(), - }) - /** - * Holds the previous selected country to be able to update - * countrycode based on country select field. - * Since PhoneInput inputs the countrys dialcode (country code) upon - * selection, we need to check if the current value is just - * the previously selected countrys dialcode number. - */ - const prevSelectedCountry = useRef(countryValue) - const { field } = useController({ + const { formatMessage } = useIntl() + const { control, setValue } = useFormContext() + const phone = useWatch({ name }) + + const { field, fieldState, formState } = useController({ control, + disabled, name, rules: registerOptions, }) - const handleCountrySelectForPhone = useCallback((country: string) => { - const selectedCountry = getCountry({ - countries: defaultCountries, - field: "iso2", - value: country.toLowerCase(), + const { country, handlePhoneValueChange, inputValue, setCountry } = + usePhoneInput({ + defaultCountry: + parsePhoneNumber( + formState.defaultValues?.phoneNumber + ).country?.toLowerCase() ?? "sv", + disableCountryGuess: true, + forceDialCode: true, + value: phone, }) - if (selectedCountry) { - phoneRef.current?.setCountry(selectedCountry.iso2) - prevSelectedCountry.current = country.toLowerCase() - } - }, []) + function handleSelectCountry(value: ParsedCountry) { + setCountry(value.iso2) + } - useEffect(() => { - if (countryValue) { - if (field.value) { - if (prevSelectedCountry.current) { - if (prevSelectedCountry.current !== countryValue) { - const selectedCountryPrev = getCountry({ - countries: defaultCountries, - field: "iso2", - value: prevSelectedCountry.current.toLowerCase(), - }) - if ( - field.value.replace("+", "") === selectedCountryPrev?.dialCode - ) { - handleCountrySelectForPhone(countryValue) - } - } - } else { - handleCountrySelectForPhone(countryValue) - } - } else { - handleCountrySelectForPhone(countryValue) - } - } - }, [countryValue, field.value, handleCountrySelectForPhone]) + function handleChange(evt: ChangeEvent) { + handlePhoneValueChange(evt) + setValue(name, evt.target.value) + } return (
- ( + + )} /> + + + + + + + + +
) diff --git a/components/TempDesignSystem/Form/Phone/phone.module.css b/components/TempDesignSystem/Form/Phone/phone.module.css index edf9730c0..11d246143 100644 --- a/components/TempDesignSystem/Form/Phone/phone.module.css +++ b/components/TempDesignSystem/Form/Phone/phone.module.css @@ -1,26 +1,130 @@ .phone { - --react-international-phone-border-color: var(--UI-Grey-60); - --react-international-phone-border-radius: var(--Corner-radius-Small); - --react-international-phone-font-size: var( - --typography-Body-Regular-fontSize - ); - --react-international-phone-height: 40px; - --react-international-phone-text-color: var(--UI-Grey-60); -} - -.phone :global(.react-international-phone-input-container) { display: grid; - /* r-i-p sets their width dynamically and doesn't respect the width property of its parent */ - grid-template-columns: 470px minmax(203px, 1fr); - width: min(280px, 100%); + gap: var(--Spacing-x2); + grid-template-columns: max(164px) 1fr; + + --react-international-phone-background-color: var(--Main-Grey-White); + --react-international-phone-border-color: var(--Scandic-Beige-40); + --react-international-phone-dropdown-preferred-list-divider-color: var( + --Scandic-Brand-Pale-Peach + ); + --react-international-phone-selected-dropdown-item-background-color: var( + --Scandic-Blue-00 + ); + --react-international-phone-text-color: var(--Main-Grey-100); + + --react-international-phone-dropdown-preferred-list-divider-margin: 8px; + + --react-international-phone-height: 60px; + --react-international-phone-dropdown-top: calc( + var(--react-international-phone-height) + var(--Spacing-x1) + ); } -/* react-international-phone only exposes variables to change border-color */ -.phone :global(.react-international-phone-country-selector-button), -.phone :global(.react-international-phone-input) { - border-width: 2px; +.phone:has(.input:active, .input:focus) { + --react-international-phone-border-color: var(--Scandic-Blue-90); } -.phone :global(.react-international-phone-input) { +.phone :global(.react-international-phone-country-selector-dropdown) { + background: var(--Main-Grey-White); + border-radius: var(--Corner-radius-Medium); + box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.08); + gap: var(--Spacing-x1); + outline: none; + padding: var(--Spacing-x2); +} + +.phone + :global(.react-international-phone-country-selector-dropdown__list-item) { + border-radius: var(--Corner-radius-Medium); + padding: var(--Spacing-x1) var(--Spacing-x1) var(--Spacing-x1) + var(--Spacing-x-one-and-half); +} + +.phone + :global(.react-international-phone-country-selector-button__button-content) { + align-self: center; +} + +.inputContainer, +.select { + align-content: center; + background-color: var(--Main-Grey-White); + border-color: var(--Scandic-Beige-40); + border-style: solid; + border-width: 1px; + border-radius: var(--Corner-radius-Medium); + display: grid; + gap: var(--Spacing-x-half); + grid-template-rows: auto auto; + height: 60px; padding: var(--Spacing-x1) var(--Spacing-x2); + transition: border-color 200ms ease; +} + +.select { + width: 100%; +} + +.select[aria-expanded="true"] .chevron { + transform: rotate(180deg); +} + +.selectContainer { + background-color: var(--Main-Grey-White); + border: none; + display: grid; + gap: var(--Spacing-x1); + grid-template-columns: auto 1fr auto; + height: 18px; + justify-content: flex-start; + order: 2; +} + +.arrow { + display: none; +} + +.flag { + height: 18px; + margin: 0; + width: 18px; +} + +.select .dialCode { + border: none; + color: var(--Main-Grey-100); + line-height: 1; + justify-self: flex-start; + padding: 0; +} + +.inputContainer:has(.input:not(:focus):placeholder-shown) { + gap: 0; + grid-template-rows: 1fr; +} + +.inputContainer:has(.input:active, .input:focus) { + border-color: var(--Scandic-Blue-90); +} + +.input { + background: none; + border: none; + color: var(--Main-Grey-100); + height: 18px; + margin: 0; + order: 2; + overflow: visible; + padding: 0; +} + +.input:not(:active, :focus):placeholder-shown { + height: 0px; +} + +.input:focus, +.input:focus:placeholder-shown { + height: 18px; + outline: none; } diff --git a/components/TempDesignSystem/Form/Phone/phone.ts b/components/TempDesignSystem/Form/Phone/phone.ts index 57142e829..4828682bc 100644 --- a/components/TempDesignSystem/Form/Phone/phone.ts +++ b/components/TempDesignSystem/Form/Phone/phone.ts @@ -1,7 +1,9 @@ import type { RegisterOptions } from "react-hook-form" export type PhoneProps = { - countrySelectName?: string + ariaLabel?: string + disabled?: boolean + label: string name?: string placeholder?: string registerOptions?: RegisterOptions diff --git a/components/TempDesignSystem/Form/Select/index.tsx b/components/TempDesignSystem/Form/Select/index.tsx index 5e839f555..5e3be5253 100644 --- a/components/TempDesignSystem/Form/Select/index.tsx +++ b/components/TempDesignSystem/Form/Select/index.tsx @@ -1,94 +1,35 @@ "use client" -import { useState } from "react" -import { - Button, - type Key, - Label, - ListBox, - ListBoxItem, - Popover, - Select as ReactAriaSelect, - SelectValue, -} from "react-aria-components" +import { useController, useFormContext } from "react-hook-form" -import Body from "../../Text/Body" -import Footnote from "../../Text/Footnote" -import SelectChevron from "../SelectChevron" +import ReactAriaSelect from "@/components/TempDesignSystem/Select" -import styles from "./select.module.css" - -import type { SelectPortalContainer, SelectProps } from "./select" +import type { SelectProps } from "./select" export default function Select({ - "aria-label": ariaLabel, items, label, - onSelect, + name, placeholder, - value, - defaultSelectedKey, + registerOptions = {}, }: SelectProps) { - const [rootDiv, setRootDiv] = useState(null) - - function setRef(node: SelectPortalContainer) { - if (node) { - setRootDiv(node) - } - } - - function handleOnSelect(key: Key) { - onSelect(key) - } + const { control } = useFormContext() + const { field } = useController({ + control, + name, + rules: registerOptions, + }) return ( -
- - - - - - - - {items.map((item) => ( - - {item.label} - - ))} - - - - -
+ ) } diff --git a/components/TempDesignSystem/Form/Select/select.ts b/components/TempDesignSystem/Form/Select/select.ts index 9ffc1b9e7..030f7b587 100644 --- a/components/TempDesignSystem/Form/Select/select.ts +++ b/components/TempDesignSystem/Form/Select/select.ts @@ -1,13 +1,12 @@ -import type { Key } from "react-aria-components" +import type { RegisterOptions } from "react-hook-form" + +import type { SelectProps as ReactAriaSelectProps } from "@/components/TempDesignSystem/Select/select" export interface SelectProps - extends Omit, "onSelect"> { - items: { label: string; value: Key }[] - label: string - name: string - onSelect: (key: Key) => void - placeholder?: string - defaultSelectedKey?: Key + extends Omit< + React.SelectHTMLAttributes, + "name" | "onSelect" + >, + Omit { + registerOptions?: RegisterOptions } - -export type SelectPortalContainer = HTMLDivElement | null diff --git a/components/TempDesignSystem/Form/SelectChevron/index.tsx b/components/TempDesignSystem/Form/SelectChevron/index.tsx index fea024233..6ee7982fb 100644 --- a/components/TempDesignSystem/Form/SelectChevron/index.tsx +++ b/components/TempDesignSystem/Form/SelectChevron/index.tsx @@ -5,7 +5,7 @@ import styles from "./chevron.module.css" export default function SelectChevron() { return ( ) } diff --git a/components/TempDesignSystem/Link/link.module.css b/components/TempDesignSystem/Link/link.module.css index 6f04eb0cb..6aa0c5c60 100644 --- a/components/TempDesignSystem/Link/link.module.css +++ b/components/TempDesignSystem/Link/link.module.css @@ -56,7 +56,7 @@ } .activeSidebar { - background-color: var(--Scandic-Brand-Warm-White); + background-color: var(--Scandic-Brand-Pale-Peach); } .black { diff --git a/components/TempDesignSystem/Link/variants.ts b/components/TempDesignSystem/Link/variants.ts index faef05d1d..a49023324 100644 --- a/components/TempDesignSystem/Link/variants.ts +++ b/components/TempDesignSystem/Link/variants.ts @@ -10,6 +10,7 @@ export const linkVariants = cva(styles.link, { color: { black: styles.black, burgundy: styles.burgundy, + none: "", pale: styles.pale, peach80: styles.peach80, }, diff --git a/components/TempDesignSystem/Select/index.tsx b/components/TempDesignSystem/Select/index.tsx new file mode 100644 index 000000000..b703a7a1b --- /dev/null +++ b/components/TempDesignSystem/Select/index.tsx @@ -0,0 +1,98 @@ +"use client" +import { useState } from "react" +import { + Button, + type Key, + ListBox, + ListBoxItem, + Popover, + Select as ReactAriaSelect, + SelectValue, +} from "react-aria-components" + +import Label from "@/components/TempDesignSystem/Form/Label" +import Body from "@/components/TempDesignSystem/Text/Body" + +import SelectChevron from "../Form/SelectChevron" + +import styles from "./select.module.css" + +import type { + SelectPortalContainer, + SelectPortalContainerArgs, + SelectProps, +} from "./select" + +export default function Select({ + "aria-label": ariaLabel, + defaultSelectedKey, + items, + label, + name, + onSelect, + placeholder, + value, +}: SelectProps) { + const [rootDiv, setRootDiv] = useState(undefined) + + function setRef(node: SelectPortalContainerArgs) { + if (node) { + setRootDiv(node) + } + } + + function handleOnSelect(key: Key) { + onSelect(key) + } + + return ( +
+ + + + + + + + {items.map((item) => ( + + {item.label} + + ))} + + + + +
+ ) +} diff --git a/components/TempDesignSystem/Form/Select/select.module.css b/components/TempDesignSystem/Select/select.module.css similarity index 95% rename from components/TempDesignSystem/Form/Select/select.module.css rename to components/TempDesignSystem/Select/select.module.css index ccf664fba..7850891e9 100644 --- a/components/TempDesignSystem/Form/Select/select.module.css +++ b/components/TempDesignSystem/Select/select.module.css @@ -2,10 +2,6 @@ position: relative; } -.label { - color: var(--Base-Text-UI-Placeholder); -} - .select { border: 1px solid var(--Base-Border-Normal); border-radius: var(--Corner-radius-Medium); @@ -27,7 +23,7 @@ color: var(--Base-Text-UI-High-contrast); display: flex; gap: var(--Spacing-x-half); - height: 56px; + height: 60px; outline: none; padding: var(--Spacing-x-one-and-half) var(--Spacing-x2); text-align: left; @@ -69,4 +65,4 @@ .listBoxItem[data-selected="true"] { font-weight: 500; -} \ No newline at end of file +} diff --git a/components/TempDesignSystem/Select/select.ts b/components/TempDesignSystem/Select/select.ts new file mode 100644 index 000000000..656fbb2a8 --- /dev/null +++ b/components/TempDesignSystem/Select/select.ts @@ -0,0 +1,15 @@ +import type { Key } from "react-aria-components" + +export interface SelectProps + extends Omit, "onSelect"> { + defaultSelectedKey?: Key + items: { label: string; value: Key }[] + label: string + name: string + onSelect: (key: Key) => void + placeholder?: string + value?: string | number +} + +export type SelectPortalContainer = HTMLDivElement | undefined +export type SelectPortalContainerArgs = HTMLDivElement | null diff --git a/components/TempDesignSystem/Text/BiroScript/biroScript.module.css b/components/TempDesignSystem/Text/BiroScript/biroScript.module.css index d907c7240..abb5f3e67 100644 --- a/components/TempDesignSystem/Text/BiroScript/biroScript.module.css +++ b/components/TempDesignSystem/Text/BiroScript/biroScript.module.css @@ -5,18 +5,22 @@ } .one { - font-size: clamp(var(--typography-Script-1-Mobile-fontSize), - 1.3vw + 14px, - var(--typography-Script-1-Desktop-fontSize)); + font-size: clamp( + var(--typography-Script-1-Mobile-fontSize), + 1.3vw + 14px, + var(--typography-Script-1-Desktop-fontSize) + ); font-weight: var(--typography-Script-1-fontWeight); letter-spacing: var(--typography-Script-1-letterSpacing); line-height: var(--typography-Script-1-lineHeight); } .two { - font-size: clamp(var(--typography-Script-2-Mobile-fontSize), - 0.6vw + 15px, - var(--typography-Script-2-Desktop-fontSize)); + font-size: clamp( + var(--typography-Script-2-Mobile-fontSize), + 0.6vw + 15px, + var(--typography-Script-2-Desktop-fontSize) + ); font-weight: var(--typography-Script-2-fontWeight); letter-spacing: var(--typography-Script-2-letterSpacing); line-height: var(--typography-Script-2-lineHeight); @@ -31,7 +35,7 @@ } .black { - color: #000; + color: var(--Main-Grey-100); } .burgundy { @@ -44,4 +48,4 @@ .plosa { color: var(--Theme-Primary-Light-On-Surface-Accent); -} \ No newline at end of file +} diff --git a/components/TempDesignSystem/Text/Body/body.module.css b/components/TempDesignSystem/Text/Body/body.module.css index aba1c9f95..6d739f00c 100644 --- a/components/TempDesignSystem/Text/Body/body.module.css +++ b/components/TempDesignSystem/Text/Body/body.module.css @@ -43,8 +43,7 @@ } .black { - /* No black variable exist yet */ - color: #000; + color: var(--Main-Grey-100); } .burgundy { diff --git a/components/TempDesignSystem/Text/Caption/caption.module.css b/components/TempDesignSystem/Text/Caption/caption.module.css index 9e5749fc4..21e846236 100644 --- a/components/TempDesignSystem/Text/Caption/caption.module.css +++ b/components/TempDesignSystem/Text/Caption/caption.module.css @@ -26,8 +26,7 @@ } .black { - /* No black variable exist yet */ - color: #000; + color: var(--Main-Grey-100); } .burgundy { diff --git a/components/TempDesignSystem/Text/Footnote/footnote.module.css b/components/TempDesignSystem/Text/Footnote/footnote.module.css index f7cc5520a..f9f8e044b 100644 --- a/components/TempDesignSystem/Text/Footnote/footnote.module.css +++ b/components/TempDesignSystem/Text/Footnote/footnote.module.css @@ -34,8 +34,7 @@ } .black { - /* No black variable exist yet */ - color: #000; + color: var(--Main-Grey-100); } .burgundy { diff --git a/components/TempDesignSystem/Text/Subtitle/subtitle.module.css b/components/TempDesignSystem/Text/Subtitle/subtitle.module.css index 4eedc90f7..943a96e95 100644 --- a/components/TempDesignSystem/Text/Subtitle/subtitle.module.css +++ b/components/TempDesignSystem/Text/Subtitle/subtitle.module.css @@ -28,6 +28,10 @@ text-align: left; } +.black { + color: var(--Main-Grey-100); +} + .burgundy { color: var(--Scandic-Brand-Burgundy); } diff --git a/components/TempDesignSystem/Text/Subtitle/variants.ts b/components/TempDesignSystem/Text/Subtitle/variants.ts index d4c294975..b4598b12a 100644 --- a/components/TempDesignSystem/Text/Subtitle/variants.ts +++ b/components/TempDesignSystem/Text/Subtitle/variants.ts @@ -5,6 +5,7 @@ import styles from "./subtitle.module.css" const config = { variants: { color: { + black: styles.black, burgundy: styles.burgundy, pale: styles.pale, }, diff --git a/components/TempDesignSystem/Text/Title/title.module.css b/components/TempDesignSystem/Text/Title/title.module.css index 91b823f57..6ce9aa080 100644 --- a/components/TempDesignSystem/Text/Title/title.module.css +++ b/components/TempDesignSystem/Text/Title/title.module.css @@ -85,7 +85,7 @@ } .black { - color: #000; + color: var(--Main-Grey-100); } .burgundy { @@ -96,6 +96,10 @@ color: var(--Scandic-Brand-Pale-Peach); } +.peach80 { + color: var(--Scandic-Peach-80); +} + .red { color: var(--Scandic-Brand-Scandic-Red); } diff --git a/components/TempDesignSystem/Text/Title/variants.ts b/components/TempDesignSystem/Text/Title/variants.ts index 45bcd3d5f..499e6ba29 100644 --- a/components/TempDesignSystem/Text/Title/variants.ts +++ b/components/TempDesignSystem/Text/Title/variants.ts @@ -8,6 +8,7 @@ const config = { black: styles.black, burgundy: styles.burgundy, pale: styles.pale, + peach80: styles.peach80, red: styles.red, }, textAlign: { diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index cf9cdb965..c31dd8aea 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -18,17 +18,19 @@ "Continue": "Blive ved", "Could not find requested resource": "Kunne ikke finde den anmodede ressource", "Country": "Land", + "Country code": "Landekode", "Current level": "Nuværende niveau", + "Current password": "Nuværende kodeord", "Date of Birth": "Fødselsdato", "Day": "Dag", "Description": "Beskrivelse", + "Discard changes": "Kassér ændringer", "Edit": "Redigere", "Edit profile": "Rediger profil", "Email": "E-mail", "Empty": "Empty", "Explore all levels and benefits": "Udforsk alle niveauer og fordele", "Find booking": "Find booking", - "Free soft drink voucher for the kids when staying": "Gratis sodavandskupon til børnene, når de bor", "Get inspired": "Blive inspireret", "Go back to overview": "Gå tilbage til oversigten", "How it works": "Hvordan det virker", @@ -45,6 +47,7 @@ "My credit cards": "Mine kreditkort", "My pages": "Mine sider", "My wishes": "Mine ønsker", + "New password": "Nyt kodeord", "Next": "Næste", "Next level": "Næste niveau", "No content published": "Intet indhold offentliggjort", @@ -66,10 +69,12 @@ "Previous victories": "Tidligere sejre", "points until next level": "point indtil næste niveau", "Read more": "Læs mere", + "Retype new password": "Gentag den nye adgangskode", "Save": "Gemme", "Select a country": "Vælg et land", "Select country of residence": "Vælg bopælsland", "Select date of birth": "Vælg fødselsdato", + "Select language": "Vælg sprog", "Show more": "Vis mere", "Skip to main content": "Spring over og gå til hovedindhold", "Something went wrong!": "Noget gik galt!", @@ -77,6 +82,7 @@ "Total Points": "Samlet antal point", "Transaction date": "Overførselsdato", "Transactions": "Transaktioner", + "User information": "Brugeroplysninger", "Visiting address": "Besøgsadresse", "Where should you go next?": "Hvor skal du tage hen næste gang?", "Year": "År", diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index 11d3128df..6044b47a6 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -18,17 +18,19 @@ "Continue": "Weitermachen", "Could not find requested resource": "Die angeforderte Ressource konnte nicht gefunden werden.", "Country": "Land", + "Country code": "Landesvorwahl", "Current level": "Aktuelles Level", + "Current password": "Aktuelles Passwort", "Date of Birth": "Geburtsdatum", "Day": "Tag", "Description": "Beschreibung", + "Discard changes": "Änderungen verwerfen", "Edit": "Bearbeiten", "Edit profile": "Profil bearbeiten", "Email": "Email", "Empty": "Empty", "Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile", "Find booking": "Buchung finden", - "Free soft drink voucher for the kids when staying": "Gutschein für einen kostenlosen Softdrink für die Kinder bei Aufenthalt", "Get inspired": "Lass dich inspirieren", "Go back to overview": "Zurück zur Übersicht", "How it works": "Wie es funktioniert", @@ -45,6 +47,7 @@ "My credit cards": "Meine Kreditkarten", "My pages": "Meine Seiten", "My wishes": "Meine Wünsche", + "New password": "Neues Kennwort", "Next": "Nächste", "Next level": "Nächste Ebene", "No content published": "Kein Inhalt veröffentlicht", @@ -66,10 +69,12 @@ "Previous victories": "Bisherige Siege", "points until next level": "punkte bis zum nächsten Level", "Read more": "Mehr lesen", + "Retype new password": "Neues Passwort erneut eingeben", "Save": "Speichern", "Select a country": "Wähle ein Land", "Select country of residence": "Wählen Sie das Land Ihres Wohnsitzes aus", "Select date of birth": "Geburtsdatum auswählen", + "Select language": "Sprache auswählen", "Show more": "Zeig mehr", "Skip to main content": "Direkt zum Inhalt", "Something went wrong!": "Etwas ist schief gelaufen!", @@ -77,6 +82,7 @@ "Total Points": "Gesamtpunktzahl", "Transaction date": "Transaktionsdatum", "Transactions": "Transaktionen", + "User information": "Nutzerinformation", "Visiting address": "Besuchsadresse", "Where should you go next?": "Wohin soll es als nächstes gehen?", "Year": "Jahr", diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index 7a9b1b383..42b4fd967 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -18,17 +18,19 @@ "Continue": "Continue", "Could not find requested resource": "Could not find requested resource", "Country": "Country", + "Country code": "Country code", "Current level": "Current level", + "Current password": "Current password", "Date of Birth": "Date of Birth", "Day": "Day", "Description": "Description", + "Discard changes": "Discard changes", "Edit": "Edit", "Edit profile": "Edit profile", "Email": "Email", "Empty": "Empty", "Explore all levels and benefits": "Explore all levels and benefits", "Find booking": "Find booking", - "Free soft drink voucher for the kids when staying": "Free soft drink voucher for the kids when staying", "Get inspired": "Get inspired", "Go back to overview": "Go back to overview", "How it works": "How it works", @@ -45,6 +47,7 @@ "My credit cards": "My credit cards", "My pages": "My pages", "My wishes": "My wishes", + "New password": "New password", "Next": "Next", "Next level": "Next level", "No content published": "No content published", @@ -66,10 +69,12 @@ "Previous victories": "Previous victories", "points until next level": "points until next level", "Read more": "Read more", + "Retype new password": "Retype new password", "Save": "Save", "Select a country": "Select a country", "Select country of residence": "Select country of residence", "Select date of birth": "Select date of birth", + "Select language": "Select language", "Show more": "Show more", "Skip to main content": "Skip to main content", "Something went wrong!": "Something went wrong!", @@ -77,8 +82,9 @@ "Total Points": "Total Points", "Transaction date": "Transaction date", "Transactions": "Transactions", - "Where should you go next?": "Where should you go next?", + "User information": "User information", "Visiting address": "Visiting address", + "Where should you go next?": "Where should you go next?", "Year": "Year", "You have no previous stays.": "You have no previous stays.", "You have no upcoming stays.": "You have no upcoming stays.", diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index 33fbead47..77d843365 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -18,17 +18,19 @@ "Continue": "Jatkaa", "Could not find requested resource": "Pyydettyä resurssia ei löytynyt", "Country": "Maa", + "Country code": "Maatunnus", "Current level": "Nykyinen taso", + "Current password": "Nykyinen salasana", "Date of Birth": "Syntymäaika", "Day": "Päivä", "Description": "Kuvaus", + "Discard changes": "Hylkää muutokset", "Edit": "Muokata", "Edit profile": "Muokkaa profiilia", "Email": "Sähköposti", "Empty": "Empty", "Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin", "Find booking": "Etsi varaus", - "Free soft drink voucher for the kids when staying": "Ilmainen virvoitusjuomakuponki lapsille majoittuessaan", "Get inspired": "Inspiroidu", "Go back to overview": "Palaa yleiskatsaukseen", "How it works": "Kuinka se toimii", @@ -45,6 +47,7 @@ "My credit cards": "Minun luottokorttini", "My pages": "Omat sivut", "My wishes": "Toiveeni", + "New password": "Uusi salasana", "Next": "Seuraava", "Next level": "Seuraava taso", "No content published": "Ei julkaistua sisältöä", @@ -66,10 +69,12 @@ "Previous victories": "Edelliset voitot", "points until next level": "pisteitä seuraavalle tasolle", "Read more": "Lue lisää", + "Retype new password": "Kirjoita uusi salasana uudelleen", "Save": "Tallentaa", "Select a country": "Valitse maa", "Select country of residence": "Valitse asuinmaa", "Select date of birth": "Valitse syntymäaika", + "Select language": "Valitse kieli", "Show more": "Näytä lisää", "Skip to main content": "Siirry pääsisältöön", "Something went wrong!": "Jotain meni pieleen!", @@ -77,6 +82,7 @@ "Total Points": "Kokonaispisteet", "Transaction date": "Tapahtuman päivämäärä", "Transactions": "Tapahtumat", + "User information": "Käyttäjän tiedot", "Visiting address": "Käyntiosoite", "Where should you go next?": "Minne sinun pitäisi mennä seuraavaksi?", "Year": "Vuosi", diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index d5a6bee8a..ce38e1561 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -18,17 +18,19 @@ "Continue": "Fortsette", "Could not find requested resource": "Kunne ikke finne den forespurte ressursen", "Country": "Land", + "Country code": "Landskode", "Current level": "Nåværende nivå", + "Current password": "Nåværende passord", "Date of Birth": "Fødselsdato", "Day": "Dag", "Description": "Beskrivelse", + "Discard changes": "Forkaste endringer", "Edit": "Redigere", "Edit profile": "Rediger profil", "Email": "E-post", "Empty": "Empty", "Explore all levels and benefits": "Utforsk alle nivåer og fordeler", "Find booking": "Finn booking", - "Free soft drink voucher for the kids when staying": "Gratis bruskupong for barna når de bor", "Get inspired": "Bli inspirert", "Go back to overview": "Gå tilbake til oversikten", "How it works": "Hvordan det fungerer", @@ -45,6 +47,7 @@ "My credit cards": "Kredittkortene mine", "My pages": "Mine sider", "My wishes": "Mine ønsker", + "New password": "Nytt passord", "Next": "Neste", "Next level": "Neste nivå", "No content published": "Ingen innhold publisert", @@ -66,10 +69,12 @@ "Previous victories": "Tidligere seire", "points until next level": "poeng til neste nivå", "Read more": "Les mer", + "Retype new password": "Skriv inn nytt passord på nytt", "Save": "Lagre", "Select a country": "Velg et land", "Select country of residence": "Velg bostedsland", "Select date of birth": "Velg fødselsdato", + "Select language": "Velg språk", "Show more": "Vis mer", "Skip to main content": "Gå videre til hovedsiden", "Something went wrong!": "Noe gikk galt!", @@ -77,6 +82,7 @@ "Total Points": "Totale poeng", "Transaction date": "Transaksjonsdato", "Transactions": "Transaksjoner", + "User information": "Brukerinformasjon", "Visiting address": "Besøksadresse", "Where should you go next?": "Hvor bør du gå videre?", "Year": "År", diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index 5b61f6f45..4a7aa614e 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -18,17 +18,19 @@ "Continue": "Fortsätt", "Could not find requested resource": "Det gick inte att hitta den begärda resursen", "Country": "Land", + "Country code": "Landskod", "Current level": "Nuvarande nivå", + "Current password": "Nuvarande lösenord", "Date of Birth": "Födelsedatum", "Day": "Dag", "Description": "Beskrivning", + "Discard changes": "Ignorera ändringar", "Edit": "Redigera", "Edit profile": "Redigera profil", "Email": "E-post", "Empty": "Tom", "Explore all levels and benefits": "Utforska alla nivåer och fördelar", "Find booking": "Hitta bokning", - "Free soft drink voucher for the kids when staying": "Gratis läskkupong för barnen när de bor", "Get inspired": "Bli inspirerad", "Go back to overview": "Gå tillbaka till översikten", "How it works": "Hur det fungerar", @@ -45,6 +47,7 @@ "My credit cards": "Mina kreditkort", "My pages": "Mina sidor", "My wishes": "Mina önskningar", + "New password": "Nytt lösenord", "Next": "Nästa", "Next level": "Nästa nivå", "No content published": "Inget innehåll publicerat", @@ -66,10 +69,12 @@ "Previous victories": "Tidigare segrar", "points until next level": "poäng till nästa nivå", "Read more": "Läs mer", + "Retype new password": "Upprepa nytt lösenord", "Save": "Spara", "Select a country": "Välj ett land", "Select country of residence": "Välj bosättningsland", "Select date of birth": "Välj födelsedatum", + "Select language": "Välj språk", "Show more": "Visa mer", "Skip to main content": "Fortsätt till huvudinnehåll", "Something went wrong!": "Något gick fel!", @@ -77,6 +82,7 @@ "Total Points": "Total poäng", "Transaction date": "Transaktionsdatum", "Transactions": "Transaktioner", + "User information": "Användar information", "Visiting address": "Besöksadress", "Where should you go next?": "Vart ska du gå härnäst?", "Year": "År", diff --git a/server/routers/user/output.ts b/server/routers/user/output.ts index b7043781a..2e4b8db10 100644 --- a/server/routers/user/output.ts +++ b/server/routers/user/output.ts @@ -7,6 +7,7 @@ import { countriesMap } from "@/components/TempDesignSystem/Form/Country/countri export const getUserSchema = z.object({ address: z.object({ city: z.string().optional(), + country: z.string().optional(), countryCode: z.nativeEnum(countriesMap).optional(), streetAddress: z.string().optional(), zipCode: z.string(), diff --git a/server/routers/user/query.ts b/server/routers/user/query.ts index 38105a6e5..732ec18a1 100644 --- a/server/routers/user/query.ts +++ b/server/routers/user/query.ts @@ -56,8 +56,6 @@ export const userQueryRouter = router({ } const apiJson = await apiResponse.json() - console.log({ apiJson }) - console.log({ attr: apiJson.data.attributes }) if (!apiJson.data?.attributes) { // throw notFound(apiJson) console.error( @@ -78,12 +76,14 @@ export const userQueryRouter = router({ const country = countries.find( (c) => c.code === verifiedData.data.address.countryCode ) + const phonenumber = parsePhoneNumber(verifiedData.data.phoneNumber) const user = { ...extendedUser, address: { city: verifiedData.data.address.city, - country: country?.name ?? verifiedData.data.address.countryCode, + country: country?.name ?? "", + countryCode: verifiedData.data.address.countryCode, streetAddress: verifiedData.data.address.streetAddress, zipCode: verifiedData.data.address.zipCode, }, @@ -94,7 +94,7 @@ export const userQueryRouter = router({ lastName: verifiedData.data.lastName, memberships: verifiedData.data.memberships, name: `${verifiedData.data.firstName} ${verifiedData.data.lastName}`, - phoneNumber: verifiedData.data.phoneNumber, + phoneNumber: phonenumber.formatInternational(), profileId: verifiedData.data.profileId, } @@ -111,7 +111,6 @@ export const userQueryRouter = router({ user.address.zipCode = maskValue.text(verifiedData.data.address.zipCode) user.email = maskValue.email(user.email) - const phonenumber = parsePhoneNumber(user.phoneNumber) user.phoneNumber = `+${phonenumber.countryCallingCode} ${maskValue.phone(user.phoneNumber)}` } diff --git a/stores/edit-profile.ts b/stores/edit-profile.ts deleted file mode 100644 index 1abce359b..000000000 --- a/stores/edit-profile.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { create } from "zustand" - -interface EditProfileState { - pending: boolean - valid: boolean -} - -interface EditProfileActions { - setIsPending: (isPending: boolean) => void - setValid: (isValid: boolean) => void -} - -export interface EditProfileStore - extends EditProfileActions, - EditProfileState { } - -export const useProfileStore = create()((set) => ({ - pending: false, - valid: true, - - setIsPending: (isPending) => set(() => ({ pending: isPending })), - setValid: (isValid) => set(() => ({ valid: isValid })), -})) diff --git a/types/components/myPages/myProfile/edit.ts b/types/components/myPages/myProfile/edit.ts index 5b4957ad7..326f9d8fb 100644 --- a/types/components/myPages/myProfile/edit.ts +++ b/types/components/myPages/myProfile/edit.ts @@ -1,15 +1,9 @@ -import type { EditProfileSchema } from "@/components/MyProfile/Profile/Edit/Form/schema" -import type { Control } from "react-hook-form" import type { User } from "@/types/user" export type EditFormProps = { user: User } -export type EditFormContentProps = { - control: Control -} - type E = { message: string path: string