init starter package
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { retrieveCart } from "@lib/data/cart"
|
||||
import { retrieveCustomer } from "@lib/data/customer"
|
||||
import PaymentWrapper from "@modules/checkout/components/payment-wrapper"
|
||||
import CheckoutForm from "@modules/checkout/templates/checkout-form"
|
||||
import CheckoutSummary from "@modules/checkout/templates/checkout-summary"
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Checkout",
|
||||
}
|
||||
|
||||
export default async function Checkout() {
|
||||
const cart = await retrieveCart()
|
||||
|
||||
if (!cart) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const customer = await retrieveCustomer()
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_416px] content-container gap-x-40 py-12">
|
||||
<PaymentWrapper cart={cart}>
|
||||
<CheckoutForm cart={cart} customer={customer} />
|
||||
</PaymentWrapper>
|
||||
<CheckoutSummary cart={cart} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||
import ChevronDown from "@modules/common/icons/chevron-down"
|
||||
import MedusaCTA from "@modules/layout/components/medusa-cta"
|
||||
|
||||
export default function CheckoutLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className="w-full bg-white relative small:min-h-screen">
|
||||
<div className="h-16 bg-white border-b ">
|
||||
<nav className="flex h-full items-center content-container justify-between">
|
||||
<LocalizedClientLink
|
||||
href="/cart"
|
||||
className="text-small-semi text-ui-fg-base flex items-center gap-x-2 uppercase flex-1 basis-0"
|
||||
data-testid="back-to-cart-link"
|
||||
>
|
||||
<ChevronDown className="rotate-90" size={16} />
|
||||
<span className="mt-px hidden small:block txt-compact-plus text-ui-fg-subtle hover:text-ui-fg-base ">
|
||||
Back to shopping cart
|
||||
</span>
|
||||
<span className="mt-px block small:hidden txt-compact-plus text-ui-fg-subtle hover:text-ui-fg-base">
|
||||
Back
|
||||
</span>
|
||||
</LocalizedClientLink>
|
||||
<LocalizedClientLink
|
||||
href="/"
|
||||
className="txt-compact-xlarge-plus text-ui-fg-subtle hover:text-ui-fg-base uppercase"
|
||||
data-testid="store-link"
|
||||
>
|
||||
Medusa Store
|
||||
</LocalizedClientLink>
|
||||
<div className="flex-1 basis-0" />
|
||||
</nav>
|
||||
</div>
|
||||
<div className="relative" data-testid="checkout-container">{children}</div>
|
||||
<div className="py-4 w-full flex items-center justify-center">
|
||||
<MedusaCTA />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import InteractiveLink from "@modules/common/components/interactive-link"
|
||||
import { Metadata } from "next"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "404",
|
||||
description: "Something went wrong",
|
||||
}
|
||||
|
||||
export default async function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 items-center justify-center min-h-[calc(100vh-64px)]">
|
||||
<h1 className="text-2xl-semi text-ui-fg-base">Page not found</h1>
|
||||
<p className="text-small-regular text-ui-fg-base">
|
||||
The page you tried to access does not exist.
|
||||
</p>
|
||||
<InteractiveLink href="/">Go to frontpage</InteractiveLink>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import AddressBook from "@modules/account/components/address-book"
|
||||
|
||||
import { getRegion } from "@lib/data/regions"
|
||||
import { retrieveCustomer } from "@lib/data/customer"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Addresses",
|
||||
description: "View your addresses",
|
||||
}
|
||||
|
||||
export default async function Addresses(props: {
|
||||
params: Promise<{ countryCode: string }>
|
||||
}) {
|
||||
const params = await props.params
|
||||
const { countryCode } = params
|
||||
const customer = await retrieveCustomer()
|
||||
const region = await getRegion(countryCode)
|
||||
|
||||
if (!customer || !region) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full" data-testid="addresses-page-wrapper">
|
||||
<div className="mb-8 flex flex-col gap-y-4">
|
||||
<h1 className="text-2xl-semi">Shipping Addresses</h1>
|
||||
<p className="text-base-regular">
|
||||
View and update your shipping addresses, you can add as many as you
|
||||
like. Saving your addresses will make them available during checkout.
|
||||
</p>
|
||||
</div>
|
||||
<AddressBook customer={customer} region={region} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import Spinner from "@modules/common/icons/spinner"
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="flex items-center justify-center w-full h-full text-ui-fg-base">
|
||||
<Spinner size={36} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { retrieveOrder } from "@lib/data/orders"
|
||||
import OrderDetailsTemplate from "@modules/order/templates/order-details-template"
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ id: string }>
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: Props): Promise<Metadata> {
|
||||
const params = await props.params
|
||||
const order = await retrieveOrder(params.id).catch(() => null)
|
||||
|
||||
if (!order) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return {
|
||||
title: `Order #${order.display_id}`,
|
||||
description: `View your order`,
|
||||
}
|
||||
}
|
||||
|
||||
export default async function OrderDetailPage(props: Props) {
|
||||
const params = await props.params
|
||||
const order = await retrieveOrder(params.id).catch(() => null)
|
||||
|
||||
if (!order) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return <OrderDetailsTemplate order={order} />
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import OrderOverview from "@modules/account/components/order-overview"
|
||||
import { notFound } from "next/navigation"
|
||||
import { listOrders } from "@lib/data/orders"
|
||||
import Divider from "@modules/common/components/divider"
|
||||
import TransferRequestForm from "@modules/account/components/transfer-request-form"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Orders",
|
||||
description: "Overview of your previous orders.",
|
||||
}
|
||||
|
||||
export default async function Orders() {
|
||||
const orders = await listOrders()
|
||||
|
||||
if (!orders) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full" data-testid="orders-page-wrapper">
|
||||
<div className="mb-8 flex flex-col gap-y-4">
|
||||
<h1 className="text-2xl-semi">Orders</h1>
|
||||
<p className="text-base-regular">
|
||||
View your previous orders and their status. You can also create
|
||||
returns or exchanges for your orders if needed.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<OrderOverview orders={orders} />
|
||||
<Divider className="my-16" />
|
||||
<TransferRequestForm />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import Overview from "@modules/account/components/overview"
|
||||
import { notFound } from "next/navigation"
|
||||
import { retrieveCustomer } from "@lib/data/customer"
|
||||
import { listOrders } from "@lib/data/orders"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Account",
|
||||
description: "Overview of your account activity.",
|
||||
}
|
||||
|
||||
export default async function OverviewTemplate() {
|
||||
const customer = await retrieveCustomer().catch(() => null)
|
||||
const orders = (await listOrders().catch(() => null)) || null
|
||||
|
||||
if (!customer) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return <Overview customer={customer} orders={orders} />
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import ProfilePhone from "@modules/account//components/profile-phone"
|
||||
import ProfileBillingAddress from "@modules/account/components/profile-billing-address"
|
||||
import ProfileEmail from "@modules/account/components/profile-email"
|
||||
import ProfileName from "@modules/account/components/profile-name"
|
||||
import ProfilePassword from "@modules/account/components/profile-password"
|
||||
|
||||
import { notFound } from "next/navigation"
|
||||
import { listRegions } from "@lib/data/regions"
|
||||
import { retrieveCustomer } from "@lib/data/customer"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Profile",
|
||||
description: "View and edit your Medusa Store profile.",
|
||||
}
|
||||
|
||||
export default async function Profile() {
|
||||
const customer = await retrieveCustomer()
|
||||
const regions = await listRegions()
|
||||
|
||||
if (!customer || !regions) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full" data-testid="profile-page-wrapper">
|
||||
<div className="mb-8 flex flex-col gap-y-4">
|
||||
<h1 className="text-2xl-semi">Profile</h1>
|
||||
<p className="text-base-regular">
|
||||
View and update your profile information, including your name, email,
|
||||
and phone number. You can also update your billing address, or change
|
||||
your password.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-8 w-full">
|
||||
<ProfileName customer={customer} />
|
||||
<Divider />
|
||||
<ProfileEmail customer={customer} />
|
||||
<Divider />
|
||||
<ProfilePhone customer={customer} />
|
||||
<Divider />
|
||||
{/* <ProfilePassword customer={customer} />
|
||||
<Divider /> */}
|
||||
<ProfileBillingAddress customer={customer} regions={regions} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Divider = () => {
|
||||
return <div className="w-full h-px bg-gray-200" />
|
||||
}
|
||||
;``
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import LoginTemplate from "@modules/account/templates/login-template"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Sign in",
|
||||
description: "Sign in to your Medusa Store account.",
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
return <LoginTemplate />
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { retrieveCustomer } from "@lib/data/customer"
|
||||
import { Toaster } from "@medusajs/ui"
|
||||
import AccountLayout from "@modules/account/templates/account-layout"
|
||||
|
||||
export default async function AccountPageLayout({
|
||||
dashboard,
|
||||
login,
|
||||
}: {
|
||||
dashboard?: React.ReactNode
|
||||
login?: React.ReactNode
|
||||
}) {
|
||||
const customer = await retrieveCustomer().catch(() => null)
|
||||
|
||||
return (
|
||||
<AccountLayout customer={customer}>
|
||||
{customer ? dashboard : login}
|
||||
<Toaster />
|
||||
</AccountLayout>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import Spinner from "@modules/common/icons/spinner"
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="flex items-center justify-center w-full h-full text-ui-fg-base">
|
||||
<Spinner size={36} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import SkeletonCartPage from "@modules/skeletons/templates/skeleton-cart-page"
|
||||
|
||||
export default function Loading() {
|
||||
return <SkeletonCartPage />
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import InteractiveLink from "@modules/common/components/interactive-link"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "404",
|
||||
description: "Something went wrong",
|
||||
}
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-64px)]">
|
||||
<h1 className="text-2xl-semi text-ui-fg-base">Page not found</h1>
|
||||
<p className="text-small-regular text-ui-fg-base">
|
||||
The cart you tried to access does not exist. Clear your cookies and try
|
||||
again.
|
||||
</p>
|
||||
<InteractiveLink href="/">Go to frontpage</InteractiveLink>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { retrieveCart } from "@lib/data/cart"
|
||||
import { retrieveCustomer } from "@lib/data/customer"
|
||||
import CartTemplate from "@modules/cart/templates"
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Cart",
|
||||
description: "View your cart",
|
||||
}
|
||||
|
||||
export default async function Cart() {
|
||||
const cart = await retrieveCart().catch((error) => {
|
||||
console.error(error)
|
||||
return notFound()
|
||||
})
|
||||
|
||||
const customer = await retrieveCustomer()
|
||||
|
||||
return <CartTemplate cart={cart} customer={customer} />
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { getCategoryByHandle, listCategories } from "@lib/data/categories"
|
||||
import { listRegions } from "@lib/data/regions"
|
||||
import { StoreRegion } from "@medusajs/types"
|
||||
import CategoryTemplate from "@modules/categories/templates"
|
||||
import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ category: string[]; countryCode: string }>
|
||||
searchParams: Promise<{
|
||||
sortBy?: SortOptions
|
||||
page?: string
|
||||
}>
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const product_categories = await listCategories()
|
||||
|
||||
if (!product_categories) {
|
||||
return []
|
||||
}
|
||||
|
||||
const countryCodes = await listRegions().then((regions: StoreRegion[]) =>
|
||||
regions?.map((r) => r.countries?.map((c) => c.iso_2)).flat()
|
||||
)
|
||||
|
||||
const categoryHandles = product_categories.map(
|
||||
(category: any) => category.handle
|
||||
)
|
||||
|
||||
const staticParams = countryCodes
|
||||
?.map((countryCode: string | undefined) =>
|
||||
categoryHandles.map((handle: any) => ({
|
||||
countryCode,
|
||||
category: [handle],
|
||||
}))
|
||||
)
|
||||
.flat()
|
||||
|
||||
return staticParams
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: Props): Promise<Metadata> {
|
||||
const params = await props.params
|
||||
try {
|
||||
const productCategory = await getCategoryByHandle(params.category)
|
||||
|
||||
const title = productCategory.name + " | Medusa Store"
|
||||
|
||||
const description = productCategory.description ?? `${title} category.`
|
||||
|
||||
return {
|
||||
title: `${title} | Medusa Store`,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: `${params.category.join("/")}`,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
notFound()
|
||||
}
|
||||
}
|
||||
|
||||
export default async function CategoryPage(props: Props) {
|
||||
const searchParams = await props.searchParams
|
||||
const params = await props.params
|
||||
const { sortBy, page } = searchParams
|
||||
|
||||
const productCategory = await getCategoryByHandle(params.category)
|
||||
|
||||
if (!productCategory) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<CategoryTemplate
|
||||
category={productCategory}
|
||||
sortBy={sortBy}
|
||||
page={page}
|
||||
countryCode={params.countryCode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { getCollectionByHandle, listCollections } from "@lib/data/collections"
|
||||
import { listRegions } from "@lib/data/regions"
|
||||
import { StoreCollection, StoreRegion } from "@medusajs/types"
|
||||
import CollectionTemplate from "@modules/collections/templates"
|
||||
import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ handle: string; countryCode: string }>
|
||||
searchParams: Promise<{
|
||||
page?: string
|
||||
sortBy?: SortOptions
|
||||
}>
|
||||
}
|
||||
|
||||
export const PRODUCT_LIMIT = 12
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const { collections } = await listCollections({
|
||||
fields: "*products",
|
||||
})
|
||||
|
||||
if (!collections) {
|
||||
return []
|
||||
}
|
||||
|
||||
const countryCodes = await listRegions().then(
|
||||
(regions: StoreRegion[]) =>
|
||||
regions
|
||||
?.map((r) => r.countries?.map((c) => c.iso_2))
|
||||
.flat()
|
||||
.filter(Boolean) as string[]
|
||||
)
|
||||
|
||||
const collectionHandles = collections.map(
|
||||
(collection: StoreCollection) => collection.handle
|
||||
)
|
||||
|
||||
const staticParams = countryCodes
|
||||
?.map((countryCode: string) =>
|
||||
collectionHandles.map((handle: string | undefined) => ({
|
||||
countryCode,
|
||||
handle,
|
||||
}))
|
||||
)
|
||||
.flat()
|
||||
|
||||
return staticParams
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: Props): Promise<Metadata> {
|
||||
const params = await props.params
|
||||
const collection = await getCollectionByHandle(params.handle)
|
||||
|
||||
if (!collection) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const metadata = {
|
||||
title: `${collection.title} | Medusa Store`,
|
||||
description: `${collection.title} collection`,
|
||||
} as Metadata
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
export default async function CollectionPage(props: Props) {
|
||||
const searchParams = await props.searchParams
|
||||
const params = await props.params
|
||||
const { sortBy, page } = searchParams
|
||||
|
||||
const collection = await getCollectionByHandle(params.handle).then(
|
||||
(collection: StoreCollection) => collection
|
||||
)
|
||||
|
||||
if (!collection) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<CollectionTemplate
|
||||
collection={collection}
|
||||
page={page}
|
||||
sortBy={sortBy}
|
||||
countryCode={params.countryCode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
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 CartMismatchBanner from "@modules/layout/components/cart-mismatch-banner"
|
||||
import Footer from "@modules/layout/templates/footer"
|
||||
import Nav from "@modules/layout/templates/nav"
|
||||
import FreeShippingPriceNudge from "@modules/shipping/components/free-shipping-price-nudge"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(getBaseURL()),
|
||||
}
|
||||
|
||||
export default async function PageLayout(props: { children: React.ReactNode }) {
|
||||
const customer = await retrieveCustomer()
|
||||
const cart = await retrieveCart()
|
||||
let shippingOptions: StoreCartShippingOption[] = []
|
||||
|
||||
if (cart) {
|
||||
const { shipping_options } = await listCartOptions()
|
||||
|
||||
shippingOptions = shipping_options
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Nav />
|
||||
{customer && cart && (
|
||||
<CartMismatchBanner customer={customer} cart={cart} />
|
||||
)}
|
||||
|
||||
{cart && (
|
||||
<FreeShippingPriceNudge
|
||||
variant="popup"
|
||||
cart={cart}
|
||||
shippingOptions={shippingOptions}
|
||||
/>
|
||||
)}
|
||||
{props.children}
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import InteractiveLink from "@modules/common/components/interactive-link"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "404",
|
||||
description: "Something went wrong",
|
||||
}
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 items-center justify-center min-h-[calc(100vh-64px)]">
|
||||
<h1 className="text-2xl-semi text-ui-fg-base">Page not found</h1>
|
||||
<p className="text-small-regular text-ui-fg-base">
|
||||
The page you tried to access does not exist.
|
||||
</p>
|
||||
<InteractiveLink href="/">Go to frontpage</InteractiveLink>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import SkeletonOrderConfirmed from "@modules/skeletons/templates/skeleton-order-confirmed"
|
||||
|
||||
export default function Loading() {
|
||||
return <SkeletonOrderConfirmed />
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { retrieveOrder } from "@lib/data/orders"
|
||||
import OrderCompletedTemplate from "@modules/order/templates/order-completed-template"
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ id: string }>
|
||||
}
|
||||
export const metadata: Metadata = {
|
||||
title: "Order Confirmed",
|
||||
description: "You purchase was successful",
|
||||
}
|
||||
|
||||
export default async function OrderConfirmedPage(props: Props) {
|
||||
const params = await props.params
|
||||
const order = await retrieveOrder(params.id).catch(() => null)
|
||||
|
||||
if (!order) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
return <OrderCompletedTemplate order={order} />
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { acceptTransferRequest } from "@lib/data/orders"
|
||||
import { Heading, Text } from "@medusajs/ui"
|
||||
import TransferImage from "@modules/order/components/transfer-image"
|
||||
|
||||
export default async function TransferPage({
|
||||
params,
|
||||
}: {
|
||||
params: { id: string; token: string }
|
||||
}) {
|
||||
const { id, token } = params
|
||||
|
||||
const { success, error } = await acceptTransferRequest(id, token)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4 items-start w-2/5 mx-auto mt-10 mb-20">
|
||||
<TransferImage />
|
||||
<div className="flex flex-col gap-y-6">
|
||||
{success && (
|
||||
<>
|
||||
<Heading level="h1" className="text-xl text-zinc-900">
|
||||
Order transfered!
|
||||
</Heading>
|
||||
<Text className="text-zinc-600">
|
||||
Order {id} has been successfully transfered to the new owner.
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
{!success && (
|
||||
<>
|
||||
<Text className="text-zinc-600">
|
||||
There was an error accepting the transfer. Please try again.
|
||||
</Text>
|
||||
{error && (
|
||||
<Text className="text-red-500">Error message: {error}</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { declineTransferRequest } from "@lib/data/orders"
|
||||
import { Heading, Text } from "@medusajs/ui"
|
||||
import TransferImage from "@modules/order/components/transfer-image"
|
||||
|
||||
export default async function TransferPage({
|
||||
params,
|
||||
}: {
|
||||
params: { id: string; token: string }
|
||||
}) {
|
||||
const { id, token } = params
|
||||
|
||||
const { success, error } = await declineTransferRequest(id, token)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4 items-start w-2/5 mx-auto mt-10 mb-20">
|
||||
<TransferImage />
|
||||
<div className="flex flex-col gap-y-6">
|
||||
{success && (
|
||||
<>
|
||||
<Heading level="h1" className="text-xl text-zinc-900">
|
||||
Order transfer declined!
|
||||
</Heading>
|
||||
<Text className="text-zinc-600">
|
||||
Transfer of order {id} has been successfully declined.
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
{!success && (
|
||||
<>
|
||||
<Text className="text-zinc-600">
|
||||
There was an error declining the transfer. Please try again.
|
||||
</Text>
|
||||
{error && (
|
||||
<Text className="text-red-500">Error message: {error}</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Heading, Text } from "@medusajs/ui"
|
||||
import TransferActions from "@modules/order/components/transfer-actions"
|
||||
import TransferImage from "@modules/order/components/transfer-image"
|
||||
|
||||
export default async function TransferPage({
|
||||
params,
|
||||
}: {
|
||||
params: { id: string; token: string }
|
||||
}) {
|
||||
const { id, token } = params
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4 items-start w-2/5 mx-auto mt-10 mb-20">
|
||||
<TransferImage />
|
||||
<div className="flex flex-col gap-y-6">
|
||||
<Heading level="h1" className="text-xl text-zinc-900">
|
||||
Transfer request for order {id}
|
||||
</Heading>
|
||||
<Text className="text-zinc-600">
|
||||
You've received a request to transfer ownership of your order ({id}).
|
||||
If you agree to this request, you can approve the transfer by clicking
|
||||
the button below.
|
||||
</Text>
|
||||
<div className="w-full h-px bg-zinc-200" />
|
||||
<Text className="text-zinc-600">
|
||||
If you accept, the new owner will take over all responsibilities and
|
||||
permissions associated with this order.
|
||||
</Text>
|
||||
<Text className="text-zinc-600">
|
||||
If you do not recognize this request or wish to retain ownership, no
|
||||
further action is required.
|
||||
</Text>
|
||||
<div className="w-full h-px bg-zinc-200" />
|
||||
<TransferActions id={id} token={token} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import FeaturedProducts from "@modules/home/components/featured-products"
|
||||
import Hero from "@modules/home/components/hero"
|
||||
import { listCollections } from "@lib/data/collections"
|
||||
import { getRegion } from "@lib/data/regions"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Medusa Next.js Starter Template",
|
||||
description:
|
||||
"A performant frontend ecommerce starter template with Next.js 15 and Medusa.",
|
||||
}
|
||||
|
||||
export default async function Home(props: {
|
||||
params: Promise<{ countryCode: string }>
|
||||
}) {
|
||||
const params = await props.params
|
||||
|
||||
const { countryCode } = params
|
||||
|
||||
const region = await getRegion(countryCode)
|
||||
|
||||
const { collections } = await listCollections({
|
||||
fields: "id, handle, title",
|
||||
})
|
||||
|
||||
if (!collections || !region) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<div className="py-12">
|
||||
<ul className="flex flex-col gap-x-6">
|
||||
<FeaturedProducts collections={collections} region={region} />
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
import { listProducts } from "@lib/data/products"
|
||||
import { getRegion, listRegions } from "@lib/data/regions"
|
||||
import ProductTemplate from "@modules/products/templates"
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ countryCode: string; handle: string }>
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
try {
|
||||
const countryCodes = await listRegions().then((regions) =>
|
||||
regions?.map((r) => r.countries?.map((c) => c.iso_2)).flat()
|
||||
)
|
||||
|
||||
if (!countryCodes) {
|
||||
return []
|
||||
}
|
||||
|
||||
const promises = countryCodes.map(async (country) => {
|
||||
const { response } = await listProducts({
|
||||
countryCode: country,
|
||||
queryParams: { limit: 100, fields: "handle" },
|
||||
})
|
||||
|
||||
return {
|
||||
country,
|
||||
products: response.products,
|
||||
}
|
||||
})
|
||||
|
||||
const countryProducts = await Promise.all(promises)
|
||||
|
||||
return countryProducts
|
||||
.flatMap((countryData) =>
|
||||
countryData.products.map((product) => ({
|
||||
countryCode: countryData.country,
|
||||
handle: product.handle,
|
||||
}))
|
||||
)
|
||||
.filter((param) => param.handle)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to generate static paths for product pages: ${
|
||||
error instanceof Error ? error.message : "Unknown error"
|
||||
}.`
|
||||
)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: Props): Promise<Metadata> {
|
||||
const params = await props.params
|
||||
const { handle } = params
|
||||
const region = await getRegion(params.countryCode)
|
||||
|
||||
if (!region) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const product = await listProducts({
|
||||
countryCode: params.countryCode,
|
||||
queryParams: { handle },
|
||||
}).then(({ response }) => response.products[0])
|
||||
|
||||
if (!product) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${product.title} | Medusa Store`,
|
||||
description: `${product.title}`,
|
||||
openGraph: {
|
||||
title: `${product.title} | Medusa Store`,
|
||||
description: `${product.title}`,
|
||||
images: product.thumbnail ? [product.thumbnail] : [],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function ProductPage(props: Props) {
|
||||
const params = await props.params
|
||||
const region = await getRegion(params.countryCode)
|
||||
|
||||
if (!region) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const pricedProduct = await listProducts({
|
||||
countryCode: params.countryCode,
|
||||
queryParams: { handle: params.handle },
|
||||
}).then(({ response }) => response.products[0])
|
||||
|
||||
if (!pricedProduct) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<ProductTemplate
|
||||
product={pricedProduct}
|
||||
region={region}
|
||||
countryCode={params.countryCode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
|
||||
import StoreTemplate from "@modules/store/templates"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Store",
|
||||
description: "Explore all of our products.",
|
||||
}
|
||||
|
||||
type Params = {
|
||||
searchParams: Promise<{
|
||||
sortBy?: SortOptions
|
||||
page?: string
|
||||
}>
|
||||
params: Promise<{
|
||||
countryCode: string
|
||||
}>
|
||||
}
|
||||
|
||||
export default async function StorePage(props: Params) {
|
||||
const params = await props.params;
|
||||
const searchParams = await props.searchParams;
|
||||
const { sortBy, page } = searchParams
|
||||
|
||||
return (
|
||||
<StoreTemplate
|
||||
sortBy={sortBy}
|
||||
page={page}
|
||||
countryCode={params.countryCode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user