feat(WEB-154): my profile view

This commit is contained in:
Simon Emanuelsson
2024-04-05 08:28:20 +02:00
parent 3b05b9f205
commit 82e4d40203
95 changed files with 1239 additions and 196 deletions

View File

@@ -1,3 +1,20 @@
export const breadcrumbs = {
"/my-pages": [
{
title: "My Pages"
}
],
"/my-pages/profile": [
{
href: "/my-pages",
title: "My Pages"
},
{
title: "My Profile",
},
],
}
export const challenges = {
journeys: [
{
@@ -84,3 +101,14 @@ export const stays = [
hotel: "Scandic Oslo City",
},
]
export const extendedUser = {
journeys: challenges.journeys,
membershipId: 30812404844732,
nights: 14,
points: 20720,
qualifyingPoints: 5000,
shortcuts,
stays,
victories: challenges.victories,
}

View File

@@ -1,31 +1,28 @@
.page {
.layout {
--max-width: 101.4rem;
--header-height: 4.5rem;
display: grid;
font-family: var(--ff-fira-sans);
grid-template-rows: auto 1fr;
grid-template-rows: var(--header-height) auto 1fr;
min-height: 100dvh;
}
.content {
display: grid;
padding: 0 0 17.5rem;
padding-bottom: 7.7rem;
padding-left: 0;
padding-right: 0;
padding-top: 0;
position: relative;
}
@media screen and (min-width: 950px) {
.page {
gap: 5.8rem;
}
.content {
gap: 10rem;
grid-template-columns: 25rem 1fr;
padding-bottom: 17.5rem;
padding-left: 2.4rem;
padding-right: 2.4rem;
padding-top: 5.8rem;
}
}
}

View File

@@ -1,5 +1,7 @@
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
import { breadcrumbs } from "./_constants"
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
import Header from "@/components/MyPages/Header"
import Sidebar from "@/components/MyPages/Sidebar"
@@ -54,8 +56,11 @@ export default async function MyPagesLayout({
const menuItems = mapMenuItems(navigation.items)
return (
<div className={`${firaMono.variable} ${firaSans.variable} ${styles.page}`}>
<div
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
>
<Header lang={params.lang} />
<Breadcrumbs breadcrumbs={breadcrumbs} lang={params.lang} />
<div className={styles.content}>
<Sidebar menuItems={menuItems} />
{children}

View File

@@ -1,7 +1,6 @@
.blocks {
display: grid;
gap: 4.2rem;
max-width: var(--max-width);
padding-left: 2rem;
padding-right: 2rem;
}
@@ -12,4 +11,4 @@
padding-left: 0;
padding-right: 0;
}
}
}

View File

@@ -1,7 +1,8 @@
import { serverClient } from "@/lib/trpc/server"
import { challenges, shortcuts, stays } from "./_constants"
import { extendedUser } from "./_constants"
import MaxWidth from "@/components/MaxWidth"
import Overview from "@/components/MyPages/Blocks/Overview"
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
import UpcomingStays from "@/components/MyPages/Blocks/UpcomingStays"
@@ -14,17 +15,10 @@ export default async function MyPage({ params }: PageArgs<LangParams>) {
const data = await serverClient().user.get()
const user = {
...data,
journeys: challenges.journeys,
membershipId: 30812404844732,
nights: 14,
points: 20720,
qualifyingPoints: 5000,
shortcuts,
stays,
victories: challenges.victories,
...extendedUser,
}
return (
<main className={styles.blocks}>
<MaxWidth className={styles.blocks} tag="main">
<Overview user={user} />
<UpcomingStays lang={params.lang} stays={user.stays} />
<Shortcuts
@@ -32,6 +26,6 @@ export default async function MyPage({ params }: PageArgs<LangParams>) {
title="Handy Shortcuts"
subtitle="The community at your fingertips"
/>
</main>
</MaxWidth>
)
}

View File

@@ -0,0 +1,3 @@
export default function DefaultEdit() {
return null
}

View File

@@ -0,0 +1,10 @@
import Button from "@/components/TempDesignSystem/Button";
export default function EditProfile() {
return (
<>
<Button form="edit-profile" type="reset">Cancel</Button>
<Button form="edit-profile" type="submit">Save</Button>
</>
)
}

View File

@@ -0,0 +1,11 @@
import Modal from "@/components/Modal";
export default function VerifyCode() {
return (
<Modal>
<Modal.Header>
<Modal.Title>Verify Code</Modal.Title>
</Modal.Header>
</Modal>
)
}

View File

@@ -0,0 +1,3 @@
export default function Default() {
return null
}

View File

@@ -0,0 +1,22 @@
import Button from "@/components/TempDesignSystem/Button";
import Link from "@/components/TempDesignSystem/Link";
import styles from "./view.module.css"
import type { LangParams, PageArgs } from "@/types/params";
export default function ProfileView({ params }: PageArgs<LangParams>) {
return (
<Button
asChild
bgcolor="quarternary"
className={styles.btn}
size="small"
weight="regular"
>
<Link href={`/${params.lang}/my-pages/profile/verify`}>
Edit
</Link>
</Button>
)
}

View File

@@ -0,0 +1,3 @@
.btn {
position: absolute;
}

View File

@@ -0,0 +1,16 @@
type ProfileLayoutProps = React.PropsWithChildren<{
edit: React.ReactNode
verifyCode: React.ReactNode
view: React.ReactNode
}>
export default function ProfileLayout({ children, edit, verifyCode, view }: ProfileLayoutProps) {
return (
<>
{edit}
{view}
{children}
{verifyCode}
</>
)
}

View File

@@ -0,0 +1,10 @@
.page {
display: grid;
gap: 3rem;
}
.cards {
display: grid;
gap: 0.4rem;
grid-template-columns: 1fr 1fr;
}

View File

@@ -0,0 +1,36 @@
import { serverClient } from "@/lib/trpc/server";
import { extendedUser } from "../_constants";
import CommunicationPreferences from "@/components/MyProfile/CommunicationPreferences";
import CreditCards from "@/components/MyProfile/CreditCards";
import MaxWidth from "@/components/MaxWidth";
import MembershipCard from "@/components/MyProfile/MembershipCard";
import Password from "@/components/MyProfile/Password";
import Profile from "@/components/MyProfile/Profile";
import Wishes from "@/components/MyProfile/Wishes";
import styles from "./page.module.css"
import Modal from "@/components/Modal";
export default async function MyProfile() {
const data = await serverClient().user.get()
const user = {
...data,
...extendedUser,
}
return (
<MaxWidth className={styles.page} tag="main">
<Modal>
<h1>HALLÅ ELLER!?!</h1>
</Modal>
<Profile user={user} />
<section className={styles.cards}>
<CommunicationPreferences />
<Wishes />
<MembershipCard />
<CreditCards />
<Password />
</section>
</MaxWidth>
)
}

View File

@@ -0,0 +1,9 @@
export default function VerifyPage() {
return (
<section>
<header>
<h1>Verify that code already!</h1>
</header>
</section>
)
}

View File

@@ -0,0 +1,17 @@
import { cva } from "class-variance-authority"
import styles from "./maxWidth.module.css"
import type { MaxWidthProps } from "@/types/components/max-width"
const maxWidthVariants = cva(styles.container)
export default function MaxWidth({
className,
tag = "section",
...props
}: MaxWidthProps
) {
const Cmp = tag
return <Cmp className={maxWidthVariants({ className })} {...props} />
}

View File

@@ -0,0 +1,3 @@
.container {
max-width: var(--max-width);
}

View File

@@ -0,0 +1 @@
.header {}

View File

@@ -0,0 +1,9 @@
import styles from "./header.module.css"
export default function Header({ children }: React.PropsWithChildren) {
return (
<header className={styles.header}>
{children}
</header>
)
}

View File

@@ -0,0 +1,22 @@
import { forwardRef } from "react"
import styles from "./modal.module.css"
const Modal = forwardRef<HTMLDivElement, React.PropsWithChildren>(
function ({ children }, ref) {
return (
<div
className={styles.modal}
ref={ref}
role="dialog"
tabIndex={-1}
>
{children}
</div>
)
}
)
Modal.displayName = "Modal"
export default Modal

View File

@@ -0,0 +1,11 @@
.modal {
background-color: var(--some-white-color, #F2F2F2);
border-radius: 0.4rem;
left: 50%;
outline: none;
overflow: auto;
padding: 3.5rem 7rem;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}

View File

@@ -0,0 +1,44 @@
"use client"
import { useCallback, useLayoutEffect } from "react";
import { useRouter } from "next/navigation";
import { useHandleKeyPress } from "@/hooks/useHandleKeyPress";
import styles from "./overlay.module.css"
export default function Overlay({ children }: React.PropsWithChildren) {
const router = useRouter()
const handleOnClose = useCallback(() => {
return router.back()
}, [router])
const handleOnEscape = useCallback((evt: KeyboardEvent) => {
if (evt.code === "Escape") {
handleOnClose()
}
}, [handleOnClose])
useHandleKeyPress(handleOnEscape)
useLayoutEffect(() => {
// Get original body overflow
const originalStyle = window.getComputedStyle(document.body).overflow;
// Prevent scrolling on mount
document.body.style.overflow = 'hidden';
// Re-enable scrolling when component unmounts
return () => {
document.body.style.overflow = originalStyle;
};
}, []);
return (
<div
className={styles.overlay}
onClick={handleOnClose}
role="button"
>
{children}
</div>
);
};

View File

@@ -0,0 +1,9 @@
.overlay {
background-color: rgba(0, 0, 0, 0.3);
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
z-index: 9999;
}

View File

@@ -0,0 +1,9 @@
"use client"
import { createPortal } from 'react-dom';
export default function Portal({ children }: React.PropsWithChildren) {
return createPortal(
children,
document.body
);
};

View File

@@ -0,0 +1,9 @@
import styles from "./title.module.css"
export default function Title({ children }: React.PropsWithChildren) {
return (
<h1 className={styles.heading}>
{children}
</h1>
)
}

View File

@@ -0,0 +1 @@
.heading {}

View File

@@ -0,0 +1,20 @@
import Header from "./Header"
import UiModal from "./Modal"
import Overlay from "./Overlay"
import Portal from "./Portal"
import Title from "./Title"
export default function Modal({ children }: React.PropsWithChildren) {
return (
<Portal>
<Overlay>
<UiModal>
{children}
</UiModal>
</Overlay>
</Portal>
)
}
Modal.Header = Header
Modal.Title = Title

View File

@@ -3,7 +3,7 @@ import Title from "@/components/MyPages/Title"
import styles from "./challenges.module.css"
import type { ChallengesProps } from "@/types/components/myPages/challenges"
import type { ChallengesProps } from "@/types/components/myPages/myPage/challenges"
export default function Challenges({ journeys, victories }: ChallengesProps) {
return (

View File

@@ -2,7 +2,7 @@ import Image from "@/components/Image"
import styles from "./friend.module.css"
import type { FriendProps } from "@/types/components/myPages/friend"
import type { FriendProps } from "@/types/components/myPages/myPage/friend"
export default function Friend({ user }: FriendProps) {
return (

View File

@@ -3,7 +3,7 @@ import Image from "@/components/Image"
import styles from "./points.module.css"
import type { QualifyingPointsProps } from "@/types/components/myPages/qualifyingPoints"
import type { QualifyingPointsProps } from "@/types/components/myPages/myPage/qualifyingPoints"
export default function QualifyingPoints({ user }: QualifyingPointsProps) {
return (

View File

@@ -3,7 +3,7 @@ import Title from "../Title"
import styles from "./totalPoints.module.css"
import type { TotalPointsProps } from "@/types/components/myPages/totalPoints"
import type { TotalPointsProps } from "@/types/components/myPages/myPage/totalPoints"
export default function TotalPoints({ user }: TotalPointsProps) {
return (

View File

@@ -3,7 +3,7 @@ import TotalPoints from "./TotalPoints"
import styles from "./stats.module.css"
import type { StatsProps } from "@/types/components/myPages/stats"
import type { StatsProps } from "@/types/components/myPages/myPage/stats"
export default function Stats({ user }: StatsProps) {
return (

View File

@@ -4,7 +4,7 @@ import Title from "@/components/MyPages/Title"
import styles from "./overview.module.css"
import type { OverviewProps } from "@/types/components/myPages/overview"
import type { OverviewProps } from "@/types/components/myPages/myPage/overview"
export default function Overview({ user }: OverviewProps) {
return (

View File

@@ -4,7 +4,7 @@ import Title from "@/components/MyPages/Title"
import styles from "./shortcuts.module.css"
import type { ShortcutsProps } from "@/types/components/myPages/shortcuts"
import type { ShortcutsProps } from "@/types/components/myPages/myPage/shortcuts"
export default function Shortcuts({
shortcuts,

View File

@@ -6,7 +6,7 @@ import Title from "@/components/MyPages/Title"
import styles from "./stay.module.css"
import type { LangParams } from "@/types/params"
import type { StayProps } from "@/types/components/myPages/stays"
import type { StayProps } from "@/types/components/myPages/myPage/stays"
export default function Stay({
dateArrive,

View File

@@ -4,7 +4,7 @@ import Title from "@/components/MyPages/Title"
import styles from "./upcoming.module.css"
import type { LangParams } from "@/types/params"
import type { StaysProps } from "@/types/components/myPages/stays"
import type { StaysProps } from "@/types/components/myPages/myPage/stays"
import Link from "next/link"
export default function UpcomingStays({

View File

@@ -0,0 +1,49 @@
"use client"
import { Fragment } from "react"
import { usePathname } from "next/navigation"
import Link from "@/components/TempDesignSystem/Link"
import styles from "./breadcrumbs.module.css"
import type { BreadcrumbsProps } from "@/types/components/myPages/breadcrumbs"
export default function ClientBreadcrumbs({ breadcrumbs, lang }: BreadcrumbsProps) {
const pathname = usePathname()
/** Temp solution until we can get breadcrumbs from CS */
const path = pathname.replace(`/${lang}`, '')
const currentBreadcrumbs = breadcrumbs?.[path]
if (!currentBreadcrumbs?.length) {
return null
}
return (
<>
<li className={styles.listItem}>
<span>/</span>
</li>
{currentBreadcrumbs.map(breadcrumb => {
if (breadcrumb.href) {
return (
<Fragment key={breadcrumb.title}>
<li className={styles.listItem}>
<Link className={styles.link} href={breadcrumb.href}>
{breadcrumb.title}
</Link>
</li>
<li className={styles.listItem}>
<span>/</span>
</li>
</Fragment>
)
}
return (
<li className={styles.listItem} key={breadcrumb.title}>
<p className={styles.currentPage}>{breadcrumb.title}</p>
</li>
)
})}
</>
)
}

View File

@@ -0,0 +1,40 @@
.breadcrumbs {
background-color: var(--some-grey-color, #f2f2f2);
display: block;
padding-bottom: 0.8rem;
padding-left: 2rem;
padding-top: 3rem;
position: sticky;
top: var(--header-height);
z-index: 999;
}
.list {
align-items: center;
display: flex;
gap: 0.4rem;
justify-content: flex-start;
list-style: none;
margin: 0;
padding: 0;
}
.listItem,
.link {
color: var(--some-text-color, #000);
font-size: 1.4rem;
font-weight: 400;
line-height: 1.56rem;
}
.currentPage {
margin: 0;
}
@media screen and (min-width: 950px) {
.breadcrumbs {
background-color: var(--some-white-color, #fff);
padding-left: 2.4rem;
padding-top: 2rem;
}
}

View File

@@ -0,0 +1,21 @@
import ClientBreadcrumbs from "./Client"
import Link from "@/components/TempDesignSystem/Link"
import styles from "./breadcrumbs.module.css"
import type { BreadcrumbsProps } from "@/types/components/myPages/breadcrumbs"
export default function Breadcrumbs({ breadcrumbs, lang }: BreadcrumbsProps) {
return (
<nav className={styles.breadcrumbs}>
<ul className={styles.list}>
<li className={styles.listItem}>
<Link className={styles.link} href="#">
Home
</Link>
</li>
<ClientBreadcrumbs breadcrumbs={breadcrumbs} lang={lang} />
</ul>
</nav>
)
}

View File

@@ -1,25 +0,0 @@
.list {
align-items: center;
display: flex;
gap: 0.4rem;
justify-content: flex-start;
list-style: none;
margin: 0;
padding: 0;
}
.listItem,
.link {
color: var(--some-text-color, #000);
font-size: 1.4rem;
font-weight: 400;
line-height: 1.56rem;
}
.link {
text-decoration: none;
}
.currentPage {
margin: 0;
}

View File

@@ -1,23 +0,0 @@
import Link from "next/link"
import styles from "./breadcrumbs.module.css"
export default function Breadcrumbs() {
return (
<nav className={styles.breadcrumbs}>
<ul className={styles.list}>
<li className={styles.listItem}>
<Link className={styles.link} href="#">
Home
</Link>
</li>
<li className={styles.listItem}>
<span>/</span>
</li>
<li className={styles.listItem}>
<p className={styles.currentPage}>My Scandic</p>
</li>
</ul>
</nav>
)
}

View File

@@ -1,9 +1,3 @@
.container {
position: sticky;
top: 0;
z-index: 999;
}
.header {
align-items: center;
background-color: var(--some-white-color, #fff);
@@ -11,16 +5,12 @@
display: grid;
gap: 3rem;
grid-template-columns: 1fr auto auto;
height: 7rem;
padding: 0 2rem;
}
height: var(--header-height);
.breadcrumbs {
background-color: var(--some-grey-color, #f2f2f2);
display: block;
padding-bottom: 0.8rem;
padding-left: 2rem;
padding-top: 3rem;
padding: 0 2rem;
position: sticky;
top: 0;
z-index: 999;
}
@media screen and (min-width: 950px) {
@@ -30,13 +20,6 @@
box-shadow: none;
gap: 3.2rem;
grid-template-columns: 1fr 19rem auto auto;
height: 4.5rem;
padding: 0 2.4rem;
}
.breadcrumbs {
background-color: var(--some-white-color, #fff);
padding-left: 2.4rem;
padding-top: 2rem;
}
}
}

View File

@@ -1,4 +1,3 @@
import Breadcrumbs from "./Breadcrumbs"
import Hamburger from "./Hamburger"
import LanguageSwitcher from "./LanguageSwitcher"
import Logo from "./Logo"
@@ -10,16 +9,11 @@ import type { LangParams } from "@/types/params"
export default function Header({ lang }: LangParams) {
return (
<div className={styles.container}>
<header className={styles.header}>
<Logo lang={lang} />
<LanguageSwitcher />
<User />
<Hamburger />
</header>
<div className={styles.breadcrumbs}>
<Breadcrumbs />
</div>
</div>
<header className={styles.header}>
<Logo lang={lang} />
<LanguageSwitcher />
<User />
<Hamburger />
</header>
)
}

View File

@@ -0,0 +1,46 @@
"use client"
import { usePathname } from "next/navigation"
import Link from "@/components/TempDesignSystem/Link"
import type { LangParams } from "@/types/params"
export default function ClientSidebar({ lang }: LangParams) {
const pathname = usePathname()
return (
<>
<Link
currentPath={pathname}
href={`/${lang}/my-pages`}
variant="sidebar"
>
My Pages
</Link>
<Link currentPath={pathname} href="#" variant="sidebar">
My Stays
</Link>
<Link currentPath={pathname} href="#" variant="sidebar">
My Points
</Link>
<Link currentPath={pathname} href="#" variant="sidebar">
My Benefits
</Link>
{/* <Link currentPath={pathname} href="#" variant="sidebar">
My Challenges
</Link>
<Link currentPath={pathname} href="#" variant="sidebar">
My Favourites
</Link> */}
<Link currentPath={pathname} href="#" variant="sidebar">
About Scandic Friends
</Link>
<Link
currentPath={pathname}
href={`/${lang}/my-pages/profile`}
variant="sidebar"
>
My Profile
</Link>
</>
)
}

View File

@@ -1,10 +1,10 @@
"use client"
import { Fragment } from "react"
import { LogOut } from "react-feather"
import Link from "../../TempDesignSystem/Link"
import Link from "@/components/TempDesignSystem/Link"
import styles from "./sidebar.module.css"
import { SidebarProps } from "@/types/requests/myPages/navigation"
import { Fragment } from "react"
import type { SidebarProps } from "@/types/requests/myPages/navigation"
export default function Sidebar({ menuItems }: SidebarProps) {
return (
@@ -17,16 +17,16 @@ export default function Sidebar({ menuItems }: SidebarProps) {
</Link>
{item.subItems
? item.subItems.map((subItem) => {
return (
<Link
key={subItem.uid}
href={subItem.url}
variant={"sidebar"}
>
{subItem.linkText}
</Link>
)
})
return (
<Link
key={subItem.uid}
href={subItem.url}
variant={"sidebar"}
>
{subItem.linkText}
</Link>
)
})
: null}
</Fragment>
))}

View File

@@ -2,7 +2,7 @@
align-self: flex-start;
display: none;
position: sticky;
top: 13.2rem;
top: 14.6rem;
}
.nav {
@@ -13,33 +13,8 @@
padding-left: 4rem;
}
.link {
align-items: center;
color: var(--some-text-color, #111);
display: flex;
font-size: 1.6rem;
font-weight: 400;
gap: 0.6rem;
line-height: 1.9rem;
position: relative;
text-decoration: none;
}
.active {
font-weight: 600;
}
.active::before {
bottom: -0.4rem;
background-color: var(--some-text-color, #000);
content: "";
height: 0.2rem;
position: absolute;
width: 100%;
}
@media screen and (min-width: 950px) {
.sidebar {
display: block;
}
}
}

View File

@@ -1,41 +1,21 @@
import { cva } from "class-variance-authority"
import styles from "./title.module.css"
import { headingVariants } from "./variants"
import type { HeadingProps } from "@/types/components/myPages/title"
const config = {
variants: {
text: {
uppercase: styles.uppercase,
},
type: {
h1: styles.h1,
h2: styles.h2,
h3: styles.h3,
h4: styles.h4,
h5: styles.h5,
},
},
defaultVariants: {
type: "h1",
},
} as const
const headingStyles = cva(styles.heading, config)
export default function Title({
as,
children,
className = "",
level = "h1",
uppercase = false,
weight,
}: HeadingProps) {
const Hx = level
const classNames = headingStyles({
const classNames = headingVariants({
className,
text: uppercase ? "uppercase" : undefined,
type: as ?? level,
weight,
})
return <Hx className={classNames}>{children}</Hx>
}

View File

@@ -1,5 +1,5 @@
.heading {
font-weight: 900;
/* font-family: var(--ff-brandon-text); */
margin: 0;
padding: 0;
}
@@ -33,6 +33,30 @@
line-height: var(--typography-Title5-Mobile-lineHeight);
}
.light {
font-weight: 300;
}
.regular {
font-weight: 400;
}
.medium {
font-weight: 500;
}
.semiBold {
font-weight: 600;
}
.bold {
font-weight: 700;
}
.black {
font-weight: 900;
}
@media screen and (min-width: 950px) {
.h1 {
font-size: var(--typography-Title1-Desktop-fontSize);
@@ -58,4 +82,4 @@
font-size: var(--typography-Title5-Desktop-fontSize);
line-height: var(--typography-Title5-Desktop-lineHeight);
}
}
}

View File

@@ -0,0 +1,33 @@
import { cva } from "class-variance-authority"
import styles from "./title.module.css"
const config = {
variants: {
text: {
uppercase: styles.uppercase,
},
type: {
h1: styles.h1,
h2: styles.h2,
h3: styles.h3,
h4: styles.h4,
h5: styles.h5,
h6: styles.h6,
},
weight: {
light: styles.light,
regular: styles.regular,
medium: styles.medium,
semiBold: styles.semiBold,
bold: styles.bold,
black: styles.black,
},
},
defaultVariants: {
type: "h1",
weight: "black",
},
} as const
export const headingVariants = cva(styles.heading, config)

View File

@@ -0,0 +1,21 @@
import { headingVariants } from "./variants"
import type { HeadingProps } from "@/types/components/myPages/myProfile/card/title"
export default function Title({
as,
children,
className = "",
level = "h1",
uppercase = false,
weight,
}: HeadingProps) {
const Hx = level
const classNames = headingVariants({
className,
text: uppercase ? "uppercase" : undefined,
type: as ?? level,
weight,
})
return <Hx className={classNames}>{children}</Hx>
}

View File

@@ -0,0 +1,68 @@
.heading {
color: var(--some-black-color, #2E2E2E);
/* font-family: var(--ff-brandon-text); */
letter-spacing: 6%;
margin: 0;
padding: 0;
}
.uppercase {
text-transform: uppercase;
}
.h1 {
font-size: 2.5rem;
line-height: 3.5rem;
}
.h2 {
font-size: 1.3rem;
line-height: 1.6rem;
}
.h3 {
font-size: 1.8rem;
}
.h4 {
font-size: 1.6rem;
}
.h5 {
font-size: 1.3rem;
}
.h6 {
font-size: 1rem;
}
.light {
font-weight: 300;
}
.regular {
font-weight: 400;
}
.medium {
font-weight: 500;
}
.semiBold {
font-weight: 600;
}
.bold {
font-weight: 700;
}
.black {
font-weight: 900;
}
/* @media screen and (min-width: 950px) {
.h1 {
font-size: 3.8rem;
line-height: 4.5rem;
}
} */

View File

@@ -0,0 +1,33 @@
import { cva } from "class-variance-authority"
import styles from "./title.module.css"
const config = {
variants: {
text: {
uppercase: styles.uppercase,
},
type: {
h1: styles.h1,
h2: styles.h2,
h3: styles.h3,
h4: styles.h4,
h5: styles.h5,
h6: styles.h6,
},
weight: {
light: styles.light,
regular: styles.regular,
medium: styles.medium,
semiBold: styles.semiBold,
bold: styles.bold,
black: styles.black,
},
},
defaultVariants: {
type: "h1",
weight: "medium",
},
} as const
export const headingVariants = cva(styles.heading, config)

View File

@@ -0,0 +1,6 @@
.card {
background-color: var(--some-grey-color, #F2F2F2);
border-radius: 0.4rem;
min-height: 15.6rem;
padding: 3.8rem;
}

View File

@@ -0,0 +1,16 @@
import { cva } from "class-variance-authority"
import Title from "./Title"
import styles from "./card.module.css"
import type { CardProps } from "@/types/components/myPages/myProfile/card/card"
const cardStyles = cva(styles.card)
export default function Card({ className, tag = "section", ...props }: CardProps) {
const Cmp = tag
return <Cmp className={cardStyles({ className })} {...props} />
}
Card.Title = Title

View File

@@ -0,0 +1,5 @@
.container {
align-items: center;
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,11 @@
import Card from "@/components/MyProfile/Card"
import styles from "./com.module.css"
export default function CommunicationPreferences() {
return (
<Card className={styles.container}>
<Card.Title level="h2" uppercase>My communication preferences</Card.Title>
</Card>
)
}

View File

@@ -0,0 +1,5 @@
.container {
align-items: center;
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,11 @@
import Card from "@/components/MyProfile/Card"
import styles from "./creditCards.module.css"
export default function CreditCards() {
return (
<Card className={styles.container}>
<Card.Title level="h2" uppercase>My credit cards</Card.Title>
</Card>
)
}

View File

@@ -0,0 +1,25 @@
import styles from "./lai.module.css"
export default function LabelAndIcon({ children }: React.PropsWithChildren) {
return (
<div className={styles.container}>
{children}
</div>
)
}
function Icon({ children }: React.PropsWithChildren) {
return <span className={styles.icon}>{children}</span>
}
function Label({ children }: React.PropsWithChildren) {
return <span className={styles.label}>{children}</span>
}
function Content({ children }: React.PropsWithChildren) {
return <span className={styles.content}>{children}</span>
}
LabelAndIcon.Icon = Icon
LabelAndIcon.Label = Label
LabelAndIcon.Content = Content

View File

@@ -0,0 +1,45 @@
.container {
align-content: flex-start;
align-items: center;
display: grid;
gap: 0.4rem 1.7rem;
grid-template-areas:
"icon label"
"icon content";
justify-content: flex-start;
}
.icon {
align-items: center;
background-color: var(--some-white-color, #FFF);
border-radius: 50%;
display: flex;
font-family: var(--ff-fira-sans);
font-size: 1.6rem;
font-weight: 400;
grid-area: icon;
height: 3rem;
justify-content: center;
line-height: 1.9rem;
width: 3rem;
}
.label,
.content {
font-family: var(--ff-fira-sans);
font-weight: 400;
letter-spacing: -1.5%;
line-height: 2.4rem;
}
.label {
color: var(--some-black-color, #404040);
grid-area: label;
font-size: 1.6rem;
}
.content {
color: var(--some-black-color, #000);
grid-area: content;
font-size: 1.8rem;
}

View File

@@ -0,0 +1,11 @@
import Card from "@/components/MyProfile/Card"
import styles from "./membershipCard.module.css"
export default function MembershipCard() {
return (
<Card className={styles.container}>
<Card.Title level="h2" uppercase>Membership cards</Card.Title>
</Card>
)
}

View File

@@ -0,0 +1,5 @@
.container {
align-items: center;
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,11 @@
import Card from "@/components/MyProfile/Card"
import styles from "./password.module.css"
export default function Password() {
return (
<Card className={styles.container}>
<Card.Title level="h2" uppercase>Password</Card.Title>
</Card>
)
}

View File

@@ -0,0 +1,5 @@
.container {
align-items: center;
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,108 @@
import { cva } from "class-variance-authority"
import Card from "@/components/MyProfile/Card"
import Image from "@/components/Image"
import LabelAndIcon from "../LabelAndIcon"
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) {
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>
<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>
</section>
</Card>
)
}

View File

@@ -0,0 +1,19 @@
.profile {
display: grid;
gap: 3.4rem;
grid-template-rows: auto 1fr;
min-height: 46rem;
}
.header {
align-items: center;
display: grid;
gap: 1.4rem;
grid-template-columns: auto 1fr;
}
.info {
display: grid;
gap: 1.8rem;
grid-template-columns: 1fr 1fr;
}

View File

@@ -0,0 +1,11 @@
import Card from "@/components/MyProfile/Card"
import styles from "./wishes.module.css"
export default function Wishes() {
return (
<Card className={styles.container}>
<Card.Title level="h2" uppercase>My wishes</Card.Title>
</Card>
)
}

View File

@@ -0,0 +1,5 @@
.container {
align-items: center;
display: flex;
justify-content: center;
}

View File

@@ -72,3 +72,79 @@
color: var(--some-grey-color, #444343);
cursor: not-allowed;
}
.small {
border-radius: 3rem;
font-size: 1.4rem;
height: 2.6rem;
line-height: 1.7rem;
padding: 0.8rem 2.2rem;
}
.average {
border-radius: 4.7rem;
font-size: 1.4rem;
height: 3.2rem;
letter-spacing: 1%;
line-height: 1.6rem;
padding: 0.65rem 1.3rem;
}
.light {
font-weight: 300;
}
.regular {
font-weight: 400;
}
.medium {
font-weight: 500;
}
.semiBold {
font-weight: 600;
}
.bold {
font-weight: 700;
}
.black {
font-weight: 900;
}
.primary {
background-color: var(--scandic-blue, #02838E);
border: 0.1rem solid var(--scandic-blue, #02838E);
color: var(--some-white-color, #FFF);
}
.secondary {
background-color: var(--some-black-color, #000);
border: 0.1rem solid var(--some-black-color, #000);
color: var(--some-white-color, #FFF);
}
.tertiary {
background-color: var(--some-red-color, #D60728);
border: 0.1rem solid var(--some-red-color, #D60728);
color: var(--some-white-color, #FFF);
}
.quarternary {
background-color: var(--some-grey-color, #727272);
border: 0.1rem solid var(--some-black-color, #727272);
color: var(--some-white-color, #FFF);
}
.white {
background-color: var(--some-white-color, #FFF);
border: 0.1rem solid var(--some-black-color, #000);
color: var(--some-black-color, #000);
}
.disabled {
background-color: var(--some-grey-color, #D9D9D9);
color: var(--some-grey-color, #757575);
}

View File

@@ -8,16 +8,22 @@ import type { ButtonProps } from "./button"
export default function Button({
asChild = false,
bgcolor,
className,
disabled,
size,
variant,
intent,
weight,
...props
}: ButtonProps) {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={buttonVariants({ className, variant, intent })}
{...props}
/>
)
const classNames = buttonVariants({
bgcolor,
className,
disabled,
size,
variant,
weight,
})
return <Comp className={classNames} disabled={disabled} {...props} />
}

View File

@@ -4,16 +4,32 @@ import styles from "./button.module.css"
export const buttonVariants = cva(styles.btn, {
variants: {
bgcolor: {
primary: styles.primary,
secondary: styles.secondary,
tertiary: styles.tertiary,
quarternary: styles.quarternary,
white: styles.white,
},
size: {
small: styles.small,
regular: styles.average,
},
variant: {
default: styles.default,
icon: styles.icon,
},
intent: {
primary: styles.primary,
secondary: styles.secondary,
weight: {
light: styles.light,
regular: styles.regular,
medium: styles.medium,
semiBold: styles.semiBold,
bold: styles.bold,
black: styles.black,
},
},
defaultVariants: {
variant: "default",
weight: "regular",
},
})

View File

@@ -28,4 +28,4 @@
height: 0.2rem;
position: absolute;
width: 100%;
}
}

View File

@@ -4,6 +4,6 @@ import type { VariantProps } from "class-variance-authority"
export interface LinkProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement>,
VariantProps<typeof linkVariants> {
VariantProps<typeof linkVariants> {
href: string
}

17
constants/myPages.js Normal file
View File

@@ -0,0 +1,17 @@
export const pageNames = {
da: "mine-sider",
de: "mein-profil",
en: "my-pages",
fi: "minun-sivujani",
no: "mine-sider",
sv: "mina-sidor",
}
export const profilePageNames = {
da: "mine-sider/profil",
de: "mein-profil/profil",
en: "my-pages/profile",
fi: "minun-sivujani/profil",
no: "mine-sider/profil",
sv: "mina-sidor/profil",
}

19
constants/myPages.ts Normal file
View File

@@ -0,0 +1,19 @@
import type { Lang } from "@/types/lang"
export const pageNames: Record<Lang, string> = {
da: "mine-sider",
de: "mein-profil",
en: "my-pages",
fi: "minun-sivujani",
no: "mine-sider",
sv: "mina-sidor",
}
export const profilePageNames: Record<Lang, string> = {
da: "mine-sider/profil",
de: "mein-profil/profil",
en: "my-pages/profile",
fi: "minun-sivujani/profil",
no: "mine-sider/profil",
sv: "mina-sidor/profil",
}

View File

@@ -0,0 +1,11 @@
"use client"
import { useEffect } from 'react';
export function useHandleKeyPress(callback: (event: KeyboardEvent) => void) {
useEffect(() => {
window.addEventListener('keydown', callback);
return () => {
window.removeEventListener('keydown', callback);
};
}, [callback]);
}

View File

@@ -0,0 +1,3 @@
<svg width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.1 31.75C9.2 30.2833 11.2833 29.1583 13.35 28.375C15.4167 27.5917 17.6333 27.2 20 27.2C22.3667 27.2 24.5917 27.5917 26.675 28.375C28.7583 29.1583 30.85 30.2833 32.95 31.75C34.4167 29.95 35.4583 28.1333 36.075 26.3C36.6917 24.4667 37 22.5333 37 20.5C37 15.6667 35.375 11.625 32.125 8.375C28.875 5.125 24.8333 3.5 20 3.5C15.1667 3.5 11.125 5.125 7.875 8.375C4.625 11.625 3 15.6667 3 20.5C3 22.5333 3.31667 24.4667 3.95 26.3C4.58333 28.1333 5.63333 29.95 7.1 31.75ZM19.9907 22C18.0636 22 16.4417 21.3386 15.125 20.0157C13.8083 18.6928 13.15 17.0678 13.15 15.1407C13.15 13.2136 13.8114 11.5917 15.1343 10.275C16.4572 8.95833 18.0822 8.3 20.0093 8.3C21.9364 8.3 23.5583 8.96143 24.875 10.2843C26.1917 11.6072 26.85 13.2322 26.85 15.1593C26.85 17.0864 26.1886 18.7083 24.8657 20.025C23.5428 21.3417 21.9178 22 19.9907 22ZM20.0234 40.5C17.2745 40.5 14.6833 39.975 12.25 38.925C9.81667 37.875 7.69167 36.4417 5.875 34.625C4.05833 32.8083 2.625 30.6872 1.575 28.2617C0.525 25.8362 0 23.2445 0 20.4867C0 17.7289 0.525 15.1417 1.575 12.725C2.625 10.3083 4.05833 8.19167 5.875 6.375C7.69167 4.55833 9.81277 3.125 12.2383 2.075C14.6638 1.025 17.2555 0.5 20.0133 0.5C22.7711 0.5 25.3583 1.025 27.775 2.075C30.1917 3.125 32.3083 4.55833 34.125 6.375C35.9417 8.19167 37.375 10.3089 38.425 12.7266C39.475 15.1443 40 17.7277 40 20.4766C40 23.2255 39.475 25.8167 38.425 28.25C37.375 30.6833 35.9417 32.8083 34.125 34.625C32.3083 36.4417 30.1911 37.875 27.7734 38.925C25.3557 39.975 22.7723 40.5 20.0234 40.5ZM20 37.5C21.8333 37.5 23.625 37.2333 25.375 36.7C27.125 36.1667 28.85 35.2333 30.55 33.9C28.85 32.7 27.1167 31.7833 25.35 31.15C23.5833 30.5167 21.8 30.2 20 30.2C18.2 30.2 16.4167 30.5167 14.65 31.15C12.8833 31.7833 11.15 32.7 9.45 33.9C11.15 35.2333 12.875 36.1667 14.625 36.7C16.375 37.2333 18.1667 37.5 20 37.5ZM20 19C21.1333 19 22.0583 18.6417 22.775 17.925C23.4917 17.2083 23.85 16.2833 23.85 15.15C23.85 14.0167 23.4917 13.0917 22.775 12.375C22.0583 11.6583 21.1333 11.3 20 11.3C18.8667 11.3 17.9417 11.6583 17.225 12.375C16.5083 13.0917 16.15 14.0167 16.15 15.15C16.15 16.2833 16.5083 17.2083 17.225 17.925C17.9417 18.6417 18.8667 19 20 19Z" fill="#1C1B1F"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,8 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_58_5363" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
<rect width="20" height="20" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_58_5363)">
<path d="M10.0094 17.5831C8.9638 17.5831 7.98052 17.3858 7.05961 16.991C6.1387 16.5963 5.33232 16.0532 4.64047 15.3617C3.94861 14.6701 3.40523 13.864 3.01032 12.9432C2.61542 12.0224 2.41797 11.0386 2.41797 9.99167C2.41797 8.94475 2.61533 7.96388 3.01005 7.04905C3.40477 6.1342 3.9479 5.33085 4.63943 4.63901C5.33097 3.94714 6.13713 3.40376 7.05791 3.00886C7.97868 2.61396 8.96252 2.4165 10.0094 2.4165C11.0563 2.4165 12.0372 2.6139 12.9521 3.00869C13.8669 3.40348 14.6702 3.94671 15.3621 4.63838C16.054 5.33005 16.5973 6.13317 16.9922 7.04776C17.3871 7.96232 17.5846 8.94634 17.5846 9.99982V11.0575C17.5846 11.7605 17.3373 12.3572 16.8428 12.8475C16.3482 13.3379 15.7483 13.5831 15.0429 13.5831C14.5654 13.5831 14.134 13.449 13.7489 13.1809C13.3637 12.9127 13.0552 12.5682 12.8233 12.1472C12.4793 12.5767 12.0619 12.9234 11.571 13.1873C11.0801 13.4512 10.5568 13.5831 10.0013 13.5831C9.00681 13.5831 8.16097 13.2341 7.46376 12.5359C6.76657 11.8378 6.41797 10.9909 6.41797 9.99511C6.41797 8.99933 6.76703 8.15395 7.46516 7.45896C8.16327 6.76399 9.01021 6.4165 10.006 6.4165C11.0018 6.4165 11.8471 6.7651 12.5421 7.4623C13.2371 8.1595 13.5846 9.00534 13.5846 9.99982V11.0575C13.5846 11.455 13.7288 11.7948 14.0173 12.0768C14.3057 12.3588 14.6476 12.4998 15.0429 12.4998C15.4382 12.4998 15.7801 12.3588 16.0686 12.0768C16.3571 11.7948 16.5013 11.455 16.5013 11.0575V9.99982C16.5013 8.19426 15.8693 6.65954 14.6054 5.39565C13.3416 4.13176 11.8068 3.49982 10.0013 3.49982C8.19573 3.49982 6.661 4.13176 5.39711 5.39565C4.13323 6.65954 3.50128 8.19426 3.50128 9.99982C3.50128 11.8054 4.13323 13.3401 5.39711 14.604C6.661 15.8679 8.19573 16.4998 10.0013 16.4998H14.0013V17.5831H10.0094ZM10.0013 12.4998C10.6957 12.4998 11.286 12.2568 11.7721 11.7707C12.2582 11.2845 12.5013 10.6943 12.5013 9.99982C12.5013 9.30537 12.2582 8.7151 11.7721 8.22898C11.286 7.74287 10.6957 7.49982 10.0013 7.49982C9.30684 7.49982 8.71656 7.74287 8.23045 8.22898C7.74434 8.7151 7.50128 9.30537 7.50128 9.99982C7.50128 10.6943 7.74434 11.2845 8.23045 11.7707C8.71656 12.2568 9.30684 12.4998 10.0013 12.4998Z" fill="#1C1B1F"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

8
public/home.svg Normal file
View File

@@ -0,0 +1,8 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_58_5402" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
<rect width="20" height="20" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_58_5402)">
<path d="M5.50128 15.5H7.78976V10.5481H12.2128V15.5H14.5013V8.25003L10.0013 4.85901L5.50128 8.25003V15.5ZM4.41797 16.5833V7.70838L10.0013 3.50488L15.5846 7.70838V16.5833H11.1295V11.6314H8.87307V16.5833H4.41797Z" fill="#1C1B1F"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 534 B

8
public/phone.svg Normal file
View File

@@ -0,0 +1,8 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_58_5381" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="21" height="20">
<rect x="0.449219" width="20" height="20" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_58_5381)">
<path d="M6.20694 18.5831C5.83183 18.5831 5.51476 18.4536 5.25573 18.1946C4.9967 17.9356 4.86719 17.6185 4.86719 17.2434V2.75625C4.86719 2.38114 4.9967 2.06407 5.25573 1.80505C5.51476 1.54602 5.83183 1.4165 6.20694 1.4165H14.6941C15.0692 1.4165 15.3862 1.54602 15.6453 1.80505C15.9043 2.06407 16.0338 2.38114 16.0338 2.75625V17.2434C16.0338 17.6185 15.9043 17.9356 15.6453 18.1946C15.3862 18.4536 15.0692 18.5831 14.6941 18.5831H6.20694ZM5.9505 16.2915V17.2434C5.9505 17.3075 5.97721 17.3663 6.03063 17.4197C6.08406 17.4731 6.14283 17.4998 6.20694 17.4998H14.6941C14.7582 17.4998 14.8169 17.4731 14.8704 17.4197C14.9238 17.3663 14.9505 17.3075 14.9505 17.2434V16.2915H5.9505ZM5.9505 15.2082H14.9505V4.79146H5.9505V15.2082ZM5.9505 3.70817H14.9505V2.75625C14.9505 2.69214 14.9238 2.63337 14.8704 2.57994C14.8169 2.52653 14.7582 2.49982 14.6941 2.49982H6.20694C6.14283 2.49982 6.08406 2.52653 6.03063 2.57994C5.97721 2.63337 5.9505 2.69214 5.9505 2.75625V3.70817Z" fill="#1C1B1F"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

16
routes/protected.ts Normal file
View File

@@ -0,0 +1,16 @@
import { pageNames, profilePageNames } from "@/constants/myPages"
import type { Lang } from "@/types/lang"
/* Authenticated routes */
export const protectedRoutes: string[] = [
...Object.keys(pageNames).map(
(locale) => `/${locale}/${pageNames[locale as Lang]}`
),
...Object.keys(profilePageNames).map(
(locale) => `/${locale}/${profilePageNames[locale as Lang]}`
),
...Object.keys(profilePageNames).map(
(locale) => `/${locale}/${profilePageNames[locale as Lang]}/verify`
),
]

View File

@@ -0,0 +1,3 @@
export interface MaxWidthProps extends React.HTMLAttributes<HTMLElement> {
tag?: "article" | "div" | "main" | "section"
}

View File

@@ -0,0 +1,10 @@
import type { LangParams } from "@/types/params"
type Breadcrumb = {
href?: string
title: string
}
export type BreadcrumbsProps = LangParams & {
breadcrumbs: Record<string, Breadcrumb[]>
}

View File

@@ -0,0 +1,3 @@
export interface CardProps extends React.HTMLAttributes<HTMLElement> {
tag?: "article" | "div" | "section"
}

View File

@@ -0,0 +1,11 @@
import { headingVariants } from "@/components/MyProfile/Card/Title/variants"
import type { VariantProps } from "class-variance-authority"
type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
export interface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement>, VariantProps<typeof headingVariants> {
as?: HeadingLevel
level?: HeadingLevel
uppercase?: boolean
}

View File

@@ -0,0 +1,5 @@
import type { User } from "@/types/user"
export interface ProfileProps extends React.HTMLAttributes<HTMLElement> {
user: User
}

View File

@@ -1,6 +1,10 @@
type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5"
import { headingVariants } from "@/components/MyPages/Title/variants"
export interface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement> {
import type { VariantProps } from "class-variance-authority"
type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
export interface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement>, VariantProps<typeof headingVariants> {
as?: HeadingLevel
level?: HeadingLevel
uppercase?: boolean