diff --git a/config/nam.3bear.design.json b/config/nam.3bear.design.json new file mode 100644 index 0000000..6e4f481 --- /dev/null +++ b/config/nam.3bear.design.json @@ -0,0 +1,308 @@ +[ + { + "AnnouncementBanner": { + "props": { + "label": [ + { + "text": "Free shipping on orders over $100", + "className": "font-medium" + }, + { + "text": ".", + "className": "" + }, + { + "text": "Free gift with every purchase", + "className": "" + }, + { + "text": ".", + "className": "" + }, + { + "text": "Free returns", + "className": "" + } + ], + "className": "bg-[#009b93] text-white" + } + } + }, + { + "Nav": { + "props": {}, + "children": [ + { + "Div": { + "props": { + "className": "flex items-center h-full ml-16" + }, + "children": [ + { + "Image": { + "props": { + "src": "https://3bears.de/cdn/shop/files/3Bears_Logo-Schutzzone_negativ_RGB.png?v=1676382997&width=335", + "alt": "Medusa Store", + "width": 160, + "height": 40 + } + } + }, + { + "Div": { + "props": { + "className": "flex items-center h-full gap-10" + }, + "children": [ + { + "DropdownMenus": { + "props": { + "label": "Shop", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-categories-link", + "isShowArrow": true + }, + "children": [ + { + "DropdownMenuItems": { + "props": { + "label": "All Categories", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-all-categories-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "New Arrivals", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-new-arrivals-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "Best Sellers", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-best-sellers-link" + } + } + } + ] + } + }, + { + "DropdownMenus": { + "props": { + "label": "About us", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-categories-link", + "isShowArrow": true + }, + "children": [ + { + "DropdownMenuItems": { + "props": { + "label": "All Categories", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-all-categories-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "New Arrivals", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-new-arrivals-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "Best Sellers", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-best-sellers-link" + } + } + } + ] + } + }, + { + "DropdownMenus": { + "props": { + "label": "About our product", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-categories-link", + "isShowArrow": true + }, + "children": [ + { + "DropdownMenuItems": { + "props": { + "label": "All Categories", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-all-categories-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "New Arrivals", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-new-arrivals-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "Best Sellers", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-best-sellers-link" + } + } + } + ] + } + }, + { + "LocalizedClientLink": { + "props": { + "href": "/recipe", + "label": "Recipes", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-register-link" + } + } + }, + { + "LocalizedClientLink": { + "props": { + "href": "/meet-harry-kane", + "label": "Meet Harry Kane", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-register-link" + } + } + } + ] + } + } + ] + } + }, + { + "Div": { + "props": { + "className": "flex items-center gap-x-6 h-full justify-end" + }, + "children": [ + { + "DropdownMenus": { + "props": { + "label": "Germany (EUR €)", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-categories-link", + "isShowArrow": true + }, + "children": [ + { + "DropdownMenuItems": { + "props": { + "label": "All Categories", + "className": "hover:text-ui-fg-base", + "data-testid": "nav-all-categories-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "New Arrivals", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-new-arrivals-link" + } + } + }, + { + "DropdownMenuItems": { + "props": { + "label": "Best Sellers", + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-best-sellers-link" + } + } + } + ] + } + }, + { + "SearchButton": { + "props": { + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-account-link" + } + } + }, + { + "UserButton": { + "props": { + "className": "hover:text-ui-fg-base flex items-center gap-2", + "data-testid": "nav-account-link" + } + } + }, + { + "Suspense": { + "props": { + "fallback": [ + { + "LocalizedClientLink": { + "props": { + "href": "/cart", + "label": "Cart (0)", + "className": "hover:text-ui-fg-base flex gap-2", + "data-testid": "nav-cart-link" + } + } + } + ] + }, + "children": [ + { + "CartButton": {} + } + ] + } + } + ] + } + } + ] + } + }, + { + "CartMismatchBanner": { + "show": true + } + }, + { + "FreeShippingPriceNudge": { + "variant": "popup" + } + }, + { + "PropsChildren": {} + }, + { + "Footer": { + "copyrightText": "© 2025 MyShop" + } + } +] diff --git a/src/app/[countryCode]/(main)/layout.tsx b/src/app/[countryCode]/(main)/layout.tsx index 6036614..db8bed6 100644 --- a/src/app/[countryCode]/(main)/layout.tsx +++ b/src/app/[countryCode]/(main)/layout.tsx @@ -4,15 +4,17 @@ import { listCartOptions, retrieveCart } from "@lib/data/cart" import { retrieveCustomer } from "@lib/data/customer" import { getBaseURL } from "@lib/util/env" import { StoreCartShippingOption } from "@medusajs/types" -import { DynamicLayoutRenderer } from "../../../vibentec/renderer" -import { LayoutContext, LayoutComponentNode, } from "../../../vibentec/component-map" +import { LayoutContext, LayoutComponentNode } from "vibentec/component-map" +import { DynamicLayoutRenderer } from "vibentec/renderer" import { loadDesignConfig } from "vibentec/configloader" +import { DESIGN_JSON_FILE } from "./config-json-file" export const metadata: Metadata = { metadataBase: new URL(getBaseURL()), } export default async function PageLayout(props: { children: React.ReactNode }) { + // Choose which design JSON to load. Swap this constant as needed. const customer = await retrieveCustomer() const cart = await retrieveCart() let shippingOptions: StoreCartShippingOption[] = [] @@ -23,14 +25,16 @@ export default async function PageLayout(props: { children: React.ReactNode }) { shippingOptions = shipping_options } - const nodes: LayoutComponentNode[] = await loadDesignConfig() + const nodes: LayoutComponentNode[] = await loadDesignConfig( + DESIGN_JSON_FILE[1].file + ) const context: LayoutContext = { customer, cart, shippingOptions, contentChildren: props.children, + designId: DESIGN_JSON_FILE[1].id, } - return } diff --git a/src/modules/layout/components/cart-dropdown/index.tsx b/src/modules/layout/components/cart-dropdown/index.tsx index 304b2fe..d7161a8 100644 --- a/src/modules/layout/components/cart-dropdown/index.tsx +++ b/src/modules/layout/components/cart-dropdown/index.tsx @@ -6,6 +6,7 @@ import { PopoverPanel, Transition, } from "@headlessui/react" +import { ShoppingBag } from "@medusajs/icons" import { convertToLocale } from "@lib/util/money" import { HttpTypes } from "@medusajs/types" import { Button } from "@medusajs/ui" @@ -82,10 +83,12 @@ const CartDropdown = ({ {`Cart (${totalItems})`} + > + + void + className?: string +} + +export default function SearchButton({ + label = "Search", + shortcut = "⌘K", + onClick, + className, +}: SearchButtonProps) { + return ( + + ) +} \ No newline at end of file diff --git a/src/modules/layout/templates/3bear-template/announcement-bar/index.tsx b/src/modules/layout/templates/3bear-template/announcement-bar/index.tsx new file mode 100644 index 0000000..a167f94 --- /dev/null +++ b/src/modules/layout/templates/3bear-template/announcement-bar/index.tsx @@ -0,0 +1,25 @@ + +type TextItem = { text: string; className?: string } + +export default async function AnnouncementBanner({ + className, + label, + ...props +}: { className: string; label: TextItem[] }) { + return ( +
+
+
+ + {label.map((item, index) => { + return ( +
+ {item.text} +
+ ) + })} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/modules/layout/templates/dropdown-menu/dropdown-menu.tsx b/src/modules/layout/templates/dropdown-menu/dropdown-menu.tsx new file mode 100644 index 0000000..78b9990 --- /dev/null +++ b/src/modules/layout/templates/dropdown-menu/dropdown-menu.tsx @@ -0,0 +1,37 @@ +'use client' +import { DropdownMenu } from "@medusajs/ui" +import { ChevronDownMini } from "@medusajs/icons" +export default function DropdownMenuComponent({ + children, + props, +}: { + children?: React.ReactNode + props?: { + className?: string + "data-testid"?: string + label?: string + isShowArrow?: boolean + } +}) { + const itemNodes = Array.isArray(children) ? children : [] + + return ( + + + + + + + {itemNodes.map((item, index) => { + const props = item?.DropdownMenuItems?.props + return props ? {props.label} : null + })} + + + ) +} \ No newline at end of file diff --git a/src/modules/layout/templates/vt-nav/index.tsx b/src/modules/layout/templates/vt-nav/index.tsx index dac8cfd..a60277d 100644 --- a/src/modules/layout/templates/vt-nav/index.tsx +++ b/src/modules/layout/templates/vt-nav/index.tsx @@ -2,19 +2,21 @@ import { listRegions } from "@lib/data/regions" import { StoreRegion } from "@medusajs/types" import SideMenu from "@modules/layout/components/side-menu" import { DynamicLayoutRenderer, DynamicLayoutRendererProps } from "vibentec/renderer" +import { clx } from "@medusajs/ui" -export default async function VtNav({ nodes, context }: DynamicLayoutRendererProps) { +export default async function VtNav({ nodes, context, className }: DynamicLayoutRendererProps & { className?: string }) { + console.log({nodes, context}) const regions = await listRegions().then((regions: StoreRegion[]) => regions) return (
-
-