namds/refactor-base-layout #8

Merged
yen.nguyen merged 27 commits from namds/refactor-base-layout into main 2025-12-15 07:44:38 +00:00
5 changed files with 265 additions and 44 deletions
Showing only changes of commit d8e78b71e4 - Show all commits

View File

@ -0,0 +1,221 @@
[
{
"Header": {
"config": {
"sticky": true
},
"children": [
{
"Banner": {
"config": {
"variant": "nav",
"className": "h-12 bg-[#E6EFFC] text-[#11314E] gap-12 pl-16",
"left": [
{
"Link": {
"config": {
"label": "Über Uns",
"href": "/",
"className": "text-[13px] flex items-center gap-1 cursor-pointer"
}
}
},
{
"Link": {
"config": {
"label": "Kontaktieren Uns",
"href": "/",
"className": "text-[13px] flex items-center gap-1"
}
}
}
],
"center": [
{
"Link": {
"config": {
"label": "Einsparung durch Digitalisierung in der Arztpraxis",
"href": "/",
"className": "text-[13px] flex items-center gap-1 "
}
}
},
{
"Button": {
"config": {
"label": "Mehr Info",
"href": "/",
"className": "text-[13px] flex items-center bg-[#112638] gap-1 "
}
}
}
],
"right": [
{
"Dropdown": {
"config": {
"trigger": {
"text": "EURO",
"className": "font-bold text-[13px] text-[#11314E] flex items-center gap-1 hover:text-[#009b93]",
"isShowArrow": true
},
"items": [
{
"icon": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Germany.svg/1200px-Flag_of_Germany.svg.png",
"text": "EURO",
"href": "/"
}
]
}
}
},
{
"VtCountryCodeSelect": {
"config": {
"trigger": {
"className": "w-auto font-bold text-[13px] text-[#11314E] flex justify-start items-center gap-1 hover:text-[#009b93] bg-transparent shadow-none hover:bg-transparent",
"isFlag": false
}
}
}
}
]
}
}
},
{
"Nav": {
"config": {
"left": [
{
"VtSideMenu": {}
},
{
"VtMegaMenu": {
"config": {
"navLabel": {
"text": "Sale",
"className": "text-[13px] text-[#11314E] flex items-center mr-8 gap-1 hover:bg-transparent hover:underline hover:text-[#009b93]"
}
}
}
}
],
"center": [
{
"HomeButton": {
"config": {
"label": "Medusa Store"
}
}
}
],
"right": [
{
"AccountButton": {
"config": {
"label": "Account",
"className": "hover:text-ui-fg-base"
}
}
},
{
"VtCartButton": {
"config": {
"variant": "link",
"className": "hover:text-ui-fg-base"
}
}
}
]
}
}
}
]
}
},
{
"CartMismatchBanner": {
"config": {
"show": true
}
}
},
{
"FreeShippingPriceNudge": {
"config": {
"variant": "popup"
}
}
},
{
"PropsChildren": {}
},
{
"Footer": {
"config": {
"className": "content-container flex w-full border h-[300px] justify-between",
"left": [
{
"VtMenuItem": {
"config": {
"title": "category",
"className": "flex flex-col gap-y-2",
"itemClassName": "text-ui-fg-subtle txt-small ml-3",
"items": [
{
"text": "Clothing",
"href": "/"
},
{
"text": "Shoes",
"href": "/categories/shoes"
},
{
"text": "Accessories",
"href": "/categories/accessories"
}
]
}
}
}
],
"center": [
{
"VtMenuItem": {
"config": {
"title": "category",
"className": "flex flex-col gap-y-2",
"itemClassName": "text-ui-fg-subtle txt-small ml-3",
"items": [
{
"text": "Clothing",
"href": "/"
},
{
"text": "Shoes",
"href": "/categories/shoes"
},
{
"text": "Accessories",
"href": "/categories/accessories"
}
]
}
}
}
],
"right": [
{
"Text": {
"config": {
"label": "Medusa Check",
"className": "text-[13px] text-[#A6A6A6]"
}
}
}
]
}
}
}
]

View File

@ -1,8 +1,8 @@
import { retrieveCart } from "@lib/data/cart"
import CartDropdown from "../cart-dropdown"
export default async function CartButton() {
export default async function CartButton({ iconName }: { iconName?: string }) {
const cart = await retrieveCart().catch(() => null)
return <CartDropdown cart={cart} />
return <CartDropdown cart={cart} iconName={iconName} />
}

View File

@ -9,6 +9,7 @@ import {
import { convertToLocale } from "@lib/util/money"
import { HttpTypes } from "@medusajs/types"
import { Button } from "@medusajs/ui"
import * as MedusaIcons from "@medusajs/icons"
import DeleteButton from "@modules/common/components/delete-button"
import LineItemOptions from "@modules/common/components/line-item-options"
import LineItemPrice from "@modules/common/components/line-item-price"
@ -19,8 +20,10 @@ import { Fragment, useEffect, useRef, useState } from "react"
const CartDropdown = ({
cart: cartState,
iconName,
}: {
cart?: HttpTypes.StoreCart | null
iconName?: string
}) => {
const [activeTimer, setActiveTimer] = useState<NodeJS.Timer | undefined>(
undefined
@ -73,6 +76,10 @@ const CartDropdown = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [totalItems, itemRef.current])
const Icon = iconName
? (MedusaIcons as Record<string, React.ComponentType<any>>)[iconName]
: undefined
return (
<div
className="h-full z-50"
@ -81,11 +88,15 @@ const CartDropdown = ({
>
<Popover className="relative h-full">
<PopoverButton className="h-full">
<LocalizedClientLink
className="hover:text-ui-fg-base"
href="/cart"
data-testid="nav-cart-link"
>{`Cart (${totalItems})`}</LocalizedClientLink>
{Icon ? (
<Icon />
) : (
<LocalizedClientLink
className="hover:text-ui-fg-base"
href="/cart"
data-testid="nav-cart-link"
>{`Cart (${totalItems})`}</LocalizedClientLink>
)}
</PopoverButton>
<Transition
show={cartDropdownOpen}

View File

@ -1,27 +1,39 @@
import LocalizedClientLink from "@modules/common/components/localized-client-link"
import { LayoutComponentDefinition, LayoutContext } from "vibentec/component-map";
import {
LayoutComponentDefinition,
LayoutContext,
} from "vibentec/component-map"
import { clx } from "@medusajs/ui"
export const AccountButton = ({ nodes, context }: { nodes: LayoutComponentDefinition; context: LayoutContext }) => {
import * as MedusaIcons from "@medusajs/icons"
export const AccountButton = ({
nodes,
context,
}: {
nodes: LayoutComponentDefinition
context: LayoutContext
}) => {
const props = nodes.config ?? {}
const className = clx("hover:text-ui-fg-base", props.className);
const style: React.CSSProperties = {};
if (props.bgColor) style.backgroundColor = props.bgColor;
if (props.textColor) style.color = props.textColor;
const className = clx("hover:text-ui-fg-base", props.className)
const style: React.CSSProperties = {}
if (props.bgColor) style.backgroundColor = props.bgColor
if (props.textColor) style.color = props.textColor
const href = props.href ?? "/account"
const label = props.label ?? "Account"
const iconName = props.icon
const Icon = iconName
? (MedusaIcons as Record<string, React.ComponentType<any>>)[iconName]
: undefined
return (
<div className="flex items-center h-full" style={style}>
<div className="flex items-center h-full">
<LocalizedClientLink
href={href}
className={className}
data-testid="nav-account-link"
>
{label}
{Icon ? <Icon /> : label}
</LocalizedClientLink>
</div>
</div>
)
}
export default AccountButton
export default AccountButton

View File

@ -3,23 +3,9 @@ import {
LayoutComponentDefinition,
LayoutContext,
} from "vibentec/component-map"
import { clx, IconButton } from "@medusajs/ui"
import { clx } from "@medusajs/ui"
import { Suspense } from "react"
import CartButton from "@modules/layout/components/cart-button"
import { ShoppingBag, ShoppingCart } from "@medusajs/icons"
const IconButtonComponent = ({ className, variant }: { className?: string; variant?: "shoppingBag" | "cart" }) => {
const variants = {
shoppingBag: ShoppingBag,
cart: ShoppingCart,
}
const Icon = variants[variant ?? "shoppingBag"]
return (
<IconButton className={className}>
<Icon />
</IconButton>
)
}
export const VtCartButton = ({
nodes,
@ -30,15 +16,6 @@ export const VtCartButton = ({
}) => {
const props = nodes.config ?? {}
const className = clx("hover:text-ui-fg-base flex gap-2", props.className)
const variants = {
link: <CartButton />,
shoppingBagbutton: <IconButtonComponent className={className} variant="shoppingBag" />,
cartIconButton: <IconButtonComponent className={className} variant="cart" />,
}
if (!props.variant) return null
const fallBackComp = variants[props.variant as keyof typeof variants]
return (
<Suspense
fallback={
@ -51,7 +28,7 @@ export const VtCartButton = ({
</LocalizedClientLink>
}
>
{fallBackComp}
<CartButton iconName={props.icon} />
</Suspense>
)
}