Compare commits

..

5 Commits

17 changed files with 896 additions and 690 deletions

View File

@ -136,31 +136,13 @@
],
"right": [
{
"Dropdown": {
"VtCountryCodeSelect": {
"config": {
"trigger": {
"icon": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Germany.svg/1200px-Flag_of_Germany.svg.png",
"text": "Germany (EUR)",
"className": "font-bold text-[1rem] text-[#003F31] 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": "Germany (EUR)",
"href": "/"
},
{
"icon": "https://upload.wikimedia.org/wikipedia/commons/2/20/Flag_of_the_Netherlands.svg",
"text": "Netherlands (EUR)",
"href": "/"
},
{
"icon": "https://cdn.shopify.com/s/files/1/0275/7784/3817/files/Australia_Flag.svg?v=1657674627",
"text": "Australia (AUD)",
"href": "/"
}
]
"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": true,
"isDisplayFullname": true
}
}
}
},
@ -173,18 +155,18 @@
}
},
{
"Button": {
"AccountButton": {
"config": {
"icon": "User",
"className": "shadow-none"
"className": " flex items-center gap-1 shadow-none"
}
}
},
{
"VtCartButton": {
"config": {
"variant": "shoppingBagbutton",
"className": "shadow-none"
"icon": "ShoppingBag",
"className": "shadow-none bg-transparent text-black w-[50px]"
}
}
}
@ -215,388 +197,158 @@
{
"Footer": {
"config": {
"className": "content-container flex w-full bg-[#003f31]",
"children": [
"className": "content-container flex w-full bg-[#003f31] text-white border justify-between pb-8 pt-14",
"leftClassName": "flex-col ml-3",
"centerClassName": "",
"rightClassName": "flex gap-[12rem] mr-[100px]",
"left": [
{
"Section": {
"Image": {
"config": {
"className": "grid grid-cols-2 w-full py-[40px]"
"src": "/3bear-white-logo.avif",
"alt": "MyShop",
"className": "h-full w-[150px] ml-10 pb-6",
"objectFit": "contain"
}
}
},
{
"Text": {
"config": {
"label": "Melde dich für unsere Oatnews an 💛",
"className": "flex flex-col gap-y-2 ml-10 w-full text-[24px] font-semibold text-white"
}
}
},
{
"Input": {
"config": {
"placeholder": "E-mail",
"className": "w-[350px] mt-6 border border-gray-200 bg-transparent rounded-md h-[48px] px-[16px] text-[16px] leading-[125%] text-white flex font-bold ml-10"
}
}
},
{
"VtSocialLinks": {
"config": {
"className": "flex items-center gap-1 ml-6 mt-8"
},
"children": [
{
"Section": {
"Button": {
"config": {
"className": "flex mr-8 gap-1 w-full flex-col gap-6"
},
"children": [
{
"Image": {
"config": {
"src": "/3bear-white-logo.avif",
"alt": "B Corp Logo",
"className": "w-[150px] h-[108px]"
}
}
},
{
"Text": {
"config": {
"label": "Melde dich für unsere Oatnews an 💛",
"className": "text-[26px] leading-[125%] text-white flex font-bold"
}
}
},
{
"Section": {
"config": {
"className": "flex w-full flex gap-6"
},
"children": [
{
"Input": {
"config": {
"placeholder": "E-mail",
"className": "w-[50%] border border-gray-200 bg-transparent rounded-md h-[48px] px-[16px] text-[16px] leading-[125%] text-white flex font-bold"
}
}
}
]
}
},
{
"Section": {
"config": {
"className": "flex w-full flex"
},
"children": [
{
"Button": {
"config": {
"icon": "Twitter",
"className": "text-white flex justify-start gap-1 shadow-none w-[50px] bg-transparent hover:bg-transparent"
}
}
},
{
"Button": {
"config": {
"icon": "Twitter",
"className": "text-white flex justify-start gap-1 shadow-none w-[50px] bg-transparent hover:bg-transparent"
}
}
},
{
"Button": {
"config": {
"icon": "Twitter",
"className": "text-white flex justify-start gap-1 shadow-none w-[50px] bg-transparent hover:bg-transparent"
}
}
},
{
"Button": {
"config": {
"icon": "Twitter",
"className": "text-white flex justify-start gap-1 shadow-none w-[50px] bg-transparent hover:bg-transparent"
}
}
},
{
"Button": {
"config": {
"icon": "Twitter",
"className": "text-white flex justify-start gap-1 shadow-none w-[50px] bg-transparent hover:bg-transparent"
}
}
}
]
}
}
]
"icon": "Twitter",
"iconClassName": "text-white",
"className": "w-[48px] h-[48px] shadow-none hover:bg-transparent bg-transparent text-white rounded-md flex items-center justify-center"
}
}
},
{
"Section": {
"Button": {
"config": {
"className": "flex mr-8 gap-1 w-full gap-6"
},
"children": [
{
"Section": {
"config": {
"className": "flex mr-8 gap-1 w-full flex-col gap-6"
},
"children": [
{
"Link": {
"config": {
"label": "Weiteres",
"href": "/",
"className": "text-[16px] leading-[125%] text-white flex hover:text-white font-bold"
}
}
},
{
"Link": {
"config": {
"label": "FAQ",
"href": "/",
"className": "text-[14px] leading-[160%] text-white flex mr-8 gap-1 hover:bg-transparent hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Track My Order",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Store policies",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Contact us",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
}
]
}
},
{
"Section": {
"config": {
"className": "flex mr-8 gap-1 w-full flex-col gap-6"
},
"children": [
{
"Link": {
"config": {
"label": "Kundendienst",
"href": "/",
"className": "text-[16px] leading-[125%] text-white flex hover:text-white font-bold"
}
}
},
{
"Link": {
"config": {
"label": "FAQ",
"href": "/",
"className": "text-[14px] leading-[160%] text-white flex mr-8 gap-1 hover:bg-transparent hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Track My Order",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Store policies",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Contact us",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
}
]
}
},
{
"Section": {
"config": {
"className": "flex mr-8 gap-1 w-full flex-col gap-6"
},
"children": [
{
"Link": {
"config": {
"label": "Info",
"href": "/",
"className": "text-[16px] leading-[125%] text-white flex hover:text-white font-bold"
}
}
},
{
"Link": {
"config": {
"label": "FAQ",
"href": "/",
"className": "text-[14px] leading-[160%] text-white flex mr-8 gap-1 hover:bg-transparent hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Track My Order",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Store policies",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Contact us",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[14px] leading-[125%] text-white flex hover:text-white font-extralight"
}
}
}
]
}
}
]
"icon": "Twitter",
"iconClassName": "text-white",
"className": "w-[48px] h-[48px] shadow-none hover:bg-transparent bg-transparent text-white rounded-md flex items-center justify-center"
}
}
},
{
"Button": {
"config": {
"icon": "Twitter",
"iconClassName": "text-white",
"className": "w-[48px] h-[48px] shadow-none hover:bg-transparent bg-transparent text-white rounded-md flex items-center justify-center"
}
}
},
{
"Button": {
"config": {
"icon": "Twitter",
"iconClassName": "text-white",
"className": "w-[48px] h-[48px] shadow-none hover:bg-transparent bg-transparent text-white rounded-md flex items-center justify-center"
}
}
}
]
}
}
],
"center": [],
"right": [
{
"VtMenuItem": {
"config": {
"title": "Information",
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-white hover:text-white",
"itemClassName": "text-[1rem] font-[400] opacity-70 hover:text-white",
"items": [
{
"text": "Über Uns",
"href": "/"
},
{
"text": "Placeholder",
"href": "/categories/shoes"
},
{
"text": "Placeholder",
"href": "/categories/accessories"
}
]
}
}
},
{
"VtMenuItem": {
"config": {
"title": "Kundendienst",
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-white hover:text-white",
"itemClassName": "text-[1rem] font-[400] flex items-center opacity-70 hover:text-white",
"items": [
{
"text": "Twitter",
"href": "/"
},
{
"text": "Facebook",
"href": "/categories/shoes"
},
{
"text": "Pinterest",
"href": "/categories/accessories"
}
]
}
}
},
{
"VtMenuItem": {
"config": {
"title": "Weiteres",
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-white",
"itemClassName": "text-[1rem] font-[400] w-[150px] opacity-70 hover:text-white",
"items": [
{
"text": "Karriere",
"href": "/"
},
{
"text": "Unser Team",
"href": "/categories/shoes"
},
{
"text": "B2B",
"href": "/categories/accessories"
},
{
"text": "Presse",
"href": "/categories/accessories"
}
]
}
}
}
]
}
}

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

@ -45,7 +45,7 @@
"config": {
"label": "Mehr Info",
"href": "/",
"className": "text-[13px] flex items-center bg-[#112638] gap-1 "
"className": "text-[13px] w-fit text-white px-3 flex items-center bg-[#112638] gap-1 "
}
}
}
@ -70,20 +70,12 @@
}
},
{
"Dropdown": {
"VtCountryCodeSelect": {
"config": {
"trigger": {
"text": "DE",
"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": "DE",
"href": "/"
}
]
"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
}
}
}
}
@ -154,10 +146,10 @@
}
},
{
"Button": {
"AccountButton": {
"config": {
"icon": "User",
"className": " flex items-center gap-1 shadow-none w-[50px]"
"className": " flex items-center gap-1 shadow-none"
}
}
},
@ -172,7 +164,7 @@
{
"VtCartButton": {
"config": {
"variant": "shoppingBagbutton",
"icon": "ShoppingCart",
"className": "shadow-none bg-transparent text-black w-[50px]"
}
}
@ -204,241 +196,189 @@
{
"Footer": {
"config": {
"children": [
"className": "content-container flex w-full border justify-between pb-8",
"leftClassName": "flex-col ml-6",
"centerClassName": "flex mt-[130px] gap-24",
"rightClassName": "flex mt-[160px]",
"left": [
{
"Image": {
"config": {
"src": "/VibentecIT-logo.svg",
"alt": "MyShop",
"className": "h-[128px] w-[324px] ml-[3rem]",
"className": "h-[100px] w-[320px]",
"objectFit": "contain"
}
}
},
{
"Section": {
"VtMenuItem": {
"config": {
"className": "flex items-start px-[3rem] justify-between"
},
"children": [
{
"Section": {
"config": {
"className": "flex flex-col"
},
"children": [
{
"Link": {
"config": {
"label": "Der Wegbereiter für innovative IT-Lösungen",
"href": "/",
"className": "text-[24px] leading-[125%] text-[#11314E] flex mr-8 gap-1 hover:underline font-bold w-[336px] pl-[2rem] mb-[22px]"
}
}
},
{
"Link": {
"config": {
"label": "Tauchen Sie ein in eine Welt modernster Technologien, zuverlässiger Support und proaktiver Innovation gemeinsam gestalten wir die digitale Zukunft Ihres Unternehmens.",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[336px] pl-[2rem] hover:no-underline"
}
}
},
{
"Button": {
"config": {
"label": "Kontaktieren Sie Uns",
"labelClassName": "order-[1]",
"iconClassName": "order-[2]",
"icon": "ChevronRight",
"className": "mt-[24px] flex items-center w-fit hover:bg-black gap-1 shadow-none p-[1rem] bg-[#18181B] text-white ml-[2rem]"
}
}
}
]
"title": "Der Wegbereiter für innovative IT-Lösungen",
"className": "flex flex-col gap-y-2 ml-10 w-[320px] text-[24px] font-semibold text-[#11314E]",
"itemClassName": "text-ui-fg-subtle txt-small",
"items": [
{
"text": "Tauchen Sie ein in eine Welt modernster Technologien, zuverlässiger Support und proaktiver Innovation gemeinsam gestalten wir die digitale Zukunft Ihres Unternehmens.",
"href": "/"
}
},
{
"Section": {
"config": {
"className": "flex items-start mt-[2rem]"
},
"children": [
{
"Section": {
"config": {
"className": "flex flex-col"
},
"children": [
{
"Link": {
"config": {
"label": "Unternehmen",
"href": "/",
"className": "text-[24px] leading-[125%] text-[#11314E] flex mr-8 gap-1 hover:underline font-bold w-[200px] pl-[2rem] mb-[16px]"
}
}
},
{
"Link": {
"config": {
"label": "Über Uns",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[200px] pl-[2rem] hover:no-underline"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[200px] pl-[2rem] hover:no-underline"
}
}
},
{
"Link": {
"config": {
"label": "Placeholder",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[200px] pl-[2rem] hover:no-underline"
}
}
}
]
}
},
{
"Section": {
"config": {
"className": "flex flex-col gap-[6px]"
},
"children": [
{
"Link": {
"config": {
"label": "Social Media",
"href": "/",
"className": "text-[24px] leading-[125%] text-[#11314E] flex mr-8 mb-[2px] gap-1 hover:underline font-bold w-[200px] pl-[2rem]"
}
}
},
{
"Button": {
"config": {
"label": "Über Uns",
"icon": "Twitter",
"className": "text-[13px] leading-[160%] text-[#11314E] flex justify-start font-semibold mr-8 gap-1 shadow-none w-[200px] pl-[2rem] hover:bg-transparent"
}
}
},
{
"Button": {
"config": {
"label": "Über Uns",
"icon": "Twitter",
"className": "text-[13px] leading-[160%] text-[#11314E] flex justify-start font-semibold mr-8 gap-1 shadow-none w-[200px] pl-[2rem] hover:bg-transparent"
}
}
},
{
"Button": {
"config": {
"label": "Über Uns",
"icon": "Twitter",
"className": "text-[13px] leading-[160%] text-[#11314E] flex justify-start font-semibold mr-8 gap-1 shadow-none w-[200px] pl-[2rem] hover:bg-transparent"
}
}
}
]
}
},
{
"Section": {
"config": {
"className": "flex flex-col"
},
"children": [
{
"Link": {
"config": {
"label": "Addresse",
"href": "/",
"className": "text-[24px] leading-[125%] text-[#11314E] flex mr-8 gap-1 hover:underline font-bold w-[200px] pl-[2rem] mb-[16px]"
}
}
},
{
"Link": {
"config": {
"label": "Über Uns",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[200px] pl-[2rem] hover:no-underline"
}
}
},
{
"Link": {
"config": {
"label": "Social Media",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[200px] pl-[2rem] hover:no-underline"
}
}
},
{
"Link": {
"config": {
"label": "Über Uns",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-[200px] pl-[2rem] hover:no-underline"
}
}
}
]
}
}
]
}
},
{
"Section": {
"config": {
"className": "flex flex-col mt-[5rem]"
},
"children": [
{
"Link": {
"config": {
"label": "Tauchen Sie ein in eine Welt modernster Technologien, zuverlässiger Support und proaktiver Innovation gemeinsam gestalten wir die digitale Zukunft Ihres Unternehmens.",
"href": "/",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-full pl-[2rem] hover:no-underline"
}
}
}
]
}
}
]
]
}
}
},
{
"Section": {
"Button": {
"config": {
"className": "relative flex px-[3rem] justify-between mt-[29px] pt-[8px] after:content-[''] after:absolute after:left-[4.8rem] after:right-[3rem] after:top-0 after:h-[1px] after:bg-[#285A86] mb-10"
},
"children": [
{
"Text": {
"config": {
"label": "©2025 Vibentec IT. All rights reserved",
"className": "text-[13px] leading-[160%] text-[#11314E] flex items-center mr-8 gap-1 w-full pl-[2rem]"
}
"label": "Kontaktieren Sie uns",
"className": "bg-black text-white items-center flex gap-2 ml-10 px-4 mt-[24px] py-5 rounded-md w-fit text-[14px]",
"icon": "ChevronRightMini",
"iconClassName": "order-1"
}
}
}
],
"center": [
{
"VtMenuItem": {
"config": {
"title": "Unternehmen",
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-[#11314E]",
"itemClassName": "text-[1rem] font-[400]",
"items": [
{
"text": "Über Uns",
"href": "/"
},
{
"text": "Placeholder",
"href": "/categories/shoes"
},
{
"text": "Placeholder",
"href": "/categories/accessories"
}
}
]
]
}
}
},
{
"VtMenuItem": {
"config": {
"title": "Social Media",
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-[#11314E]",
"itemClassName": "text-[1rem] font-[400] flex items-center",
"items": [
{
"text": "Twitter",
"href": "/",
"icon": "X"
},
{
"text": "Facebook",
"href": "/categories/shoes",
"icon": "X"
},
{
"text": "Pinterest",
"href": "/categories/accessories",
"icon": "X"
}
]
}
}
},
{
"VtMenuItem": {
"config": {
"title": "Addresse",
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-[#11314E]",
"itemClassName": "text-[1rem] font-[400] w-[150px]",
"items": [
{
"text": "Hopfenstr. 10c76185 Karlsruhe Deutschland",
"href": "/"
},
{
"text": "+497271 5970098",
"href": "/categories/shoes"
},
{
"text": "info@vibentec-it.io",
"href": "/categories/accessories"
}
]
}
}
}
],
"right": [
{
"VtMenuItem": {
"config": {
"className": "flex flex-col gap-y-2 text-[24px] font-semibold text-[#11314E]",
"itemClassName": "text-[1rem] font-[400] w-[150px]",
"items": [
{
"text": "Datenschutz",
"href": "/"
},
{
"text": "Impressum",
"href": "/categories/shoes"
},
{
"text": "Installation Info",
"href": "/categories/accessories"
}
]
}
}
}
]
}
}
},
{
"Footer": {
"config": {
"className": "content-container h-[128px] w-full text text-[#11314E] flex items-center justify-between px-20 mt-2",
"left": [
{
"Text": {
"config": {
"label": "©2025 Vibentec IT. All rights reserved",
"className": "text-[14px] font-[400] pt-2"
}
}
}
],
"center": [],
"right": [
{
"Button": {
"config": {
"href": "/",
"icon": "Mastercard",
"className": "shadow-none"
}
}
},
{
"Button": {
"config": {
"href": "/",
"icon": "PayPal",
"className": "shadow-none"
}
}
},
{
"Button": {
"config": {
"href": "/",
"icon": "Visa",
"className": "shadow-none"
}
}
}
]

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>
)
}

View File

@ -20,22 +20,13 @@ export default function VtButton({
(CustomIcons as Record<string, any>)[iconName]
: undefined
return (
<>
{props?.icon && (
<IconButton className={props?.className ?? ""}>
{IconComponent && (
<IconComponent className={props?.iconClassName ?? ""} />
)}
{props?.label && (
<span className={props?.labelClassName ?? ""}>{props.label}</span>
)}
</IconButton>
<IconButton className={props?.className ?? ""}>
{IconComponent && (
<IconComponent className={props?.iconClassName ?? ""} />
)}
{!props?.icon && (
<Button className={props?.className ?? ""}>
{props?.label && <span>{props.label}</span>}
</Button>
{props?.label && (
<span className={props?.labelClassName ?? ""}>{props.label}</span>
)}
</>
</IconButton>
)
}

View File

@ -0,0 +1,112 @@
"use client"
import { Select } from "@medusajs/ui"
import ChevronDown from "@modules/common/icons/chevron-down"
import {
LayoutComponentDefinition,
LayoutContext,
} from "@vibentec/component-map"
import { useMemo, useState, useEffect } from "react"
import { useParams, usePathname, useRouter } from "next/navigation"
import ReactCountryFlag from "react-country-flag"
import { HttpTypes } from "@medusajs/types"
export default function VtCountrySelectClient({
nodes,
context,
regions,
}: {
nodes: LayoutComponentDefinition
context: LayoutContext
regions?: HttpTypes.StoreRegion[]
}) {
const props = nodes.config ?? {}
const triggerText = props?.trigger?.text
const [items, setItems] = useState<{ text: string; label?: string }[]>([])
const { countryCode } = useParams()
console.log(regions)
useEffect(() => {
if (!regions || regions.length === 0) {
setItems([])
return
}
const opts = regions
.map((r) =>
(r.countries || []).map((c) => ({
text: c.iso_2 ?? "",
label: c.display_name,
}))
)
.flat()
.filter((o) => o.text)
.sort((a, b) => (a.label ?? "").localeCompare(b.label ?? ""))
console.log(opts)
setItems(opts)
}, [regions])
const initialValue = (countryCode as string) || triggerText || ""
const [value, setValue] = useState(initialValue)
const pathname = usePathname()
const router = useRouter()
const handleChange = (next: string) => {
setValue(next)
if (!pathname || !next) return
const parts = pathname.split("/")
if (parts.length > 1) {
parts[1] = next.toLowerCase()
const newPath = parts.join("/")
router.replace(newPath)
}
}
const selectedItem = useMemo(() => {
return items.find((i) => i.text === value)
}, [items, value])
if (!triggerText && items.length === 0) {
return null
}
return (
<Select value={value} onValueChange={handleChange}>
<Select.Trigger
className={
(props.trigger?.className ?? "") +
"flex items-center gap-1 [&_svg:not(:first-of-type)]:hidden"
}
>
<span className="txt-compact-small flex items-center">
{/* @ts-ignore */}
{props.trigger?.isFlag && (
<ReactCountryFlag
svg
style={{
width: "16px",
height: "16px",
}}
countryCode={value ?? ""}
/>
)}
</span>
{props.trigger?.isDisplayFullname ? (selectedItem?.label || value) : (selectedItem?.text.toUpperCase() || value)} <ChevronDown />
</Select.Trigger>
<Select.Content>
{items.length > 0 &&
items.map((item: { text: string; label?: string }, index: number) => (
<Select.Item value={item.text || ""} key={item.text + index}>
<div className="flex items-center w-full gap-3">
{props.trigger?.isFlag && item.text && (
<ReactCountryFlag
svg
style={{
width: "16px",
height: "16px",
}}
countryCode={item.text ?? ""}
/>
)}
{item.text.toUpperCase()}
</div>
</Select.Item>
))}
</Select.Content>
</Select>
)
}

View File

@ -0,0 +1,22 @@
import { listRegions, getRegion } from "@lib/data/regions"
import { HttpTypes } from "@medusajs/types"
import {
LayoutComponentDefinition,
LayoutContext,
} from "@vibentec/component-map"
import VtCountryCodeSelectClient from "./index"
export default async function VtCountryCodeSelect({
nodes,
context,
}: {
nodes: LayoutComponentDefinition
context: LayoutContext
}) {
const regions = (await listRegions()) as HttpTypes.StoreRegion[]
return (
<VtCountryCodeSelectClient nodes={nodes} context={context} regions={regions} />
)
}

View File

@ -0,0 +1,72 @@
"use client"
import { Select } from "@medusajs/ui"
import LocalizedClientLink from "@modules/common/components/localized-client-link"
import ChevronDown from "@modules/common/icons/chevron-down"
import {
LayoutComponentDefinition,
LayoutContext,
} from "@vibentec/component-map"
export default function VtCurrencySelect({
nodes,
context,
}: {
nodes: LayoutComponentDefinition
context: LayoutContext
}) {
const props = nodes.config ?? {}
if (!props.trigger.text || props.items.length === 0) {
return null
}
return (
<Select>
<Select.Trigger
className={props.trigger.className + " flex items-center gap-1"}
>
{props.trigger.icon && (
<img
src={props.trigger.icon}
alt={props.trigger.text}
className="w-5 h-5 rounded-[50%] mr-3"
/>
)}
{props.trigger.text} {props.trigger.isShowArrow && <ChevronDown />}
</Select.Trigger>
<Select.Content>
{props.items.length > 0 &&
props.items.map(
(
item: {
text: string
className?: string
href?: string
icon?: string
},
index: number
) => (
<Select.Item
value={item.text || ""}
key={item.text + index}
className={item.className || ""}
>
{item.icon && (
<img
src={item.icon}
alt={item.text}
className="w-5 h-5 rounded-[50%] mr-3"
/>
)}
{item.href ? (
<LocalizedClientLink href={item.href}>
{item.text}
</LocalizedClientLink>
) : (
item.text
)}
</Select.Item>
)
)}
</Select.Content>
</Select>
)
}

View File

@ -24,7 +24,21 @@ export default async function VtFooter({
return (
<footer className={props?.className ?? ""}>
{props.children && <DynamicLayoutRenderer nodes={props.children} context={context} />}
<div className={clx("flex gap-x-4 h-full", props?.leftClassName)}>
{props.left && (
<DynamicLayoutRenderer nodes={props.left} context={context} />
)}
</div>
<div className={clx("flex gap-x-4 h-full", props?.centerClassName)}>
{props.center && (
<DynamicLayoutRenderer nodes={props.center} context={context} />
)}
</div>
<div className={clx("flex gap-x-4 h-full", props?.rightClassName)}>
{props.right && (
<DynamicLayoutRenderer nodes={props.right} context={context} />
)}
</div>
</footer>
)
}

View File

@ -21,7 +21,7 @@ export default function VtImage({
const props = (nodes.config as VtImageConfig) ?? {}
return (
<div className={clx("relative", props.className)}>
<Image src={props.src} alt={props.alt} fill objectFit={props.objectFit ?? "contain"} />
<img src={props.src} alt={props.alt} className={clx("w-full h-full object-cover", props.objectFit)} />
</div>
)
}

View File

@ -0,0 +1,56 @@
"use client"
import {
LayoutComponentDefinition,
LayoutContext,
} from "@vibentec/component-map"
import LocalizedClientLink from "@modules/common/components/localized-client-link"
import * as MedusaIcons from "@medusajs/icons"
import * as CustomIcons from "@modules/common/icons"
export default function VtMenuItem({
nodes,
context,
}: {
nodes: LayoutComponentDefinition
context: LayoutContext
}) {
const props = nodes.config ?? {}
const title = props.title ?? ""
const items = props.items ?? []
const icon = props.icon ?? ""
const getIconComponent = (icon: string | undefined) => {
return icon
? (MedusaIcons as Record<string, any>)[icon] ??
(CustomIcons as Record<string, any>)[icon]
: undefined
}
return (
<div className={props.className ?? "flex flex-col gap-y-2"}>
<span>{title}</span>
<ul className="grid grid-cols-1 gap-2" data-testid="footer-categories">
{items.map((item: { text: string; href?: string; icon?: string }, index: number) => {
const Icon = getIconComponent(item.icon)
return (
<li
key={`${item.text}-${index}`}
className={props.itemClassName ?? "text-ui-fg-subtle txt-small"}
>
{Icon && <Icon className={props.iconClassName ?? "inline-block mr-2"} />}
{item.href ? (
<LocalizedClientLink
className="hover:text-ui-fg-base"
href={item.href}
data-testid="category-link"
>
{item.text}
</LocalizedClientLink>
) : (
<span className="hover:text-ui-fg-base">{item.text}</span>
)}
</li>
)
})}
</ul>
</div>
)
}

View File

@ -0,0 +1,17 @@
import { LayoutComponentDefinition, LayoutContext } from "@vibentec/component-map"
import { DynamicLayoutRenderer } from "@vibentec/renderer"
export default function VtSocialLinks({
nodes,
context,
}: {
nodes: LayoutComponentDefinition
context: LayoutContext
}) {
const props = nodes.config ?? {}
return (
<div className={props.className ?? ""}>
{nodes.children && <DynamicLayoutRenderer nodes={nodes.children} context={context} />}
</div>
)
}

View File

@ -19,6 +19,10 @@ import VtSearchInput from "@modules/layout/templates/vt-search-input"
import VtSection from "@modules/layout/templates/vt-section"
import VtText from "@modules/layout/templates/vt-text"
import VtInput from "@modules/layout/templates/vt-input"
import VtCurrencySelect from "@modules/layout/templates/vt-currency-select"
import VtMenuItem from "@modules/layout/templates/vt-menu-item"
import VtCountryCodeSelect from "@modules/layout/templates/vt-country-select/server"
import VtSocialLinks from "@modules/layout/templates/vt-social-link"
type ComponentConfig = Record<string, any>;
@ -64,11 +68,15 @@ export const componentMap: Record<string, ComponentRenderer> = {
Section: nodesContextRenderer(VtSection),
SearchInput: nodesContextRenderer(VtSearchInput),
VtCartButton: nodesContextRenderer(VtCartButton),
VtCurrencySelect: nodesContextRenderer(VtCurrencySelect),
VtCountryCodeSelect: nodesContextRenderer(VtCountryCodeSelect),
VtSocialLinks: nodesContextRenderer(VtSocialLinks),
Link: nodesContextRenderer(VtLink),
Input: nodesContextRenderer(VtInput),
Image: nodesContextRenderer(VtImage),
Text: nodesContextRenderer(VtText),
Dropdown: nodesContextRenderer(VtDropdown),
VtMenuItem: nodesContextRenderer(VtMenuItem),
CartMismatchBanner: configOnly(CartMismatchBanner),
FreeShippingPriceNudge: configOnly(FreeShippingPriceNudge),
PropsChildren: {

View File

@ -3,5 +3,6 @@ export const jsonFileNames = {
stePlayGround: "ste.playground.design.json",
nam3Bear: "nam.3bear.design.json",
namDrsquatch: "nam.drsquatch.design.json",
namVibentec: "nam.vibentec.design.json"
namVibentec: "nam.vibentec.design.json",
namStarter: "nam.mds-starter-design.json",
};