Merged in feat/mypages-parallel-routes (pull request #1388)

feat: my profile - removed all parallel routes

* Removed all parallel routes on my-profile

* Fixed suspense

* Moved components into myprofile folder

* Turn off browser cache on myprofile

* Clear router cache when editing profile

* Clear route cache when adding new credit card

* PR fixes


Approved-by: Joakim Jäderberg
This commit is contained in:
Linus Flood
2025-02-21 11:24:46 +00:00
parent 9cd648fd65
commit 15dbeb9d46
25 changed files with 205 additions and 247 deletions

View File

@@ -0,0 +1,10 @@
.container {
display: grid;
gap: var(--Spacing-x3);
max-width: 510px;
}
.content {
display: grid;
gap: var(--Spacing-x1);
}

View File

@@ -0,0 +1,25 @@
import ManagePreferencesButton from "@/components/Profile/ManagePreferencesButton"
import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n"
import styles from "./communication.module.css"
export default async function CommunicationSlot() {
const intl = await getIntl()
return (
<section className={styles.container}>
<article className={styles.content}>
<Subtitle type="two" color="black">
{intl.formatMessage({ id: "My communication preferences" })}
</Subtitle>
<Body color="black">
{intl.formatMessage({
id: "Tell us what information and updates you'd like to receive, and how, by clicking the link below.",
})}
</Body>
</article>
<ManagePreferencesButton />
</section>
)
}

View File

@@ -0,0 +1,22 @@
.container {
display: grid;
gap: var(--Spacing-x2);
justify-items: flex-start;
max-width: 510px;
}
.content {
display: grid;
gap: var(--Spacing-x1);
}
.cardContainer {
display: grid;
gap: var(--Spacing-x1);
}
@media screen and (min-width: 768px) {
.container {
gap: var(--Spacing-x3);
}
}

View File

@@ -0,0 +1,31 @@
import { serverClient } from "@/lib/trpc/server"
import AddCreditCardButton from "@/components/Profile/AddCreditCardButton"
import CreditCardList from "@/components/Profile/CreditCardList"
import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n"
import styles from "./creditCards.module.css"
export default async function CreditCardSlot() {
const intl = await getIntl()
const creditCards = await serverClient().user.creditCards()
return (
<section className={styles.container}>
<article className={styles.content}>
<Subtitle type="two" color="black">
{intl.formatMessage({ id: "My payment cards" })}
</Subtitle>
<Body color="black">
{intl.formatMessage({
id: "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.",
})}
</Body>
</article>
<CreditCardList initialData={creditCards} />
<AddCreditCardButton />
</section>
)
}

View File

@@ -0,0 +1,22 @@
.container {
display: grid;
gap: var(--Spacing-x3);
max-width: 510px;
}
.content {
display: grid;
gap: var(--Spacing-x1);
}
.card {
margin-top: 2rem;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(3, auto);
gap: 0.5rem;
}
.subTitle {
grid-column: span 2;
}

View File

@@ -0,0 +1,74 @@
import { getMembershipCards } from "@/lib/trpc/memoizedRequests"
import { PlusCircleIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n"
import styles from "./membershipcards.module.css"
export default async function MembershipCardSlot() {
const intl = await getIntl()
const membershipCards = await getMembershipCards()
return (
<section className={styles.container}>
<article className={styles.content}>
<Subtitle color="black">
{intl.formatMessage({ id: "My membership cards" })}
</Subtitle>
</article>
{membershipCards &&
membershipCards.length > 0 &&
membershipCards.map((card, idx) => (
<div className={styles.card} key={idx}>
<Subtitle className={styles.subTitle}>
{intl.formatMessage(
{ id: "Name: {cardMembershipType}" },
{
cardMembershipType: card.membershipType,
}
)}
</Subtitle>
<span>
{intl.formatMessage(
{ id: "Current Points: {points, number}" },
{ points: card.currentPoints }
)}
</span>
<span>
{intl.formatMessage(
{ id: "Member Since: {value}" },
{
value: card.memberSince,
}
)}
</span>
<span>
{intl.formatMessage(
{ id: "Number: {membershipNumber}" },
{
membershipNumber: card.membershipNumber,
}
)}
</span>
<span>
{intl.formatMessage(
{ id: "Expiration Date: {expirationDate}" },
{
expirationDate: card.expirationDate.split("T")[0],
}
)}
</span>
</div>
))}
<Link href="#" variant="icon">
<PlusCircleIcon color="burgundy" />
<Body color="burgundy" textTransform="underlined">
{intl.formatMessage({ id: "Add new card" })}
</Body>
</Link>
</section>
)
}

View File

@@ -0,0 +1,48 @@
.profile {
display: flex;
gap: var(--Spacing-x2);
justify-content: space-between;
}
.info {
display: grid;
gap: var(--Spacing-x-one-and-half) var(--Spacing-x7);
width: 100%;
justify-items: flex-start;
}
.item {
align-items: center;
display: grid;
gap: var(--Spacing-x1);
grid-template-columns: auto auto 1fr;
justify-items: flex-end;
width: 100%;
}
.content {
display: grid;
padding-bottom: var(--Spacing-x9);
position: relative;
}
.container {
background-color: var(--Main-Grey-White);
border-radius: var(--Corner-radius-Large);
display: grid;
gap: var(--Spacing-x3);
padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x4);
}
@media screen and (min-width: 768px) {
.info {
grid-template-columns: repeat(3, auto);
}
.item {
justify-items: flex-start;
}
.container {
padding: var(--Spacing-x3) var(--Spacing-x3) var(--Spacing-x4);
}
}
@media screen and (min-width: 1367px) {
.content {
gap: var(--Spacing-x5);
grid-template-columns: max(340px) 1fr;
}
}

View File

@@ -0,0 +1,132 @@
import { languages, languageSelect } from "@/constants/languages"
import { profileEdit } from "@/constants/routes/myPages"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import {
CalendarIcon,
EmailIcon,
GlobeIcon,
LocationIcon,
LockIcon,
PhoneIcon,
} from "@/components/Icons"
import CommunicationSlot from "@/components/MyPages/myprofile/communication/communication"
import CreditCardSlot from "@/components/MyPages/myprofile/creditCards/creditCards"
import MembershipCardSlot from "@/components/MyPages/myprofile/membershipCards/membershipcards"
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 { getLang } from "@/i18n/serverContext"
import styles from "./profile.module.css"
export default async function Profile() {
const intl = await getIntl()
const lang = getLang()
const user = await getProfile()
if (!user || "error" in user) {
return null
}
const addressParts = []
if (user.address.streetAddress) {
addressParts.push(user.address.streetAddress)
}
if (user.address.city) {
addressParts.push(user.address.city)
}
if (user.address.country) {
addressParts.push(user.address.country)
}
const addressOutput =
addressParts.length > 0
? addressParts.join(", ")
: intl.formatMessage({ id: "N/A" })
const defaultLanguage = languages[lang]
const language = languageSelect.find((l) => l.value === user.language)
return (
<>
<section className={styles.container}>
<Header>
<div>
<Title as="h4" color="red" level="h1" textTransform="capitalize">
{intl.formatMessage({ id: "Welcome" })}
</Title>
<Title
data-hj-suppress
as="h4"
color="burgundy"
level="h2"
textTransform="capitalize"
>
{user.name}
</Title>
</div>
<Button asChild intent="primary" size="small" theme="base">
<Link prefetch={false} color="none" href={profileEdit[lang]}>
{intl.formatMessage({ id: "Edit profile" })}
</Link>
</Button>
</Header>
<div className={styles.profile}>
<div className={styles.info}>
<div className={styles.item}>
<CalendarIcon color="burgundy" />
<Body color="burgundy" textTransform="bold">
{intl.formatMessage({ id: "Date of Birth" })}
</Body>
<Body color="burgundy">{user.dateOfBirth}</Body>
</div>
<div className={styles.item}>
<PhoneIcon color="burgundy" />
<Body color="burgundy" textTransform="bold">
{intl.formatMessage({ id: "Phone number" })}
</Body>
<Body color="burgundy">{user.phoneNumber}</Body>
</div>
<div className={styles.item}>
<GlobeIcon color="burgundy" />
<Body color="burgundy" textTransform="bold">
{intl.formatMessage({ id: "Language" })}
</Body>
<Body color="burgundy">{language?.label ?? defaultLanguage}</Body>
</div>
<div className={styles.item}>
<EmailIcon color="burgundy" />
<Body color="burgundy" textTransform="bold">
{intl.formatMessage({ id: "Email" })}
</Body>
<Body color="burgundy">{user.email}</Body>
</div>
<div className={styles.item}>
<LocationIcon color="burgundy" />
<Body color="burgundy" textTransform="bold">
{intl.formatMessage({ id: "Address" })}
</Body>
<Body color="burgundy">{addressOutput}</Body>
</div>
<div className={styles.item}>
<LockIcon color="burgundy" />
<Body color="burgundy" textTransform="bold">
{intl.formatMessage({ id: "Password" })}
</Body>
<Body color="burgundy">**********</Body>
</div>
</div>
</div>
<Divider color="burgundy" opacity={8} />
<CreditCardSlot />
<MembershipCardSlot />
<CommunicationSlot />
</section>
</>
)
}