feat(dashboard): move shipping profile to locations (#7777)
This commit is contained in:
1 parent
48963f55ef
commit
79a8f0ef2c
10 files changed
+189
-62
No files matched your search
-4
@@ -53,10 +53,6 @@ const useSettingRoutes = (): NavItemProps[] => {
|
|||||||
label: t("productTypes.domain"),
|
label: t("productTypes.domain"),
|
||||||
to: "/settings/product-types",
|
to: "/settings/product-types",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: t("shippingProfile.domain"),
|
|
||||||
to: "/settings/shipping-profiles",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t("stockLocations.domain"),
|
label: t("stockLocations.domain"),
|
||||||
to: "/settings/locations",
|
to: "/settings/locations",
|
||||||
|
|||||||
@@ -804,6 +804,14 @@
|
|||||||
"shipping": "Shipping was successfully enabled."
|
"shipping": "Shipping was successfully enabled."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sidebar": {
|
||||||
|
"shippingConfiguration": "Shipping configuration",
|
||||||
|
"shippingProfiles": "Shipping profiles",
|
||||||
|
"shippingProfilesDesc": "Shipping rules for different types of products",
|
||||||
|
"shippingOptionTypes": "Shipping Option Types",
|
||||||
|
"shippingOptionTypesDesc": "Group options based on characteristic"
|
||||||
|
|
||||||
|
},
|
||||||
"salesChannels": {
|
"salesChannels": {
|
||||||
"header": "Sales Channels",
|
"header": "Sales Channels",
|
||||||
"label": "Connected sales channels",
|
"label": "Connected sales channels",
|
||||||
|
|||||||
@@ -747,6 +747,48 @@ export const RouteMap: RouteObject[] = [
|
|||||||
path: "create",
|
path: "create",
|
||||||
lazy: () => import("../../routes/locations/location-create"),
|
lazy: () => import("../../routes/locations/location-create"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "shipping-profiles",
|
||||||
|
element: <Outlet />,
|
||||||
|
handle: {
|
||||||
|
crumb: () => "Shipping Profiles",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/shipping-profiles/shipping-profiles-list"
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "create",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/shipping-profiles/shipping-profile-create"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":id",
|
||||||
|
handle: {
|
||||||
|
crumb: (data) => data.shipping_profile.name,
|
||||||
|
},
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/shipping-profiles/shipping-profile-detail"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "shipping-option-types",
|
||||||
|
element: <Outlet />,
|
||||||
|
handle: {
|
||||||
|
crumb: () => "Shipping Option Types",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: ":location_id",
|
path: ":location_id",
|
||||||
lazy: () => import("../../routes/locations/location-detail"),
|
lazy: () => import("../../routes/locations/location-detail"),
|
||||||
@@ -900,38 +942,7 @@ export const RouteMap: RouteObject[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "shipping-profiles",
|
|
||||||
element: <Outlet />,
|
|
||||||
handle: {
|
|
||||||
crumb: () => "Shipping Profiles",
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
lazy: () =>
|
|
||||||
import(
|
|
||||||
"../../routes/shipping-profiles/shipping-profiles-list"
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "create",
|
|
||||||
lazy: () =>
|
|
||||||
import(
|
|
||||||
"../../routes/shipping-profiles/shipping-profile-create"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ":id",
|
|
||||||
lazy: () =>
|
|
||||||
import(
|
|
||||||
"../../routes/shipping-profiles/shipping-profile-detail"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "publishable-api-keys",
|
path: "publishable-api-keys",
|
||||||
element: <Outlet />,
|
element: <Outlet />,
|
||||||
|
|||||||
+5
-1
@@ -48,7 +48,11 @@ export const InventoryItemVariantsSection = ({
|
|||||||
size="2xsmall"
|
size="2xsmall"
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => navigate(`/products/${variant.product.id}`)} // TODO: navigate to variant details when implemented
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
`/products/${variant.product.id}/variants/${variant.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<ArrowUpRightOnBox className="text-ui-fg-muted" />
|
<ArrowUpRightOnBox className="text-ui-fg-muted" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
+100
-20
@@ -1,4 +1,7 @@
|
|||||||
import { Outlet, useLoaderData } from "react-router-dom"
|
import { Outlet, useLoaderData, useNavigate } from "react-router-dom"
|
||||||
|
import { Container, Heading, IconButton } from "@medusajs/ui"
|
||||||
|
import { ArrowUpRightOnBox, Buildings, ShoppingBag } from "@medusajs/icons"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { useStockLocations } from "../../../hooks/api/stock-locations"
|
import { useStockLocations } from "../../../hooks/api/stock-locations"
|
||||||
import LocationListItem from "./components/location-list-item/location-list-item"
|
import LocationListItem from "./components/location-list-item/location-list-item"
|
||||||
@@ -30,28 +33,105 @@ export function LocationList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-3">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => {
|
<div className="flex flex-col gap-x-4 gap-y-3 xl:flex-row xl:items-start">
|
||||||
return (
|
<div className="flex w-full flex-col gap-y-3">
|
||||||
<div key={i}>
|
{before.widgets.map((w, i) => {
|
||||||
<w.Component />
|
return (
|
||||||
|
<div key={i}>
|
||||||
|
<w.Component />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<LocationListHeader />
|
||||||
|
<div className="flex flex-col gap-3 lg:col-span-2">
|
||||||
|
{stockLocations.map((location) => (
|
||||||
|
<LocationListItem key={location.id} location={location} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
{after.widgets.map((w, i) => {
|
||||||
})}
|
return (
|
||||||
<LocationListHeader />
|
<div key={i}>
|
||||||
<div className="flex flex-col gap-3 lg:col-span-2">
|
<w.Component />
|
||||||
{stockLocations.map((location) => (
|
</div>
|
||||||
<LocationListItem key={location.id} location={location} />
|
)
|
||||||
))}
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex w-full max-w-[100%] flex-col gap-y-3 xl:mt-0 xl:max-w-[400px]">
|
||||||
|
<LinksSection />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{after.widgets.map((w, i) => {
|
|
||||||
return (
|
|
||||||
<div key={i}>
|
|
||||||
<w.Component />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LinksSection = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className="p-0">
|
||||||
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
|
<Heading level="h2">
|
||||||
|
{t("stockLocations.sidebar.shippingConfiguration")}
|
||||||
|
</Heading>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="txt-small flex flex-col gap-2 px-2 pb-2">
|
||||||
|
<div className="shadow-elevation-card-rest bg-ui-bg-component rounded-md px-4 py-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="shadow-elevation-card-rest rounded-md">
|
||||||
|
<ShoppingBag />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-1 flex-col">
|
||||||
|
<span className="text-ui-fg-base font-medium">
|
||||||
|
{t("stockLocations.sidebar.shippingProfiles")}
|
||||||
|
</span>
|
||||||
|
<span className="text-ui-fg-subtle">
|
||||||
|
{t("stockLocations.sidebar.shippingProfilesDesc")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<IconButton
|
||||||
|
size="2xsmall"
|
||||||
|
variant="transparent"
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigate(`/settings/locations/shipping-profiles`)}
|
||||||
|
>
|
||||||
|
<ArrowUpRightOnBox className="text-ui-fg-muted" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="txt-small flex flex-col gap-2 px-2 pb-2">
|
||||||
|
<div className="shadow-elevation-card-rest bg-ui-bg-component rounded-md px-4 py-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="shadow-elevation-card-rest rounded-md">
|
||||||
|
<Buildings />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-1 flex-col">
|
||||||
|
<span className="text-ui-fg-base font-medium">
|
||||||
|
{t("stockLocations.sidebar.shippingOptionTypes")}
|
||||||
|
</span>
|
||||||
|
<span className="text-ui-fg-subtle">
|
||||||
|
{t("stockLocations.sidebar.shippingOptionTypesDesc")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<IconButton
|
||||||
|
size="2xsmall"
|
||||||
|
variant="transparent"
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(`/settings/locations/shipping-option-types`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ArrowUpRightOnBox className="text-ui-fg-muted" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
+4
-1
@@ -51,7 +51,10 @@ export function VariantPricesSection({ variant }: VariantPricesSectionProps) {
|
|||||||
{!hasPrices && <NoRecords className="h-60" />}
|
{!hasPrices && <NoRecords className="h-60" />}
|
||||||
{displayPrices.map((price) => {
|
{displayPrices.map((price) => {
|
||||||
return (
|
return (
|
||||||
<div className="txt-small text-ui-fg-subtle flex justify-between px-6 py-4">
|
<div
|
||||||
|
key={price.id}
|
||||||
|
className="txt-small text-ui-fg-subtle flex justify-between px-6 py-4"
|
||||||
|
>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{price.currency_code.toUpperCase()}
|
{price.currency_code.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
+5
-3
@@ -6,8 +6,8 @@ import * as zod from "zod"
|
|||||||
|
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
import { useCreateShippingProfile } from "../../../../../hooks/api/shipping-profiles"
|
import { useCreateShippingProfile } from "../../../../../hooks/api/shipping-profiles"
|
||||||
|
|
||||||
@@ -45,7 +45,9 @@ export function CreateShippingProfileForm() {
|
|||||||
dismissLabel: t("actions.close"),
|
dismissLabel: t("actions.close"),
|
||||||
})
|
})
|
||||||
|
|
||||||
handleSuccess(`/settings/shipping-profiles/${shipping_profile.id}`)
|
handleSuccess(
|
||||||
|
`/settings/locations/shipping-profiles/${shipping_profile.id}`
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast.error(t("general.error"), {
|
toast.error(t("general.error"), {
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ export const ShippingProfileGeneralSection = ({
|
|||||||
dismissLabel: t("actions.close"),
|
dismissLabel: t("actions.close"),
|
||||||
})
|
})
|
||||||
|
|
||||||
navigate("/settings/shipping-profiles", { replace: true })
|
navigate("/settings/locations/shipping-profiles", { replace: true })
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast.error(t("general.error"), {
|
toast.error(t("general.error"), {
|
||||||
|
|||||||
+1
@@ -1 +1,2 @@
|
|||||||
|
export { shippingProfileLoader as loader } from "./loader"
|
||||||
export { ShippingProfileDetail as Component } from "./shipping-profile-detail"
|
export { ShippingProfileDetail as Component } from "./shipping-profile-detail"
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { LoaderFunctionArgs } from "react-router-dom"
|
||||||
|
|
||||||
|
import { sdk } from "../../../lib/client"
|
||||||
|
import { queryClient } from "../../../lib/query-client"
|
||||||
|
import { shippingProfileQueryKeys } from "../../../hooks/api/shipping-profiles"
|
||||||
|
|
||||||
|
const shippingProfileQuery = (id: string) => ({
|
||||||
|
queryKey: shippingProfileQueryKeys.detail(id),
|
||||||
|
queryFn: async () => sdk.admin.shippingProfile.retrieve(id),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const shippingProfileLoader = async ({ params }: LoaderFunctionArgs) => {
|
||||||
|
const id = params.id
|
||||||
|
const query = shippingProfileQuery(id!)
|
||||||
|
|
||||||
|
return (
|
||||||
|
queryClient.getQueryData<{
|
||||||
|
shipping_profile: HttpTypes.AdminShippingProfile
|
||||||
|
}>(query.queryKey) ?? (await queryClient.fetchQuery(query))
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user