From f869d12c7af75aaf8fe4d4159c57cdfb11d38aeb Mon Sep 17 00:00:00 2001 From: Nam Doan Date: Mon, 8 Dec 2025 11:39:41 +0700 Subject: [PATCH] feat: reuse country select and refactor some component --- .../layout/templates/vt-button/index.tsx | 21 +--- .../templates/vt-country-select/index.tsx | 114 ++++++++++++++++++ .../templates/vt-country-select/server.tsx | 22 ++++ .../templates/vt-currency-select/index.tsx | 72 +++++++++++ .../layout/templates/vt-image/index.tsx | 2 +- .../layout/templates/vt-menu-item/index.tsx | 56 +++++++++ 6 files changed, 271 insertions(+), 16 deletions(-) create mode 100644 src/modules/layout/templates/vt-country-select/index.tsx create mode 100644 src/modules/layout/templates/vt-country-select/server.tsx create mode 100644 src/modules/layout/templates/vt-currency-select/index.tsx create mode 100644 src/modules/layout/templates/vt-menu-item/index.tsx diff --git a/src/modules/layout/templates/vt-button/index.tsx b/src/modules/layout/templates/vt-button/index.tsx index e3602d8..b32dc53 100644 --- a/src/modules/layout/templates/vt-button/index.tsx +++ b/src/modules/layout/templates/vt-button/index.tsx @@ -20,22 +20,13 @@ export default function VtButton({ (CustomIcons as Record)[iconName] : undefined return ( - <> - {props?.icon && ( - - {IconComponent && ( - - )} - {props?.label && ( - {props.label} - )} - + + {IconComponent && ( + )} - {!props?.icon && ( - + {props?.label && ( + {props.label} )} - + ) } diff --git a/src/modules/layout/templates/vt-country-select/index.tsx b/src/modules/layout/templates/vt-country-select/index.tsx new file mode 100644 index 0000000..bb200d2 --- /dev/null +++ b/src/modules/layout/templates/vt-country-select/index.tsx @@ -0,0 +1,114 @@ +"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 ( + + ) +} diff --git a/src/modules/layout/templates/vt-country-select/server.tsx b/src/modules/layout/templates/vt-country-select/server.tsx new file mode 100644 index 0000000..5c9e9a7 --- /dev/null +++ b/src/modules/layout/templates/vt-country-select/server.tsx @@ -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 ( + + ) +} + diff --git a/src/modules/layout/templates/vt-currency-select/index.tsx b/src/modules/layout/templates/vt-currency-select/index.tsx new file mode 100644 index 0000000..1632fa2 --- /dev/null +++ b/src/modules/layout/templates/vt-currency-select/index.tsx @@ -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 ( + + ) +} diff --git a/src/modules/layout/templates/vt-image/index.tsx b/src/modules/layout/templates/vt-image/index.tsx index 7d84d72..ba09f5f 100644 --- a/src/modules/layout/templates/vt-image/index.tsx +++ b/src/modules/layout/templates/vt-image/index.tsx @@ -21,7 +21,7 @@ export default function VtImage({ const props = (nodes.config as VtImageConfig) ?? {} return (
- {props.alt} + {props.alt}
) } diff --git a/src/modules/layout/templates/vt-menu-item/index.tsx b/src/modules/layout/templates/vt-menu-item/index.tsx new file mode 100644 index 0000000..09fb922 --- /dev/null +++ b/src/modules/layout/templates/vt-menu-item/index.tsx @@ -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)[icon] ?? + (CustomIcons as Record)[icon] + : undefined + } + return ( +
+ {title} +
    + {items.map((item: { text: string; href?: string; icon?: string }, index: number) => { + const Icon = getIconComponent(item.icon) + return ( +
  • + {Icon && } + {item.href ? ( + + {item.text} + + ) : ( + {item.text} + )} +
  • + ) + })} +
+
+ ) +}