feat(WEB-170): edit profile view

This commit is contained in:
Simon Emanuelsson
2024-04-11 18:51:38 +02:00
parent 82e4d40203
commit 9396b2c3d5
114 changed files with 3642 additions and 2171 deletions

View File

@@ -0,0 +1,32 @@
import { cva } from "class-variance-authority"
import Card from "@/components/MyProfile/Card"
import Image from "@/components/Image"
import styles from "./profile.module.css"
import type { ProfileProps } from "@/types/components/myPages/myProfile/profile"
const profileStyles = cva(styles.profile)
export default function Container({
children,
className,
user,
...props
}: ProfileProps) {
return (
<Card className={profileStyles({ className })} {...props}>
<header className={styles.header}>
<Image
alt="Account Icon"
height={40}
src="/account_circle.svg"
width={40}
/>
<Card.Title uppercase>{user.name}</Card.Title>
</header>
{children}
</Card>
)
}

View File

@@ -0,0 +1,126 @@
"use client"
import { useEffect } from "react"
import { useFormStatus } from "react-dom"
import { _ } from "@/lib/translation"
import { useProfileStore } from "@/stores/edit-profile"
import {
CalendarIcon,
EmailIcon,
HouseIcon,
PhoneIcon,
} from "@/components/Icons"
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import DateSelect from "@/components/TempDesignSystem/Form/Date"
import Field from "@/components/MyProfile/Field"
import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone"
import type { EditFormContentProps } from "@/types/components/myPages/myProfile/edit"
export default function FormContent({ control }: EditFormContentProps) {
const { pending } = useFormStatus()
const setIsPending = useProfileStore((store) => store.setIsPending)
useEffect(() => {
setIsPending(pending)
}, [pending])
return (
<>
<Field>
<Field.Icon>SE</Field.Icon>
<Field.Label htmlFor="country">*{_("Country")}</Field.Label>
<Field.Content>
<CountrySelect name="country" />
</Field.Content>
</Field>
<Field>
<Field.Icon>
<CalendarIcon />
</Field.Icon>
<Field.Label htmlFor="dob">*{_("Date of Birth")}</Field.Label>
<Field.Content>
<DateSelect
control={control}
name="dob"
registerOptions={{ required: true }}
/>
</Field.Content>
</Field>
<Field>
<Field.Icon>
<EmailIcon />
</Field.Icon>
<Field.Label htmlFor="email">*{_("Email")}</Field.Label>
<Field.Content>
<Input
control={control}
name="email"
placeholder={_("Email")}
registerOptions={{ required: true }}
type="email"
/>
</Field.Content>
</Field>
<Field>
<Field.Icon>
<PhoneIcon />
</Field.Icon>
<Field.Label htmlFor="phone">*{_("Phone")}</Field.Label>
<Field.Content>
<Phone name="phone" />
</Field.Content>
</Field>
<Field>
<Field.Icon>
<HouseIcon />
</Field.Icon>
<Field.Label htmlFor="street">*{_("Address")}</Field.Label>
<Field.Content>
<Input
control={control}
name="street"
placeholder={_("Street 123")}
registerOptions={{ required: true }}
/>
</Field.Content>
</Field>
<Field>
<Field.Icon>
<HouseIcon />
</Field.Icon>
<Field.Label htmlFor="city">*{_("City/State")}</Field.Label>
<Field.Content>
<Input
control={control}
name="city"
placeholder={_("City")}
registerOptions={{ required: true }}
/>
</Field.Content>
</Field>
<Field>
<Field.Icon>
<HouseIcon />
</Field.Icon>
<Field.Label htmlFor="zip">*{_("Zip code")}</Field.Label>
<Field.Content>
<Input
control={control}
name="zip"
placeholder={_("Zip code")}
registerOptions={{ required: true }}
/>
</Field.Content>
</Field>
</>
)
}

View File

@@ -0,0 +1,5 @@
.form {
display: grid;
gap: 1.8rem;
grid-template-columns: 1fr 1fr;
}

View File

@@ -0,0 +1,61 @@
"use client"
import { FormProvider, useForm } from "react-hook-form"
import { useFormState as useReactFormState } from "react-dom"
import { useEffect } from "react"
import { zodResolver } from "@hookform/resolvers/zod"
import { editProfile } from "@/actions/editProfile"
import { editProfileSchema, type EditProfileSchema } from "./schema"
import { useProfileStore } from "@/stores/edit-profile"
import FormContent from "./Content"
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<State, FormData>(
editProfile,
null
)
const form = useForm<EditProfileSchema>({
defaultValues: {
country: user.country,
city: user.address.city,
dob: user.dob,
email: user.email,
phone: user.phone,
street: user.address.street,
zip: user.address.zipcode,
},
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 (
<FormProvider {...form}>
<form action={formAction} className={styles.form} id="edit-profile">
<FormContent control={form.control} />
</form>
</FormProvider>
)
}

View File

@@ -0,0 +1,29 @@
import { z } from "zod"
import { _ } from "@/lib/translation"
import { phoneValidator } from "@/utils/phoneValidator"
export const editProfileSchema = z.object({
city: z
.string({ required_error: _("City is required") })
.min(1, { message: _("City is required") }),
country: z
.string({ required_error: _("Country is required") })
.min(1, { message: _("Country is required") }),
dob: z
.string({ required_error: _("Date of Birth is required") })
.min(1, { message: _("Date of Birth is required") }),
email: z.string().email(),
phone: phoneValidator(
_("Phone is required"),
_("Please enter a valid phone number")
),
street: z
.string({ required_error: _("Address is required") })
.min(1, { message: _("Address is required") }),
zip: z
.string({ required_error: _("Zip code is required") })
.min(1, { message: _("Zip code is required") }),
})
export type EditProfileSchema = z.infer<typeof editProfileSchema>

View File

@@ -0,0 +1,12 @@
import Container from "../Container"
import Form from "./Form"
import type { ProfileProps } from "@/types/components/myPages/myProfile/profile"
export default function EditProfile(props: ProfileProps) {
return (
<Container {...props}>
<Form user={props.user} />
</Container>
)
}

View File

@@ -1,108 +1,70 @@
import { cva } from "class-variance-authority"
import { _ } from "@/lib/translation"
import Card from "@/components/MyProfile/Card"
import Image from "@/components/Image"
import LabelAndIcon from "../LabelAndIcon"
import {
CalendarIcon,
EmailIcon,
HouseIcon,
PhoneIcon,
} from "@/components/Icons"
import Container from "./Container"
import Field from "../Field"
import styles from "./profile.module.css"
import type { ProfileProps } from "@/types/components/myPages/myProfile/profile"
const profileStyles = cva(styles.profile)
export default function Profile({ className, user, ...props }: ProfileProps) {
export default function Profile(props: ProfileProps) {
return (
<Card className={profileStyles({ className })} {...props}>
<header className={styles.header}>
<Image
alt="Account Icon"
height={40}
src="/account_circle.svg"
width={40}
/>
<Card.Title uppercase>
{user.name}
</Card.Title>
</header>
<Container {...props}>
<section className={styles.info}>
<LabelAndIcon>
<LabelAndIcon.Icon>SE</LabelAndIcon.Icon>
<LabelAndIcon.Label>Country</LabelAndIcon.Label>
<LabelAndIcon.Content>Sweden</LabelAndIcon.Content>
</LabelAndIcon>
<LabelAndIcon>
<LabelAndIcon.Icon>
<Image
alt="Calendar Icon"
height={20}
src="/calendar_month.svg"
width={20}
/>
</LabelAndIcon.Icon>
<LabelAndIcon.Label>Date of Birth</LabelAndIcon.Label>
<LabelAndIcon.Content>27/05/1977</LabelAndIcon.Content>
</LabelAndIcon>
<LabelAndIcon>
<LabelAndIcon.Icon>
<Image
alt="Email Icon"
height={20}
src="/alternate_email.svg"
width={20}
/>
</LabelAndIcon.Icon>
<LabelAndIcon.Label>Email</LabelAndIcon.Label>
<LabelAndIcon.Content>f*********@g****.com</LabelAndIcon.Content>
</LabelAndIcon>
<LabelAndIcon>
<LabelAndIcon.Icon>
<Image
alt="Cellphone Icon"
height={20}
src="/phone.svg"
width={20}
/>
</LabelAndIcon.Icon>
<LabelAndIcon.Label>Phone number</LabelAndIcon.Label>
<LabelAndIcon.Content>+46 ******00</LabelAndIcon.Content>
</LabelAndIcon>
<LabelAndIcon>
<LabelAndIcon.Icon>
<Image
alt="House Icon"
height={20}
src="/home.svg"
width={20}
/>
</LabelAndIcon.Icon>
<LabelAndIcon.Label>Address</LabelAndIcon.Label>
<LabelAndIcon.Content>T***************</LabelAndIcon.Content>
</LabelAndIcon>
<LabelAndIcon>
<LabelAndIcon.Icon>
<Image
alt="House Icon"
height={20}
src="/home.svg"
width={20}
/>
</LabelAndIcon.Icon>
<LabelAndIcon.Label>City/State</LabelAndIcon.Label>
<LabelAndIcon.Content>S*******</LabelAndIcon.Content>
</LabelAndIcon>
<LabelAndIcon>
<LabelAndIcon.Icon>
<Image
alt="House Icon"
height={20}
src="/home.svg"
width={20}
/>
</LabelAndIcon.Icon>
<LabelAndIcon.Label>Zip code</LabelAndIcon.Label>
<LabelAndIcon.Content>1****</LabelAndIcon.Content>
</LabelAndIcon>
<Field>
<Field.Icon>SE</Field.Icon>
<Field.TextLabel>{_("Country")}</Field.TextLabel>
<Field.Content>Sweden</Field.Content>
</Field>
<Field>
<Field.Icon>
<CalendarIcon />
</Field.Icon>
<Field.TextLabel>{_("Date of Birth")}</Field.TextLabel>
<Field.Content>27/05/1977</Field.Content>
</Field>
<Field>
<Field.Icon>
<EmailIcon />
</Field.Icon>
<Field.TextLabel>{_("Email")}</Field.TextLabel>
<Field.Content>f*********@g****.com</Field.Content>
</Field>
<Field>
<Field.Icon>
<PhoneIcon />
</Field.Icon>
<Field.TextLabel>{_("Phone number")}</Field.TextLabel>
<Field.Content>+46 ******00</Field.Content>
</Field>
<Field>
<Field.Icon>
<HouseIcon />
</Field.Icon>
<Field.TextLabel>{_("Address")}</Field.TextLabel>
<Field.Content>T***************</Field.Content>
</Field>
<Field>
<Field.Icon>
<HouseIcon />
</Field.Icon>
<Field.TextLabel>{_("City/State")}</Field.TextLabel>
<Field.Content>S*******</Field.Content>
</Field>
<Field>
<Field.Icon>
<HouseIcon />
</Field.Icon>
<Field.TextLabel>{_("Zip code")}</Field.TextLabel>
<Field.Content>1****</Field.Content>
</Field>
</section>
</Card>
</Container>
)
}