feat(dashboard,admin-shared): Add injection zones to regions, shipping profiles, and locations (#7467)

This commit is contained in:
Kasper Fabricius Kristensen
2024-05-27 12:46:58 +02:00
committed by GitHub
parent ebc4215f16
commit 0b0e210f67
64 changed files with 457 additions and 306 deletions
@@ -85,6 +85,29 @@ const GIFT_CARD_INJECTION_ZONES = [
"custom_gift_card.after",
] as const
const REGION_INJECTION_ZONES = [
"region.details.before",
"region.details.after",
"region.list.before",
"region.list.after",
] as const
const SHIPPING_PROFILE_INJECTION_ZONES = [
"shipping_profile.details.before",
"shipping_profile.details.after",
"shipping_profile.list.before",
"shipping_profile.list.after",
] as const
const LOCATION_INJECTION_ZONES = [
"location.details.before",
"location.details.after",
"location.details.side.before",
"location.details.side.after",
"location.list.before",
"location.list.after",
] as const
const LOGIN_INJECTION_ZONES = ["login.before", "login.after"] as const
/**
@@ -103,5 +126,8 @@ export const INJECTION_ZONES = [
...DISCOUNT_INJECTION_ZONES,
...PROMOTION_INJECTION_ZONES,
...GIFT_CARD_INJECTION_ZONES,
...REGION_INJECTION_ZONES,
...SHIPPING_PROFILE_INJECTION_ZONES,
...LOCATION_INJECTION_ZONES,
...LOGIN_INJECTION_ZONES,
] as const
@@ -54,8 +54,8 @@ const useSettingRoutes = (): NavItemProps[] => {
to: "/settings/shipping-profiles",
},
{
label: t("shipping.domain"),
to: "/settings/shipping",
label: t("location.domain"),
to: "/settings/locations",
},
],
[t]
@@ -675,8 +675,8 @@
"invalidEmail": "Email must be a valid email address."
}
},
"shipping": {
"title": "Location & Shipping",
"location": {
"title": "Locations & Shipping",
"domain": "Location & Shipping",
"description": "Choose where you ship and how much you charge for shipping at checkout. Define shipping options specific for your locations.",
"createLocation": "Create location",
@@ -682,33 +682,33 @@ export const RouteMap: RouteObject[] = [
],
},
{
path: "shipping",
path: "locations",
element: <Outlet />,
handle: {
crumb: () => "Location & Shipping",
crumb: () => "Locations & Shipping",
},
children: [
{
path: "",
lazy: () => import("../../routes/shipping/location-list"),
lazy: () => import("../../routes/locations/location-list"),
},
{
path: "create",
lazy: () => import("../../routes/shipping/location-create"),
lazy: () => import("../../routes/locations/location-create"),
},
{
path: ":location_id",
lazy: () => import("../../routes/shipping/location-details"),
lazy: () => import("../../routes/locations/location-details"),
children: [
{
path: "edit",
lazy: () => import("../../routes/shipping/location-edit"),
lazy: () => import("../../routes/locations/location-edit"),
},
{
path: "sales-channels/edit",
lazy: () =>
import(
"../../routes/shipping/location-add-sales-channels"
"../../routes/locations/location-add-sales-channels"
),
},
{
@@ -717,7 +717,7 @@ export const RouteMap: RouteObject[] = [
{
path: "service-zones/create",
lazy: () =>
import("../../routes/shipping/service-zone-create"),
import("../../routes/locations/service-zone-create"),
},
{
path: "service-zone/:zone_id",
@@ -725,13 +725,15 @@ export const RouteMap: RouteObject[] = [
{
path: "edit",
lazy: () =>
import("../../routes/shipping/service-zone-edit"),
import(
"../../routes/locations/service-zone-edit"
),
},
{
path: "edit-areas",
lazy: () =>
import(
"../../routes/shipping/service-zone-areas-edit"
"../../routes/locations/service-zone-areas-edit"
),
},
{
@@ -741,7 +743,7 @@ export const RouteMap: RouteObject[] = [
path: "create",
lazy: () =>
import(
"../../routes/shipping/shipping-options-create"
"../../routes/locations/shipping-options-create"
),
},
{
@@ -751,14 +753,14 @@ export const RouteMap: RouteObject[] = [
path: "edit",
lazy: () =>
import(
"../../routes/shipping/shipping-option-edit"
"../../routes/locations/shipping-option-edit"
),
},
{
path: "edit-pricing",
lazy: () =>
import(
"../../routes/shipping/shipping-options-edit-pricing"
"../../routes/locations/shipping-options-edit-pricing"
),
},
],
@@ -55,7 +55,7 @@ export const CreateLocationForm = () => {
address: values.address,
})
handleSuccess("/settings/shipping")
handleSuccess("/settings/locations")
toast.success(t("general.success"), {
description: t("locations.toast.create"),
@@ -92,10 +92,10 @@ export const CreateLocationForm = () => {
<div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16">
<div>
<Heading className="capitalize">
{t("shipping.createLocation")}
{t("location.createLocation")}
</Heading>
<Text size="small" className="text-ui-fg-subtle">
{t("shipping.createLocationDetailsHint")}
{t("location.createLocationDetailsHint")}
</Text>
</div>
<div className="grid grid-cols-2 gap-4">
@@ -1,12 +1,3 @@
import { useNavigate } from "react-router-dom"
import { useTranslation } from "react-i18next"
import { useMemo, useState } from "react"
import {
FulfillmentSetDTO,
ServiceZoneDTO,
ShippingOptionDTO,
StockLocationDTO,
} from "@medusajs/types"
import {
ChevronDownMini,
CurrencyDollar,
@@ -15,6 +6,12 @@ import {
Plus,
Trash,
} from "@medusajs/icons"
import {
FulfillmentSetDTO,
ServiceZoneDTO,
ShippingOptionDTO,
StockLocationDTO,
} from "@medusajs/types"
import {
Badge,
Button,
@@ -25,19 +22,22 @@ import {
toast,
usePrompt,
} from "@medusajs/ui"
import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { countries as staticCountries } from "../../../../../lib/countries"
import { NoRecords } from "../../../../../components/common/empty-table-content"
import { ListSummary } from "../../../../../components/common/list-summary"
import { useDeleteShippingOption } from "../../../../../hooks/api/shipping-options"
import {
useCreateFulfillmentSet,
useDeleteFulfillmentSet,
useDeleteServiceZone,
useDeleteStockLocation,
} from "../../../../../hooks/api/stock-locations"
import { useDeleteShippingOption } from "../../../../../hooks/api/shipping-options"
import { countries as staticCountries } from "../../../../../lib/countries"
import { formatProvider } from "../../../../../lib/format-provider"
import { NoRecords } from "../../../../../components/common/empty-table-content"
import { ListSummary } from "../../../../../components/common/list-summary"
import {
isOptionEnabledInStore,
isReturnOption,
@@ -103,7 +103,7 @@ function ShippingOption({
const handleDelete = async () => {
const res = await prompt({
title: t("general.areYouSure"),
description: t("shipping.shippingOptions.deleteWarning", {
description: t("location.shippingOptions.deleteWarning", {
name: option.name,
}),
confirmText: t("actions.delete"),
@@ -118,7 +118,7 @@ function ShippingOption({
await deleteOption()
toast.success(t("general.success"), {
description: t("shipping.shippingOptions.toast.delete", {
description: t("location.shippingOptions.toast.delete", {
name: option.name,
}),
dismissLabel: t("actions.close"),
@@ -141,7 +141,7 @@ function ShippingOption({
</div>
{isInStore && (
<Badge className="mr-4" color="purple">
{t("shipping.shippingOptions.inStore")}
{t("location.shippingOptions.inStore")}
</Badge>
)}
<ActionMenu
@@ -150,13 +150,13 @@ function ShippingOption({
actions: [
{
icon: <PencilSquare />,
label: t("shipping.serviceZone.editOption"),
to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit`,
label: t("location.serviceZone.editOption"),
to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit`,
},
{
label: t("shipping.serviceZone.editPrices"),
label: t("location.serviceZone.editPrices"),
icon: <CurrencyDollar />,
to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit-pricing`,
to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit-pricing`,
},
{
label: t("actions.delete"),
@@ -196,18 +196,18 @@ function ServiceZoneOptions({
<div className="mt-4 flex flex-col border-t border-dashed px-6 py-4">
<div className="item-center flex justify-between">
<span className="text-ui-fg-subtle txt-small self-center font-medium">
{t("shipping.serviceZone.shippingOptions")}
{t("location.serviceZone.shippingOptions")}
</span>
<Button
className="text-ui-fg-interactive txt-small px-0 font-medium hover:bg-transparent active:bg-transparent"
variant="transparent"
onClick={() =>
navigate(
`/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/shipping-option/create`
`/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/shipping-option/create`
)
}
>
{t("shipping.serviceZone.addOption")}
{t("location.serviceZone.addOption")}
</Button>
</div>
@@ -228,18 +228,18 @@ function ServiceZoneOptions({
<div className="-mb-4 flex flex-col border-t border-dashed px-6 py-4">
<div className="item-center flex justify-between">
<span className="text-ui-fg-subtle txt-small self-center font-medium">
{t("shipping.serviceZone.returnOptions")}
{t("location.serviceZone.returnOptions")}
</span>
<Button
className="text-ui-fg-interactive txt-small px-0 font-medium hover:bg-transparent active:bg-transparent"
variant="transparent"
onClick={() =>
navigate(
`/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/shipping-option/create?is_return`
`/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/shipping-option/create?is_return`
)
}
>
{t("shipping.serviceZone.addOption")}
{t("location.serviceZone.addOption")}
</Button>
</div>
@@ -280,7 +280,7 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
const handleDelete = async () => {
const res = await prompt({
title: t("general.areYouSure"),
description: t("shipping.serviceZone.deleteWarning", {
description: t("location.serviceZone.deleteWarning", {
name: zone.name,
}),
confirmText: t("actions.delete"),
@@ -295,7 +295,7 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
await deleteZone()
toast.success(t("general.success"), {
description: t("shipping.serviceZone.toast.delete", {
description: t("location.serviceZone.toast.delete", {
name: zone.name,
}),
dismissLabel: t("actions.close"),
@@ -350,14 +350,14 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
<span>·</span>
<Text className="text-ui-fg-subtle txt-small">
{shippingOptionsCount}{" "}
{t("shipping.serviceZone.optionsLength", {
{t("location.serviceZone.optionsLength", {
count: shippingOptionsCount,
})}
</Text>
<span>·</span>
<Text className="text-ui-fg-subtle txt-small">
{returnOptionsCount}{" "}
{t("shipping.serviceZone.returnOptionsLength", {
{t("location.serviceZone.returnOptionsLength", {
count: returnOptionsCount,
})}
</Text>
@@ -389,12 +389,12 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
{
label: t("actions.edit"),
icon: <PencilSquare />,
to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit`,
to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit`,
},
{
label: t("shipping.serviceZone.areas.manage"),
label: t("location.serviceZone.areas.manage"),
icon: <Map />,
to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit-areas`,
to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit-areas`,
},
{
label: t("actions.delete"),
@@ -469,7 +469,7 @@ function FulfillmentSet(props: FulfillmentSetProps) {
const handleDelete = async () => {
const res = await prompt({
title: t("general.areYouSure"),
description: t("shipping.fulfillmentSet.disableWarning", {
description: t("location.fulfillmentSet.disableWarning", {
name: fulfillmentSet?.name,
}),
confirmText: t("actions.delete"),
@@ -484,7 +484,7 @@ function FulfillmentSet(props: FulfillmentSetProps) {
await deleteFulfillmentSet()
toast.success(t("general.success"), {
description: t("shipping.fulfillmentSet.toast.disable", {
description: t("location.fulfillmentSet.toast.disable", {
name: fulfillmentSet?.name,
}),
dismissLabel: t("actions.close"),
@@ -502,7 +502,7 @@ function FulfillmentSet(props: FulfillmentSetProps) {
<div className="flex flex-col divide-y">
<div className="flex items-center justify-between px-6 py-4">
<Text size="large" weight="plus" className="flex-1" as="div">
{t(`shipping.fulfillmentSet.${type}.offers`)}
{t(`location.fulfillmentSet.${type}.offers`)}
</Text>
<div className="flex items-center gap-4">
<StatusBadge color={fulfillmentSetExists ? "green" : "red"}>
@@ -517,10 +517,10 @@ function FulfillmentSet(props: FulfillmentSetProps) {
actions: [
{
icon: <Plus />,
label: t("shipping.fulfillmentSet.addZone"),
label: t("location.fulfillmentSet.addZone"),
onClick: () =>
navigate(
`/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSet.id}/service-zones/create`
`/settings/locations/${locationId}/fulfillment-set/${fulfillmentSet.id}/service-zones/create`
),
disabled: !fulfillmentSetExists,
},
@@ -543,7 +543,7 @@ function FulfillmentSet(props: FulfillmentSetProps) {
{fulfillmentSetExists && !hasServiceZones && (
<div className="text-ui-fg-muted txt-medium flex flex-col items-center justify-center gap-y-4 py-8">
<NoRecords
message={t("shipping.fulfillmentSet.placeholder")}
message={t("location.fulfillmentSet.placeholder")}
className="h-fit"
/>
@@ -551,11 +551,11 @@ function FulfillmentSet(props: FulfillmentSetProps) {
variant="secondary"
onClick={() =>
navigate(
`/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSet.id}/service-zones/create`
`/settings/locations/${locationId}/fulfillment-set/${fulfillmentSet.id}/service-zones/create`
)
}
>
{t("shipping.fulfillmentSet.addZone")}
{t("location.fulfillmentSet.addZone")}
</Button>
</div>
)}
@@ -586,7 +586,7 @@ const Actions = ({ location }: { location: StockLocationDTO }) => {
const handleDelete = async () => {
const res = await prompt({
title: t("general.areYouSure"),
description: t("shipping.deleteLocationWarning", {
description: t("location.deleteLocationWarning", {
name: location.name,
}),
verificationText: location.name,
@@ -602,7 +602,7 @@ const Actions = ({ location }: { location: StockLocationDTO }) => {
try {
await mutateAsync(undefined)
toast.success(t("general.success"), {
description: t("shipping.toast.delete"),
description: t("location.toast.delete"),
dismissLabel: t("actions.close"),
})
} catch (e) {
@@ -611,7 +611,7 @@ const Actions = ({ location }: { location: StockLocationDTO }) => {
dismissLabel: t("actions.close"),
})
}
navigate("/settings/shipping", { replace: true })
navigate("/settings/locations", { replace: true })
}
return (
@@ -1,11 +1,11 @@
import { Channels, PencilSquare } from "@medusajs/icons"
import { StockLocationDTO } from "@medusajs/types"
import { Heading, Text } from "@medusajs/ui"
import { Trans, useTranslation } from "react-i18next"
import { StockLocationDTO } from "@medusajs/types"
import { Channels, PencilSquare } from "@medusajs/icons"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { ListSummary } from "../../../../../components/common/list-summary"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
type Props = {
location: StockLocationDTO
@@ -20,7 +20,7 @@ function LocationsSalesChannelsSection({ location }: Props) {
return (
<div className="shadow-elevation-card-rest bg-ui-bg-base rounded-md p-4">
<div className="mb-4 flex items-center justify-between">
<Heading level="h2">{t("shipping.salesChannels.title")}</Heading>
<Heading level="h2">{t("location.salesChannels.title")}</Heading>
<ActionMenu
groups={[
{
@@ -43,7 +43,7 @@ function LocationsSalesChannelsSection({ location }: Props) {
</div>
{noChannels ? (
<Text size="small" leading="compact" className="text-ui-fg-subtle">
{t("shipping.salesChannels.placeholder")}
{t("location.salesChannels.placeholder")}
</Text>
) : (
<ListSummary
@@ -0,0 +1,92 @@
import { Outlet, useLoaderData, useParams } from "react-router-dom"
import { JsonViewSection } from "../../../components/common/json-view-section"
import { useStockLocation } from "../../../hooks/api/stock-locations"
import { LocationGeneralSection } from "./components/location-general-section"
import LocationsSalesChannelsSection from "./components/location-sales-channels-section/locations-sales-channels-section"
import { locationLoader } from "./loader"
import after from "virtual:medusa/widgets/location/details/after"
import before from "virtual:medusa/widgets/location/details/before"
import sideAfter from "virtual:medusa/widgets/location/details/side/after"
import sideBefore from "virtual:medusa/widgets/location/details/side/before"
export const LocationDetails = () => {
const initialData = useLoaderData() as Awaited<
ReturnType<typeof locationLoader>
>
const { location_id } = useParams()
const {
stock_location: location,
isPending: isLoading,
isError,
error,
} = useStockLocation(
location_id!,
{
fields:
"name,*sales_channels,address.city,address.country_code,fulfillment_sets.type,fulfillment_sets.name,*fulfillment_sets.service_zones.geo_zones,*fulfillment_sets.service_zones,*fulfillment_sets.service_zones.shipping_options,*fulfillment_sets.service_zones.shipping_options.rules,*fulfillment_sets.service_zones.shipping_options.shipping_profile",
},
{
initialData,
}
)
// TODO: Move to loading.tsx and set as Suspense fallback for the route
if (isLoading || !location) {
return <div>Loading...</div>
}
if (isError) {
throw error
}
return (
<div className="flex flex-col gap-y-3">
{before.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={location} />
</div>
)
})}
<div className="flex flex-col gap-x-4 lg:flex-row xl:items-start">
<div className="flex w-full flex-col gap-y-2">
<LocationGeneralSection location={location} />
{after.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={location} />
</div>
)
})}
<div className="hidden xl:block">
<JsonViewSection data={location} />
</div>
</div>
<div className="hidden w-full max-w-[400px] flex-col gap-y-2 xl:flex">
{sideBefore.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={location} />
</div>
)
})}
<LocationsSalesChannelsSection location={location} />
{sideAfter.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={location} />
</div>
)
})}
<div className="xl:hidden">
<JsonViewSection data={location} />
</div>
</div>
<Outlet />
</div>
</div>
)
}
@@ -1,3 +1,9 @@
import { Buildings, PencilSquare, Trash } from "@medusajs/icons"
import {
FulfillmentSetDTO,
SalesChannelDTO,
StockLocationDTO,
} from "@medusajs/types"
import {
Button,
Container,
@@ -6,19 +12,13 @@ import {
toast,
usePrompt,
} from "@medusajs/ui"
import {
FulfillmentSetDTO,
SalesChannelDTO,
StockLocationDTO,
} from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { Buildings, PencilSquare, Trash } from "@medusajs/icons"
import { useNavigate } from "react-router-dom"
import { countries } from "../../../../../lib/countries"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { useDeleteStockLocation } from "../../../../../hooks/api/stock-locations"
import { BadgeListSummary } from "../../../../../components/common/badge-list-summary"
import { useDeleteStockLocation } from "../../../../../hooks/api/stock-locations"
import { countries } from "../../../../../lib/countries"
type SalesChannelsProps = {
salesChannels?: SalesChannelDTO[]
@@ -37,7 +37,7 @@ function SalesChannels(props: SalesChannelsProps) {
className="text-ui-fg-subtle flex-1"
as="div"
>
{t(`shipping.fulfillmentSet.salesChannels`)}
{t(`location.fulfillmentSet.salesChannels`)}
</Text>
<div className="flex-1 text-left">
{salesChannels?.length ? (
@@ -80,7 +80,7 @@ function FulfillmentSet(props: FulfillmentSetProps) {
className="text-ui-fg-subtle flex-1"
as="div"
>
{t(`shipping.fulfillmentSet.${type}.title`)}
{t(`location.fulfillmentSet.${type}.title`)}
</Text>
<div className="flex-1 text-left">
<StatusBadge color={fulfillmentSetExists ? "green" : "red"}>
@@ -107,7 +107,7 @@ function Location(props: LocationProps) {
const handleDelete = async () => {
const result = await prompt({
title: t("general.areYouSure"),
description: t("shipping.deleteLocation.confirm", {
description: t("location.deleteLocation.confirm", {
name: location.name,
}),
confirmText: t("actions.remove"),
@@ -122,7 +122,7 @@ function Location(props: LocationProps) {
await deleteLocation()
toast.success(t("general.success"), {
description: t("shipping.deleteLocation.success", {
description: t("location.deleteLocation.success", {
name: location.name,
}),
dismissLabel: t("general.close"),
@@ -139,14 +139,14 @@ function Location(props: LocationProps) {
<Container className="flex flex-col divide-y p-0">
<div className="px-6 py-5">
<div className="flex flex-row items-center justify-between gap-x-4">
{/*ICON*/}
{/* ICON*/}
<div className="grow-0 rounded-lg border">
<div className="bg-ui-bg-field m-1 rounded-md p-2">
<Buildings className="text-ui-fg-subtle" />
</div>
</div>
{/*LOCATION INFO*/}
{/* LOCATION INFO*/}
<div className="grow-1 flex flex-1 flex-col">
<Text weight="plus">{location.name}</Text>
<Text className="text-ui-fg-subtle txt-small">
@@ -160,7 +160,7 @@ function Location(props: LocationProps) {
</Text>
</div>
{/*ACTION*/}
{/* ACTION*/}
<div className="flex h-[12px] grow-0 items-center gap-4 divide-x overflow-hidden">
<ActionMenu
groups={[
@@ -169,10 +169,10 @@ function Location(props: LocationProps) {
{
label: t("actions.edit"),
icon: <PencilSquare />,
to: `/settings/shipping/${location.id}/edit`,
to: `/settings/locations/${location.id}/edit`,
},
{
label: t("shipping.deleteLocation.label"),
label: t("location.deleteLocation.label"),
icon: <Trash />,
onClick: handleDelete,
},
@@ -182,7 +182,7 @@ function Location(props: LocationProps) {
/>
<Button
className="text-ui-fg-interactive rounded-none pl-5 hover:bg-transparent active:bg-transparent"
onClick={() => navigate(`/settings/shipping/${location.id}`)}
onClick={() => navigate(`/settings/locations/${location.id}`)}
variant="transparent"
>
{t("actions.viewDetails")}
@@ -0,0 +1,62 @@
import { Button, Container, Heading, Text } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { Link, Outlet, useLoaderData } from "react-router-dom"
import { useStockLocations } from "../../../hooks/api/stock-locations"
import Location from "./components/location/location"
import { locationListFields } from "./const"
import { shippingListLoader } from "./loader"
import after from "virtual:medusa/widgets/location/list/after"
import before from "virtual:medusa/widgets/location/list/before"
export function LocationList() {
const { t } = useTranslation()
const initialData = useLoaderData() as Awaited<
ReturnType<typeof shippingListLoader>
>
const { stock_locations: stockLocations = [], isPending } = useStockLocations(
{
fields: locationListFields,
},
{ initialData }
)
return (
<div className="flex flex-col gap-y-3">
{before.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component />
</div>
)
})}
<Container className="flex h-fit items-center justify-between p-8">
<div>
<Heading className="mb-2">{t("location.title")}</Heading>
<Text className="text-ui-fg-subtle txt-small">
{t("location.description")}
</Text>
</div>
<Button size="small" variant="secondary" asChild>
<Link to="create">{t("location.createLocation")}</Link>
</Button>
</Container>
<div className="flex flex-col gap-3 lg:col-span-2">
{stockLocations.map((location) => (
<Location key={location.id} location={location} />
))}
</div>
{after.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component />
</div>
)
})}
<Outlet />
</div>
)
}
@@ -1,39 +1,39 @@
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import {
ColumnDef,
createColumnHelper,
RowSelectionState,
ColumnDef,
createColumnHelper,
RowSelectionState,
} from "@tanstack/react-table"
import { useForm } from "react-hook-form"
import * as zod from "zod"
import {
Alert,
Badge,
Button,
Checkbox,
Heading,
IconButton,
Text,
toast,
} from "@medusajs/ui"
import { ServiceZoneDTO, HttpTypes } from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { XMarkMini } from "@medusajs/icons"
import { HttpTypes, ServiceZoneDTO } from "@medusajs/types"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { SplitView } from "../../../../../components/layout/split-view"
import { useUpdateServiceZone } from "../../../../../hooks/api/stock-locations"
Alert,
Badge,
Button,
Checkbox,
Heading,
IconButton,
Text,
toast,
} from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { useEffect, useMemo, useState } from "react"
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
import { useCountries } from "../../../../regions/common/hooks/use-countries"
import { countries as staticCountries } from "../../../../../lib/countries"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
import { SplitView } from "../../../../../components/layout/split-view"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { DataTable } from "../../../../../components/table/data-table"
import { useUpdateServiceZone } from "../../../../../hooks/api/stock-locations"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { countries as staticCountries } from "../../../../../lib/countries"
import { useCountries } from "../../../../regions/common/hooks/use-countries"
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
const PREFIX = "ac"
const PAGE_SIZE = 50
@@ -215,7 +215,7 @@ export function EditServiceZoneAreasForm({
<SplitView.Content className="mx-auto max-w-[720px]">
<div className="container w-fit px-1 py-8">
<Heading className="mt-8 text-2xl">
{t("shipping.serviceZone.editAreasTitle", {
{t("location.serviceZone.editAreasTitle", {
zone: zone.name,
})}
</Heading>
@@ -224,10 +224,10 @@ export function EditServiceZoneAreasForm({
<div className="container flex items-center justify-between py-8 pr-1">
<div>
<Text weight="plus">
{t("shipping.serviceZone.areas.title")}
{t("location.serviceZone.areas.title")}
</Text>
<Text className="text-ui-fg-subtle mt-2">
{t("shipping.serviceZone.areas.description")}
{t("location.serviceZone.areas.description")}
</Text>
</div>
<Button
@@ -235,7 +235,7 @@ export function EditServiceZoneAreasForm({
variant="secondary"
type="button"
>
{t("shipping.serviceZone.areas.manage")}
{t("location.serviceZone.areas.manage")}
</Button>
</div>
{!!selectedCountries.length && (
@@ -268,7 +268,7 @@ export function EditServiceZoneAreasForm({
)}
{showAreasError && (
<Alert dismissible variant="error">
{t("shipping.serviceZone.areas.error")}
{t("location.serviceZone.areas.error")}
</Alert>
)}
</SplitView.Content>
@@ -1,41 +1,41 @@
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import {
ColumnDef,
createColumnHelper,
RowSelectionState,
ColumnDef,
createColumnHelper,
RowSelectionState,
} from "@tanstack/react-table"
import { useForm } from "react-hook-form"
import * as zod from "zod"
import {
Alert,
Badge,
Button,
Checkbox,
Heading,
IconButton,
Input,
Text,
toast,
} from "@medusajs/ui"
import { FulfillmentSetDTO, HttpTypes } from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { XMarkMini } from "@medusajs/icons"
import { FulfillmentSetDTO, HttpTypes } from "@medusajs/types"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
Alert,
Badge,
Button,
Checkbox,
Heading,
IconButton,
Input,
Text,
toast,
} from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { useEffect, useMemo, useState } from "react"
import { Form } from "../../../../../components/common/form"
import { SplitView } from "../../../../../components/layout/split-view"
import { useCreateServiceZone } from "../../../../../hooks/api/stock-locations"
import { useEffect, useMemo, useState } from "react"
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
import { useCountries } from "../../../../regions/common/hooks/use-countries"
import { countries as staticCountries } from "../../../../../lib/countries"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { DataTable } from "../../../../../components/table/data-table"
import { useCreateServiceZone } from "../../../../../hooks/api/stock-locations"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { countries as staticCountries } from "../../../../../lib/countries"
import { useCountries } from "../../../../regions/common/hooks/use-countries"
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
const PREFIX = "ac"
const PAGE_SIZE = 50
@@ -211,7 +211,7 @@ export function CreateServiceZoneForm({
<SplitView.Content className="mx-auto max-w-[720px]">
<div className="container w-fit px-1 py-8">
<Heading className="mb-12 mt-8 text-2xl">
{t("shipping.fulfillmentSet.create.title", {
{t("location.fulfillmentSet.create.title", {
fulfillmentSet: fulfillmentSet.name,
})}
</Heading>
@@ -224,7 +224,7 @@ export function CreateServiceZoneForm({
return (
<Form.Item>
<Form.Label>
{t("shipping.serviceZone.create.zoneName")}
{t("location.serviceZone.create.zoneName")}
</Form.Label>
<Form.Control>
<Input placeholder={t("fields.name")} {...field} />
@@ -239,10 +239,10 @@ export function CreateServiceZoneForm({
<Alert>
<Text weight="plus">
{t("shipping.serviceZone.create.subtitle")}
{t("location.serviceZone.create.subtitle")}
</Text>
<Text className="text-ui-fg-subtle mt-2">
{t("shipping.serviceZone.create.description")}
{t("location.serviceZone.create.description")}
</Text>
</Alert>
@@ -250,10 +250,10 @@ export function CreateServiceZoneForm({
<div className="container flex items-center justify-between py-8 pr-1">
<div>
<Text weight="plus">
{t("shipping.serviceZone.areas.title")}
{t("location.serviceZone.areas.title")}
</Text>
<Text className="text-ui-fg-subtle mt-2">
{t("shipping.serviceZone.areas.description")}
{t("location.serviceZone.areas.description")}
</Text>
</div>
<Button
@@ -261,7 +261,7 @@ export function CreateServiceZoneForm({
variant="secondary"
type="button"
>
{t("shipping.serviceZone.areas.manage")}
{t("location.serviceZone.areas.manage")}
</Button>
</div>
{!!selectedCountries.length && (
@@ -294,7 +294,7 @@ export function CreateServiceZoneForm({
)}
{showAreasError && (
<Alert dismissible variant="error">
{t("shipping.serviceZone.areas.error")}
{t("location.serviceZone.areas.error")}
</Alert>
)}
</SplitView.Content>
@@ -1,13 +1,13 @@
import { ServiceZoneDTO } from "@medusajs/types"
import { Alert, Button, Input, Text, toast } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import * as zod from "zod"
import { ServiceZoneDTO } from "@medusajs/types"
import { Form } from "../../../../../components/common/form"
import {
RouteDrawer,
useRouteModal,
RouteDrawer,
useRouteModal,
} from "../../../../../components/route-modal"
import { useUpdateServiceZone } from "../../../../../hooks/api/stock-locations"
@@ -88,10 +88,10 @@ export const EditServiceZoneForm = ({
</div>
<Alert>
<Text weight="plus">
{t("shipping.serviceZone.create.subtitle")}
{t("location.serviceZone.create.subtitle")}
</Text>
<Text className="text-ui-fg-subtle mt-2">
{t("shipping.serviceZone.create.description")}
{t("location.serviceZone.create.description")}
</Text>
</Alert>
</div>
@@ -3,8 +3,8 @@ import { useTranslation } from "react-i18next"
import { json, useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/route-modal"
import { EditServiceZoneForm } from "./components/edit-region-form"
import { useStockLocation } from "../../../hooks/api/stock-locations"
import { EditServiceZoneForm } from "./components/edit-region-form"
export const ServiceZoneEdit = () => {
const { t } = useTranslation()
@@ -36,7 +36,7 @@ export const ServiceZoneEdit = () => {
return (
<RouteDrawer>
<RouteDrawer.Header>
<Heading>{t("shipping.serviceZone.edit.title")}</Heading>
<Heading>{t("location.serviceZone.edit.title")}</Heading>
</RouteDrawer.Header>
{!isPending && zone && (
<EditServiceZoneForm
@@ -9,12 +9,12 @@ import {
RouteDrawer,
useRouteModal,
} from "../../../../../components/route-modal"
import { useShippingProfiles } from "../../../../../hooks/api/shipping-profiles"
import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options"
import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers"
import { isOptionEnabledInStore } from "../../../../../lib/shipping-options"
import { formatProvider } from "../../../../../lib/format-provider"
import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options"
import { useShippingProfiles } from "../../../../../hooks/api/shipping-profiles"
import { pick } from "../../../../../lib/common"
import { formatProvider } from "../../../../../lib/format-provider"
import { isOptionEnabledInStore } from "../../../../../lib/shipping-options"
enum ShippingAllocation {
FlatRate = "flat",
@@ -118,26 +118,26 @@ export const EditShippingOptionForm = ({
return (
<Form.Item>
<Form.Label>
{t("shipping.shippingOptions.create.allocation")}
{t("location.shippingOptions.create.allocation")}
</Form.Label>
<Form.Control>
<RadioGroup {...field} onValueChange={field.onChange}>
<RadioGroup.ChoiceBox
className="flex-1"
value={ShippingAllocation.FlatRate}
label={t("shipping.shippingOptions.create.fixed")}
label={t("location.shippingOptions.create.fixed")}
description={t(
"shipping.shippingOptions.create.fixedDescription"
"location.shippingOptions.create.fixedDescription"
)}
/>
<RadioGroup.ChoiceBox
className="flex-1"
value={ShippingAllocation.Calculated}
label={t(
"shipping.shippingOptions.create.calculated"
"location.shippingOptions.create.calculated"
)}
description={t(
"shipping.shippingOptions.create.calculatedDescription"
"location.shippingOptions.create.calculatedDescription"
)}
/>
</RadioGroup>
@@ -173,7 +173,7 @@ export const EditShippingOptionForm = ({
return (
<Form.Item>
<Form.Label>
{t("shipping.shippingOptions.create.profile")}
{t("location.shippingOptions.create.profile")}
</Form.Label>
<Form.Control>
<Select {...field} onValueChange={onChange}>
@@ -203,7 +203,7 @@ export const EditShippingOptionForm = ({
return (
<Form.Item>
<Form.Label>
{t("shipping.shippingOptions.edit.provider")}
{t("location.shippingOptions.edit.provider")}
</Form.Label>
<Form.Control>
<Select {...field} onValueChange={onChange}>
@@ -236,7 +236,7 @@ export const EditShippingOptionForm = ({
<Form.Item>
<div className="flex items-center justify-between">
<Form.Label>
{t("shipping.shippingOptions.create.enable")}
{t("location.shippingOptions.create.enable")}
</Form.Label>
<Form.Control>
<Switch
@@ -3,8 +3,8 @@ import { useTranslation } from "react-i18next"
import { json, useParams, useSearchParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/route-modal"
import { EditShippingOptionForm } from "./components/edit-region-form"
import { useShippingOptions } from "../../../hooks/api/shipping-options"
import { EditShippingOptionForm } from "./components/edit-region-form"
export const ShippingOptionEdit = () => {
const { t } = useTranslation()
@@ -31,7 +31,7 @@ export const ShippingOptionEdit = () => {
return (
<RouteDrawer>
<RouteDrawer.Header>
<Heading>{t("shipping.shippingOptions.edit.title")}</Heading>
<Heading>{t("location.shippingOptions.edit.title")}</Heading>
</RouteDrawer.Header>
{!isPending && shippingOption && (
<EditShippingOptionForm
@@ -1,8 +1,9 @@
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import React, { useEffect } from "react"
import { useForm } from "react-hook-form"
import * as zod from "zod"
import { ServiceZoneDTO } from "@medusajs/types"
import {
Button,
Heading,
@@ -10,26 +11,25 @@ import {
ProgressStatus,
ProgressTabs,
RadioGroup,
Select,
Switch,
Text,
clx,
Select,
} from "@medusajs/ui"
import { ServiceZoneDTO } from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { Form } from "../../../../../components/common/form"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { Form } from "../../../../../components/common/form"
import { CreateShippingOptionsPricesForm } from "./create-shipping-options-prices-form"
import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers"
import { useRegions } from "../../../../../hooks/api/regions"
import { useCreateShippingOptions } from "../../../../../hooks/api/shipping-options"
import { useShippingProfiles } from "../../../../../hooks/api/shipping-profiles"
import { getDbAmount } from "../../../../../lib/money-amount-helpers"
import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers"
import { formatProvider } from "../../../../../lib/format-provider"
import { useRegions } from "../../../../../hooks/api/regions"
import { getDbAmount } from "../../../../../lib/money-amount-helpers"
import { CreateShippingOptionsPricesForm } from "./create-shipping-options-prices-form"
enum Tab {
DETAILS = "details",
@@ -230,7 +230,7 @@ export function CreateShippingOptionsForm({
className="w-full max-w-[200px]"
>
<span className="w-full cursor-auto overflow-hidden text-ellipsis whitespace-nowrap">
{t("shipping.shippingOptions.create.details")}
{t("location.shippingOptions.create.details")}
</span>
</ProgressTabs.Trigger>
{!isCalculatedPriceType && (
@@ -241,7 +241,7 @@ export function CreateShippingOptionsForm({
disabled={!canMoveToPricing}
>
<span className="w-full overflow-hidden text-ellipsis whitespace-nowrap">
{t("shipping.shippingOptions.create.pricing")}
{t("location.shippingOptions.create.pricing")}
</span>
</ProgressTabs.Trigger>
)}
@@ -286,7 +286,7 @@ export function CreateShippingOptionsForm({
<div className="container mx-auto w-[720px] px-1 py-8">
<Heading className="mb-12 mt-8 text-2xl">
{t(
`shipping.${
`location.${
isReturn ? "returnOptions" : "shippingOptions"
}.create.title`,
{
@@ -298,10 +298,10 @@ export function CreateShippingOptionsForm({
{!isReturn && (
<div>
<Text weight="plus">
{t("shipping.shippingOptions.create.subtitle")}
{t("location.shippingOptions.create.subtitle")}
</Text>
<Text className="text-ui-fg-subtle mb-8 mt-2">
{t("shipping.shippingOptions.create.description")}
{t("location.shippingOptions.create.description")}
</Text>
</div>
)}
@@ -313,7 +313,7 @@ export function CreateShippingOptionsForm({
return (
<Form.Item>
<Form.Label>
{t("shipping.shippingOptions.create.allocation")}
{t("location.shippingOptions.create.allocation")}
</Form.Label>
<Form.Control>
<RadioGroup
@@ -324,19 +324,19 @@ export function CreateShippingOptionsForm({
<RadioGroup.ChoiceBox
className="flex-1"
value={ShippingAllocation.FlatRate}
label={t("shipping.shippingOptions.create.fixed")}
label={t("location.shippingOptions.create.fixed")}
description={t(
"shipping.shippingOptions.create.fixedDescription"
"location.shippingOptions.create.fixedDescription"
)}
/>
<RadioGroup.ChoiceBox
className="flex-1"
value={ShippingAllocation.Calculated}
label={t(
"shipping.shippingOptions.create.calculated"
"location.shippingOptions.create.calculated"
)}
description={t(
"shipping.shippingOptions.create.calculatedDescription"
"location.shippingOptions.create.calculatedDescription"
)}
/>
</RadioGroup>
@@ -374,7 +374,7 @@ export function CreateShippingOptionsForm({
return (
<Form.Item>
<Form.Label>
{t("shipping.shippingOptions.create.profile")}
{t("location.shippingOptions.create.profile")}
</Form.Label>
<Form.Control>
<Select {...field} onValueChange={onChange}>
@@ -405,7 +405,7 @@ export function CreateShippingOptionsForm({
return (
<Form.Item>
<Form.Label>
{t("shipping.shippingOptions.edit.provider")}
{t("location.shippingOptions.edit.provider")}
</Form.Label>
<Form.Control>
<Select {...field} onValueChange={onChange}>
@@ -437,7 +437,7 @@ export function CreateShippingOptionsForm({
<Form.Item>
<div className="flex items-center justify-between">
<Form.Label>
{t("shipping.shippingOptions.create.enable")}
{t("location.shippingOptions.create.enable")}
</Form.Label>
<Form.Control>
<Switch
@@ -449,7 +449,7 @@ export function CreateShippingOptionsForm({
</div>
<Form.Hint className="!mt-1">
{t(
"shipping.shippingOptions.create.enableDescription"
"location.shippingOptions.create.enableDescription"
)}
</Form.Hint>
<Form.ErrorMessage />
@@ -11,8 +11,8 @@ import {
Heading,
StatusBadge,
Text,
toast,
Tooltip,
toast,
usePrompt,
} from "@medusajs/ui"
import { format } from "date-fns"
@@ -21,10 +21,10 @@ import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { Skeleton } from "../../../../../components/common/skeleton"
import { Thumbnail } from "../../../../../components/common/thumbnail"
import { useCancelFulfillment } from "../../../../../hooks/api/fulfillment"
import { useStockLocation } from "../../../../../hooks/api/stock-locations"
import { formatProvider } from "../../../../../lib/format-provider"
import { getLocaleAmount } from "../../../../../lib/money-amount-helpers"
import { useStockLocation } from "../../../../../hooks/api/stock-locations"
import { useCancelFulfillment } from "../../../../../hooks/api/fulfillment"
type OrderFulfillmentSectionProps = {
order: OrderDTO & { fulfillments: FulfillmentDTO[] }
@@ -285,7 +285,7 @@ const Fulfillment = ({
</Text>
{stock_location ? (
<Link
to={`/settings/shipping/${stock_location.id}`}
to={`/settings/locations/${stock_location.id}`}
className="text-ui-fg-interactive hover:text-ui-fg-interactive-hover transition-fg"
>
<Text size="small" leading="compact">
@@ -6,6 +6,9 @@ import { RegionCountrySection } from "./components/region-country-section"
import { RegionGeneralSection } from "./components/region-general-section"
import { regionLoader } from "./loader"
import after from "virtual:medusa/widgets/region/details/after"
import before from "virtual:medusa/widgets/region/details/before"
export const RegionDetail = () => {
const initialData = useLoaderData() as Awaited<
ReturnType<typeof regionLoader>
@@ -36,8 +39,22 @@ export const RegionDetail = () => {
return (
<div className="flex flex-col gap-y-2">
{before.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={region} />
</div>
)
})}
<RegionGeneralSection region={region} />
<RegionCountrySection region={region} />
{after.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={region} />
</div>
)
})}
<JsonViewSection data={region} />
<Outlet />
</div>
@@ -1,10 +1,28 @@
import { Outlet } from "react-router-dom"
import { RegionListTable } from "./components/region-list-table"
import after from "virtual:medusa/widgets/region/list/after"
import before from "virtual:medusa/widgets/region/list/before"
export const RegionList = () => {
return (
<div className="flex flex-col gap-y-2">
{before.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component />
</div>
)
})}
<RegionListTable />
{after.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component />
</div>
)
})}
<Outlet />
</div>
)
@@ -6,8 +6,8 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form"
import {
RouteFocusModal,
useRouteModal,
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { useCreateShippingProfile } from "../../../../../hooks/api/shipping-profiles"
@@ -1,4 +1,5 @@
import { useParams } from "react-router-dom"
import { JsonViewSection } from "../../../components/common/json-view-section"
import {
GeneralSectionSkeleton,
@@ -7,6 +8,9 @@ import {
import { useShippingProfile } from "../../../hooks/api/shipping-profiles"
import { ShippingProfileGeneralSection } from "./components/shipping-profile-general-section"
import after from "virtual:medusa/widgets/shipping_profile/details/after"
import before from "virtual:medusa/widgets/shipping_profile/details/before"
export const ShippingProfileDetail = () => {
const { id } = useParams()
@@ -29,7 +33,21 @@ export const ShippingProfileDetail = () => {
return (
<div className="flex flex-col gap-y-2">
{before.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={shipping_profile} />
</div>
)
})}
<ShippingProfileGeneralSection profile={shipping_profile} />
{after.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component data={shipping_profile} />
</div>
)
})}
<JsonViewSection data={shipping_profile} />
</div>
)
@@ -1,10 +1,28 @@
import { Outlet } from "react-router-dom"
import { ShippingProfileListTable } from "./components/shipping-profile-list-table"
import after from "virtual:medusa/widgets/shipping_profile/list/after"
import before from "virtual:medusa/widgets/shipping_profile/list/before"
export const ShippingProfileList = () => {
return (
<div className="flex flex-col gap-y-2">
{before.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component />
</div>
)
})}
<ShippingProfileListTable />
{after.widgets.map((w, i) => {
return (
<div key={i}>
<w.Component />
</div>
)
})}
<Outlet />
</div>
)
@@ -1,55 +0,0 @@
import { Outlet, useLoaderData, useParams } from "react-router-dom"
import { JsonViewSection } from "../../../components/common/json-view-section"
import { LocationGeneralSection } from "./components/location-general-section"
import { useStockLocation } from "../../../hooks/api/stock-locations"
import LocationsSalesChannelsSection from "./components/location-sales-channels-section/locations-sales-channels-section"
import { locationLoader } from "./loader"
export const LocationDetails = () => {
const initialData = useLoaderData() as Awaited<
ReturnType<typeof locationLoader>
>
const { location_id } = useParams()
const {
stock_location: location,
isPending: isLoading,
isError,
error,
} = useStockLocation(
location_id!,
{
fields:
"name,*sales_channels,address.city,address.country_code,fulfillment_sets.type,fulfillment_sets.name,*fulfillment_sets.service_zones.geo_zones,*fulfillment_sets.service_zones,*fulfillment_sets.service_zones.shipping_options,*fulfillment_sets.service_zones.shipping_options.rules,*fulfillment_sets.service_zones.shipping_options.shipping_profile",
},
{
initialData,
}
)
// TODO: Move to loading.tsx and set as Suspense fallback for the route
if (isLoading || !location) {
return <div>Loading...</div>
}
if (isError) {
throw error
}
return (
<div className="flex flex-col gap-x-4 xl:flex-row xl:items-start">
<div className="flex w-full flex-col gap-y-2">
<LocationGeneralSection location={location} />
<div className="flex w-full flex-col gap-y-2 xl:hidden">
<LocationsSalesChannelsSection location={location} />
</div>
<JsonViewSection data={location} />
</div>
<div className="hidden w-full max-w-[400px] flex-col gap-y-2 xl:flex">
<LocationsSalesChannelsSection location={location} />
</div>
<Outlet />
</div>
)
}
@@ -1,47 +0,0 @@
import { Button, Container, Heading, Text } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { Link, Outlet, useLoaderData } from "react-router-dom"
import { shippingListLoader } from "./loader"
import { useStockLocations } from "../../../hooks/api/stock-locations"
import Location from "./components/location/location"
import { locationListFields } from "./const"
export function LocationList() {
const { t } = useTranslation()
const initialData = useLoaderData() as Awaited<
ReturnType<typeof shippingListLoader>
>
let { stock_locations: stockLocations = [], isPending } = useStockLocations(
{
fields: locationListFields,
},
{ initialData }
)
return (
<>
<div className="py-4">
<Container className="mb-4 flex h-fit items-center justify-between p-8">
<div>
<Heading className="mb-2">{t("shipping.title")}</Heading>
<Text className="text-ui-fg-subtle txt-small">
{t("shipping.description")}
</Text>
</div>
<Button size="small" variant="secondary" asChild>
<Link to="create">{t("shipping.createLocation")}</Link>
</Button>
</Container>
<div className="flex flex-col gap-4 lg:col-span-2">
{stockLocations.map((location) => (
<Location key={location.id} location={location} />
))}
</div>
</div>
<Outlet />
</>
)
}