Merged in feat/my-pages-breadcrumbs (pull request #105)

Feat/my pages breadcrumbs
This commit is contained in:
Simon.Emanuelsson
2024-04-16 08:12:02 +00:00
committed by Michael Zetterberg
31 changed files with 228 additions and 87 deletions

View File

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

View File

@@ -0,0 +1,10 @@
import { serverClient } from "@/lib/trpc/server"
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
export default async function BenefitsBreadcrumbs() {
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
href: "/my-pages/benefits",
})
return <Breadcrumbs breadcrumbs={breadcrumbs} />
}

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
import { serverClient } from "@/lib/trpc/server"
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
export default async function OverviewBreadcrumbs() {
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
href: "/my-pages/overview",
})
return <Breadcrumbs breadcrumbs={breadcrumbs} />
}

View File

@@ -0,0 +1,10 @@
import { serverClient } from "@/lib/trpc/server"
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
export default async function MyPagesBreadcrumbs() {
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
href: "/my-pages",
})
return <Breadcrumbs breadcrumbs={breadcrumbs} />
}

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
import { serverClient } from "@/lib/trpc/server"
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
export default async function ProfileBreadcrumbs() {
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
href: "/my-pages/profile",
})
return <Breadcrumbs breadcrumbs={breadcrumbs} />
}

View File

@@ -0,0 +1,10 @@
import { serverClient } from "@/lib/trpc/server"
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
export default async function ProfileBreadcrumbs() {
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
href: "/my-pages/profile",
})
return <Breadcrumbs breadcrumbs={breadcrumbs} />
}

View File

@@ -1,24 +1,23 @@
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"
import styles from "./layout.module.css"
import type { LangParams, LayoutArgs } from "@/types/params"
import type { MyPagesLayoutProps } from "@/types/components/myPages/layout"
export default async function MyPagesLayout({
breadcrumbs,
children,
params,
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
}: React.PropsWithChildren<MyPagesLayoutProps>) {
return (
<div
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
>
<Header lang={params.lang} />
<Breadcrumbs breadcrumbs={breadcrumbs} lang={params.lang} />
{breadcrumbs}
<div className={styles.content}>
<Sidebar lang={params.lang} />
{children}

View File

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

View File

@@ -0,0 +1,9 @@
import styles from "./breadcrumbs.module.css"
export default function Breadcrumb({ children }: React.PropsWithChildren) {
return (
<li className={styles.listItem}>
<p className={styles.currentPage}>{children}</p>
</li>
)
}

View File

@@ -0,0 +1,17 @@
import Link from "@/components/TempDesignSystem/Link"
import styles from "./breadcrumbs.module.css"
export default function BreadcrumbsWithLink({
children,
href,
}: React.PropsWithChildren<{ href: string }>) {
return (
<li className={styles.listItem}>
<Link className={styles.link} href={href}>
{children}
</Link>
<span aria-hidden="true">/</span>
</li>
)
}

View File

@@ -1,49 +0,0 @@
"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

@@ -27,6 +27,11 @@
line-height: 1.56rem;
}
.listItem {
display: flex;
gap: 0.4rem;
}
.currentPage {
margin: 0;
}
@@ -37,4 +42,4 @@
padding-left: 2.4rem;
padding-top: 2rem;
}
}
}

View File

@@ -1,20 +1,31 @@
import ClientBreadcrumbs from "./Client"
import Link from "@/components/TempDesignSystem/Link"
import { _ } from "@/lib/translation"
import Breadcrumb from "./Breadcrumb"
import BreadcrumbsWithLink from "./BreadcrumbWithLink"
import styles from "./breadcrumbs.module.css"
import type { BreadcrumbsProps } from "@/types/components/myPages/breadcrumbs"
export default function Breadcrumbs({ breadcrumbs, lang }: BreadcrumbsProps) {
export default function Breadcrumbs({ breadcrumbs }: 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} />
<BreadcrumbsWithLink href="#">{_("Home")}</BreadcrumbsWithLink>
{breadcrumbs.map((breadcrumb) => {
if (breadcrumb.href) {
return (
<BreadcrumbsWithLink
key={breadcrumb.title}
href={breadcrumb.href}
>
{breadcrumb.title}
</BreadcrumbsWithLink>
)
}
return <Breadcrumb>{breadcrumb.title}</Breadcrumb>
})}
</ul>
</nav>
)

View File

@@ -24,7 +24,7 @@ export function mapMenuItems(navigationItems: NavigationItem[]) {
lang: node.system.locale,
subItems: item.sub_items ? mapMenuItems(item.sub_items) : null,
uid: node.system.uid,
url: `/${node.system.locale}/${getURL(node)}`.replaceAll("//+", "/"),
url: `/${node.system.locale}/${getURL(node)}`.replaceAll(/\/\/+/g, "/"),
}
})
}

View File

@@ -30,21 +30,22 @@ export default async function Sidebar({ lang }: SidebarProps) {
</Title>
{menuItems.map((item) => (
<Fragment key={item.uid}>
<Link href={item.url} variant="sidebar">
<Link href={item.url} partialMatch variant="sidebar">
{item.linkText}
</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}
partialMatch
variant="sidebar"
>
{subItem.linkText}
</Link>
)
})
: null}
</Fragment>
))}

View File

@@ -10,12 +10,16 @@ import type { LinkProps } from "./link"
export default function Link({
className,
href,
partialMatch = false,
size,
variant,
...props
}: LinkProps) {
const currentPageSlug = usePathname()
const isActive = currentPageSlug === href
let isActive = currentPageSlug === href
if (partialMatch && !isActive) {
isActive = currentPageSlug.startsWith(href)
}
const classNames = linkVariants({
active: isActive,
className,

View File

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

5
env/server.ts vendored
View File

@@ -2,6 +2,11 @@ import { createEnv } from "@t3-oss/env-nextjs"
import { z } from "zod"
export const env = createEnv({
/**
* Due to t3-env only checking typeof window === "undefined"
* and Netlify running Deno, window is never "undefined"
* https://github.com/t3-oss/t3-env/issues/154
*/
isServer: typeof window === "undefined" || "Deno" in window,
server: {
ADOBE_SCRIPT_SRC: z.string().optional(),

View File

@@ -1,9 +1,11 @@
import { router } from "./trpc"
/** Routers */
import { contentstackRouter } from "./routers/contentstack"
import { userRouter } from "./routers/user"
export const appRouter = router({
contentstack: contentstackRouter,
user: userRouter,
})

View File

@@ -0,0 +1,5 @@
import { mergeRouters } from "@/server/trpc"
import { breadcrumbsQueryRouter } from "./query"
export const breadcrumbsRouter = mergeRouters(breadcrumbsQueryRouter)

View File

@@ -0,0 +1,8 @@
import { z } from "zod"
export const getBreadcrumbsSchema = z.array(
z.object({
href: z.string().optional(),
title: z.string(),
})
)

View File

@@ -0,0 +1,43 @@
import { z } from "zod"
import { badRequestError } from "@/server/errors/trpc"
import { getBreadcrumbsSchema } from "./output"
import { publicProcedure, router } from "@/server/trpc"
const rootMyPagesBreadcrumb = {
href: "/en/my-pages",
title: "My Pages",
}
enum paths {
"/my-pages",
"/my-pages/benefits",
"/my-pages/overview",
"/my-pages/profile",
}
const keys = Object.keys(paths) as [keyof typeof paths]
const possibleBreadcrumbs: Record<string, { title: string; href?: string }[]> =
{
"/my-pages": [
{
title: rootMyPagesBreadcrumb.title,
},
],
"/my-pages/benefits": [rootMyPagesBreadcrumb, { title: "Benefits" }],
"/my-pages/overview": [rootMyPagesBreadcrumb, { title: "Overview" }],
"/my-pages/profile": [rootMyPagesBreadcrumb, { title: "Profile" }],
}
export const breadcrumbsQueryRouter = router({
get: publicProcedure.input(z.object({ href: z.enum(keys) })).query((opts) => {
const breadcrumbs = possibleBreadcrumbs[opts.input.href]
const validatedBreadcrumbs = getBreadcrumbsSchema.safeParse(breadcrumbs)
if (validatedBreadcrumbs.success) {
return breadcrumbs
}
throw badRequestError()
}),
})

View File

@@ -0,0 +1,7 @@
import { router } from "@/server/trpc"
import { breadcrumbsRouter } from "./breadcrumbs"
export const contentstackRouter = router({
breadcrumbs: breadcrumbsRouter,
})

View File

@@ -12,7 +12,7 @@ interface EditProfileActions {
export interface EditProfileStore
extends EditProfileActions,
EditProfileState {}
EditProfileState { }
export const useProfileStore = create<EditProfileStore>()((set) => ({
pending: false,

5
types/breadcrumbs.ts Normal file
View File

@@ -0,0 +1,5 @@
import { z } from "zod"
import { getBreadcrumbsSchema } from "@/server/routers/contentstack/breadcrumbs/output"
export interface Breadcrumbs extends z.infer<typeof getBreadcrumbsSchema> {}

View File

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

View File

@@ -0,0 +1,5 @@
import type { LangParams, LayoutArgs } from "@/types/params"
export interface MyPagesLayoutProps extends LayoutArgs<LangParams> {
breadcrumbs: React.ReactNode
}