From 493d242c1259cdc68ecdce5f0f9d7885cbd67094 Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:13:03 +0100 Subject: [PATCH] fix(dashboard): Add Breadcrumb components (#10079) **What** - Adds Breadcrumb component to all routes that needs breadcrumbs. - The Breadcrumb components use a combination of loader data and useQuery to ensure that the displayed value is kept up to date if the underlying data is changed via a mutation. - Also fixes a couple of places where the breadcrumb was not setup correctly. Resolves CMRC-688 --- .changeset/big-horses-sneeze.md | 5 + .../src/components/layout/shell/shell.tsx | 14 +- .../dashboard/src/hooks/api/products.tsx | 17 +- .../src/hooks/api/shipping-profiles.tsx | 13 +- .../dashboard/src/providers/providers.tsx | 1 - .../providers/router-provider/route-map.tsx | 538 ++++++++++++------ .../api-key-management-detail/breadcrumb.tsx | 24 + .../api-key-management-detail/index.ts | 1 + .../api-key-management-detail/loader.ts | 6 +- .../campaigns/campaign-detail/breadcrumb.tsx | 29 + .../campaign-detail/campaign-detail.tsx | 3 +- .../campaigns/campaign-detail/constants.ts | 1 + .../routes/campaigns/campaign-detail/index.ts | 1 + .../campaigns/campaign-detail/loader.ts | 9 +- .../categories/category-detail/breadcrumb.tsx | 29 + .../categories/category-detail/index.ts | 1 + .../categories/category-detail/loader.ts | 6 +- .../collection-detail/breadcrumb.tsx | 23 + .../collections/collection-detail/index.ts | 1 + .../collections/collection-detail/loader.ts | 7 +- .../customer-group-detail/breadcrumb.tsx | 31 + .../customer-group-detail/constants.ts | 1 + .../customer-group-detail.tsx | 3 +- .../customer-group-detail/index.ts | 1 + .../customer-group-detail/loader.ts | 10 +- .../customers/customer-detail/breadcrumb.tsx | 29 + .../routes/customers/customer-detail/index.ts | 1 + .../customers/customer-detail/loader.ts | 7 +- .../inventory/inventory-detail/breadcrumb.tsx | 31 + .../inventory/inventory-detail/constants.ts | 2 + .../inventory/inventory-detail/index.ts | 1 + .../inventory-detail/inventory-detail.tsx | 3 +- .../inventory/inventory-detail/loader.ts | 10 +- .../locations/location-detail/breadcrumb.tsx | 31 + .../{const.ts => constants.ts} | 2 +- .../routes/locations/location-detail/index.ts | 1 + .../locations/location-detail/loader.ts | 31 +- .../location-detail/location-detail.tsx | 8 +- .../routes/orders/order-detail/breadcrumb.tsx | 27 + .../src/routes/orders/order-detail/index.ts | 1 + .../src/routes/orders/order-detail/loader.ts | 6 +- .../price-list-detail/breadcrumb.tsx | 23 + .../price-lists/price-list-detail/index.ts | 1 + .../price-lists/price-list-detail/loader.ts | 7 +- .../product-tag-detail/breadcrumb.tsx | 23 + .../product-tags/product-tag-detail/index.ts | 1 + .../product-tags/product-tag-detail/loader.ts | 5 +- .../product-type-detail/breadcrumb.tsx | 24 + .../product-type-detail/index.ts | 1 + .../product-type-detail/loader.ts | 7 +- .../product-variant-detail/breadcrumb.tsx | 31 + .../product-variant-detail/index.ts | 1 + .../product-variant-detail/loader.ts | 5 +- .../products/product-detail/breadcrumb.tsx | 29 + .../routes/products/product-detail/index.ts | 1 + .../routes/products/product-detail/loader.ts | 12 +- .../promotion-detail/breadcrumb.tsx | 22 + .../promotions/promotion-detail/index.ts | 1 + .../promotions/promotion-detail/loader.ts | 7 +- .../regions/region-detail/breadcrumb.tsx | 27 + .../routes/regions/region-detail/constants.ts | 2 + .../src/routes/regions/region-detail/index.ts | 1 + .../routes/regions/region-detail/loader.ts | 3 +- .../regions/region-detail/region-detail.tsx | 3 +- .../reservation-detail/breadcrumb.tsx | 29 + .../reservations/reservation-detail/index.ts | 1 + .../reservations/reservation-detail/loader.ts | 7 +- .../sales-channel-detail/breadcrumb.tsx | 23 + .../sales-channel-detail/index.ts | 1 + .../sales-channel-detail/loader.ts | 6 +- .../shipping-profile-detail/breadcrumb.tsx | 27 + .../shipping-profile-detail/index.ts | 1 + .../shipping-profile-detail/loader.ts | 11 +- .../shipping-profile-detail.tsx | 13 +- .../tax-region-detail/breadcrumb.tsx | 29 + .../tax-regions/tax-region-detail/index.ts | 1 + .../tax-regions/tax-region-detail/loader.ts | 6 +- .../tax-region-province-detail/breadcrumb.tsx | 32 ++ .../tax-region-province-detail/index.ts | 1 + .../tax-region-province-detail/loader.ts | 6 +- .../routes/users/user-detail/breadcrumb.tsx | 24 + .../src/routes/users/user-detail/index.ts | 1 + .../src/routes/users/user-detail/loader.ts | 6 +- .../workflow-execution-detail/breadcrumb.tsx | 26 + .../workflow-execution-detail/index.ts | 2 + .../workflow-execution-detail/loader.ts | 11 +- 86 files changed, 1123 insertions(+), 344 deletions(-) create mode 100644 .changeset/big-horses-sneeze.md create mode 100644 packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/campaigns/campaign-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/campaigns/campaign-detail/constants.ts create mode 100644 packages/admin/dashboard/src/routes/categories/category-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/collections/collection-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/constants.ts create mode 100644 packages/admin/dashboard/src/routes/customers/customer-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/inventory/inventory-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/inventory/inventory-detail/constants.ts create mode 100644 packages/admin/dashboard/src/routes/locations/location-detail/breadcrumb.tsx rename packages/admin/dashboard/src/routes/locations/location-detail/{const.ts => constants.ts} (90%) create mode 100644 packages/admin/dashboard/src/routes/orders/order-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/price-lists/price-list-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/product-tags/product-tag-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/product-types/product-type-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/product-variants/product-variant-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/products/product-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/promotions/promotion-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/regions/region-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/regions/region-detail/constants.ts create mode 100644 packages/admin/dashboard/src/routes/reservations/reservation-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/users/user-detail/breadcrumb.tsx create mode 100644 packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx diff --git a/.changeset/big-horses-sneeze.md b/.changeset/big-horses-sneeze.md new file mode 100644 index 0000000000000..e294a5c0dd03e --- /dev/null +++ b/.changeset/big-horses-sneeze.md @@ -0,0 +1,5 @@ +--- +"@medusajs/dashboard": patch +--- + +fix(dashboard): Ensure Breadcrumbs don't display stale data diff --git a/packages/admin/dashboard/src/components/layout/shell/shell.tsx b/packages/admin/dashboard/src/components/layout/shell/shell.tsx index 727a790a7b710..26c7603cb570c 100644 --- a/packages/admin/dashboard/src/components/layout/shell/shell.tsx +++ b/packages/admin/dashboard/src/components/layout/shell/shell.tsx @@ -2,7 +2,7 @@ import * as Dialog from "@radix-ui/react-dialog" import { SidebarLeft, TriangleRightMini, XMark } from "@medusajs/icons" import { IconButton, clx } from "@medusajs/ui" -import { PropsWithChildren } from "react" +import { PropsWithChildren, ReactNode } from "react" import { useTranslation } from "react-i18next" import { Link, Outlet, UIMatch, useMatches } from "react-router-dom" @@ -45,18 +45,20 @@ const Gutter = ({ children }: PropsWithChildren) => { const Breadcrumbs = () => { const matches = useMatches() as unknown as UIMatch< unknown, - { crumb?: (data?: unknown) => string } + { + breadcrumb?: (match?: UIMatch) => string | ReactNode + } >[] const crumbs = matches - .filter((match) => Boolean(match.handle?.crumb)) + .filter((match) => match.handle?.breadcrumb) .map((match) => { const handle = match.handle - let label: string | null = null + let label: string | ReactNode | undefined = undefined try { - label = handle.crumb!(match.data) + label = handle.breadcrumb?.(match) } catch (error) { // noop } @@ -70,7 +72,7 @@ const Breadcrumbs = () => { path: match.pathname, } }) - .filter(Boolean) as { label: string; path: string }[] + .filter(Boolean) as { label: string | ReactNode; path: string }[] return (
    , options?: Omit< - UseQueryOptions, + UseQueryOptions< + HttpTypes.AdminProductResponse, + FetchError, + HttpTypes.AdminProductResponse, + QueryKey + >, "queryFn" | "queryKey" > ) => { @@ -302,9 +307,13 @@ export const useUpdateProduct = ( ) => { return useMutation({ mutationFn: (payload) => sdk.admin.product.update(id, payload), - onSuccess: (data, variables, context) => { - queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() }) - queryClient.invalidateQueries({ queryKey: productsQueryKeys.detail(id) }) + onSuccess: async (data, variables, context) => { + await queryClient.invalidateQueries({ + queryKey: productsQueryKeys.lists(), + }) + await queryClient.invalidateQueries({ + queryKey: productsQueryKeys.detail(id), + }) options?.onSuccess?.(data, variables, context) }, diff --git a/packages/admin/dashboard/src/hooks/api/shipping-profiles.tsx b/packages/admin/dashboard/src/hooks/api/shipping-profiles.tsx index 07aa0d860ca67..c7834e37e20f4 100644 --- a/packages/admin/dashboard/src/hooks/api/shipping-profiles.tsx +++ b/packages/admin/dashboard/src/hooks/api/shipping-profiles.tsx @@ -40,11 +40,14 @@ export const useCreateShippingProfile = ( export const useShippingProfile = ( id: string, query?: Record, - options?: UseQueryOptions< - HttpTypes.AdminShippingProfileResponse, - FetchError, - HttpTypes.AdminShippingProfileResponse, - QueryKey + options?: Omit< + UseQueryOptions< + HttpTypes.AdminShippingProfileResponse, + FetchError, + HttpTypes.AdminShippingProfileResponse, + QueryKey + >, + "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ diff --git a/packages/admin/dashboard/src/providers/providers.tsx b/packages/admin/dashboard/src/providers/providers.tsx index 588215d637b5b..c52ad0ad97f40 100644 --- a/packages/admin/dashboard/src/providers/providers.tsx +++ b/packages/admin/dashboard/src/providers/providers.tsx @@ -2,7 +2,6 @@ import { Toaster, TooltipProvider } from "@medusajs/ui" import { QueryClientProvider } from "@tanstack/react-query" import type { PropsWithChildren } from "react" import { HelmetProvider } from "react-helmet-async" - import { I18n } from "../components/utilities/i18n" import { DashboardExtensionManager, diff --git a/packages/admin/dashboard/src/providers/router-provider/route-map.tsx b/packages/admin/dashboard/src/providers/router-provider/route-map.tsx index 33449aa70e5d6..5ad5068a60822 100644 --- a/packages/admin/dashboard/src/providers/router-provider/route-map.tsx +++ b/packages/admin/dashboard/src/providers/router-provider/route-map.tsx @@ -1,5 +1,5 @@ -import { AdminProductCategoryResponse, HttpTypes } from "@medusajs/types" -import { Outlet, RouteObject } from "react-router-dom" +import { HttpTypes } from "@medusajs/types" +import { Outlet, RouteObject, UIMatch } from "react-router-dom" import { t } from "i18next" import { ProtectedRoute } from "../../components/authentication/protected-route" @@ -7,12 +7,7 @@ import { MainLayout } from "../../components/layout/main-layout" import { PublicLayout } from "../../components/layout/public-layout" import { SettingsLayout } from "../../components/layout/settings-layout" import { ErrorBoundary } from "../../components/utilities/error-boundary" -import { getCountryByIso2 } from "../../lib/data/countries" -import { - getProvinceByIso2, - isProvinceInCountry, -} from "../../lib/data/country-states" -import { productLoader } from "../../routes/products/product-detail/loader" +import { TaxRegionDetailBreadcrumb } from "../../routes/tax-regions/tax-region-detail/breadcrumb" import { taxRegionLoader } from "../../routes/tax-regions/tax-region-detail/loader" import { RouteExtensions } from "./route-extensions" import { SettingsExtensions } from "./settings-extensions" @@ -34,7 +29,7 @@ export const RouteMap: RouteObject[] = [ path: "/products", errorElement: , handle: { - crumb: () => t("products.domain"), + breadcrumb: () => t("products.domain"), }, children: [ { @@ -58,11 +53,20 @@ export const RouteMap: RouteObject[] = [ { path: ":id", errorElement: , - Component: Outlet, - loader: productLoader, - handle: { - crumb: (data: HttpTypes.AdminProductResponse) => - data.product.title, + lazy: async () => { + const { Breadcrumb, loader } = await import( + "../../routes/products/product-detail" + ) + + return { + Component: Outlet, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -134,13 +138,21 @@ export const RouteMap: RouteObject[] = [ }, { path: "variants/:variant_id", - lazy: () => - import( + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( "../../routes/product-variants/product-variant-detail" - ), - handle: { - crumb: (data: HttpTypes.AdminProductVariantResponse) => - data.variant.title, + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + // eslint-disable-next-line max-len + match: UIMatch + ) => , + }, + } }, children: [ { @@ -172,7 +184,7 @@ export const RouteMap: RouteObject[] = [ path: "/categories", errorElement: , handle: { - crumb: () => t("categories.domain"), + breadcrumb: () => t("categories.domain"), }, children: [ { @@ -193,10 +205,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/categories/category-detail"), - handle: { - crumb: (data: AdminProductCategoryResponse) => - data.product_category.name, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/categories/category-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -226,7 +248,7 @@ export const RouteMap: RouteObject[] = [ path: "/orders", errorElement: , handle: { - crumb: () => t("orders.domain"), + breadcrumb: () => t("orders.domain"), }, children: [ { @@ -235,7 +257,21 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/orders/order-detail"), + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/orders/order-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } + }, children: [ { path: "fulfillment", @@ -289,7 +325,7 @@ export const RouteMap: RouteObject[] = [ path: "/promotions", errorElement: , handle: { - crumb: () => t("promotions.domain"), + breadcrumb: () => t("promotions.domain"), }, children: [ { @@ -302,10 +338,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/promotions/promotion-detail"), - handle: { - // TODO: Re-add type when it's available again - crumb: (data: any) => data.promotion?.code, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/promotions/promotion-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -330,7 +376,9 @@ export const RouteMap: RouteObject[] = [ { path: "/campaigns", errorElement: , - handle: { crumb: () => t("campaigns.domain") }, + handle: { + breadcrumb: () => t("campaigns.domain"), + }, children: [ { path: "", @@ -343,8 +391,21 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/campaigns/campaign-detail"), - handle: { crumb: (data: any) => data.campaign.name }, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/campaigns/campaign-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } + }, children: [ { path: "edit", @@ -373,7 +434,7 @@ export const RouteMap: RouteObject[] = [ path: "/collections", errorElement: , handle: { - crumb: () => t("collections.domain"), + breadcrumb: () => t("collections.domain"), }, children: [ { @@ -389,11 +450,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/collections/collection-detail"), - handle: { - crumb: (data: { collection: HttpTypes.AdminCollection }) => - data.collection.title, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/collections/collection-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -416,7 +486,7 @@ export const RouteMap: RouteObject[] = [ path: "/price-lists", errorElement: , handle: { - crumb: () => t("priceLists.domain"), + breadcrumb: () => t("priceLists.domain"), }, children: [ { @@ -432,11 +502,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/price-lists/price-list-detail"), - handle: { - crumb: (data: HttpTypes.AdminPriceListResponse) => - data.price_list.title, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/price-lists/price-list-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -469,7 +548,7 @@ export const RouteMap: RouteObject[] = [ path: "/customers", errorElement: , handle: { - crumb: () => t("customers.domain"), + breadcrumb: () => t("customers.domain"), }, children: [ { @@ -485,10 +564,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/customers/customer-detail"), - handle: { - // Re-add type when it's available again - crumb: (data: any) => data.customer.email, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/customers/customer-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -515,7 +604,7 @@ export const RouteMap: RouteObject[] = [ path: "/customer-groups", errorElement: , handle: { - crumb: () => t("customerGroups.domain"), + breadcrumb: () => t("customerGroups.domain"), }, children: [ { @@ -534,12 +623,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/customer-groups/customer-group-detail"), - handle: { - crumb: (data: { - customer_group: HttpTypes.AdminCustomerGroup - }) => data.customer_group.name, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/customer-groups/customer-group-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -571,7 +668,7 @@ export const RouteMap: RouteObject[] = [ path: "/reservations", errorElement: , handle: { - crumb: () => t("reservations.domain"), + breadcrumb: () => t("reservations.domain"), }, children: [ { @@ -588,16 +685,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/reservations/reservation-detail"), - handle: { - crumb: ({ reservation }: any) => { - return ( - reservation?.inventory_item?.title ?? - reservation?.inventory_item?.sku ?? - reservation?.id - ) - }, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/reservations/reservation-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -620,7 +721,7 @@ export const RouteMap: RouteObject[] = [ path: "/inventory", errorElement: , handle: { - crumb: () => t("inventory.domain"), + breadcrumb: () => t("inventory.domain"), }, children: [ { @@ -636,10 +737,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/inventory/inventory-detail"), - handle: { - crumb: (data: HttpTypes.AdminInventoryItemResponse) => - data.inventory_item.title ?? data.inventory_item.sku, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/inventory/inventory-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -691,7 +802,7 @@ export const RouteMap: RouteObject[] = [ { path: "/settings", handle: { - crumb: () => t("app.nav.settings.header"), + breadcrumb: () => t("app.nav.settings.header"), }, element: , children: [ @@ -705,7 +816,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , lazy: () => import("../../routes/profile/profile-detail"), handle: { - crumb: () => t("profile.domain"), + breadcrumb: () => t("profile.domain"), }, children: [ { @@ -719,7 +830,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("regions.domain"), + breadcrumb: () => t("regions.domain"), }, children: [ { @@ -734,10 +845,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/regions/region-detail"), - handle: { - crumb: (data: { region: HttpTypes.AdminRegion }) => - data.region.name, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/regions/region-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -758,7 +879,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , lazy: () => import("../../routes/store/store-detail"), handle: { - crumb: () => t("store.domain"), + breadcrumb: () => t("store.domain"), }, children: [ { @@ -780,7 +901,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("users.domain"), + breadcrumb: () => t("users.domain"), }, children: [ { @@ -795,9 +916,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => import("../../routes/users/user-detail"), - handle: { - crumb: (data: HttpTypes.AdminUserResponse) => data.user.email, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/users/user-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -817,7 +949,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("salesChannels.domain"), + breadcrumb: () => t("salesChannels.domain"), }, children: [ { @@ -836,11 +968,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/sales-channels/sales-channel-detail"), - handle: { - crumb: (data: HttpTypes.AdminSalesChannelResponse) => - data.sales_channel.name, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/sales-channels/sales-channel-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -871,7 +1012,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("locations.domain"), + breadcrumb: () => t("locations.domain"), }, children: [ { @@ -886,7 +1027,7 @@ export const RouteMap: RouteObject[] = [ path: "shipping-profiles", element: , handle: { - crumb: () => t("shippingProfile.domain"), + breadcrumb: () => t("shippingProfile.domain"), }, children: [ { @@ -906,24 +1047,42 @@ export const RouteMap: RouteObject[] = [ ], }, { - path: ":id", - handle: { - crumb: (data: HttpTypes.AdminShippingProfileResponse) => - data.shipping_profile.name, - }, - lazy: () => - import( + path: ":shipping_profile_id", + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( "../../routes/shipping-profiles/shipping-profile-detail" - ), + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + // eslint-disable-next-line max-len + match: UIMatch + ) => , + }, + } + }, }, ], }, { path: ":location_id", - lazy: () => import("../../routes/locations/location-detail"), - handle: { - crumb: (data: HttpTypes.AdminStockLocationResponse) => - data.stock_location.name, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/locations/location-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -1013,7 +1172,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("productTags.domain"), + breadcrumb: () => t("productTags.domain"), }, children: [ { @@ -1030,11 +1189,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/product-tags/product-tag-detail"), - handle: { - crumb: (data: HttpTypes.AdminProductTagResponse) => - data.product_tag.value, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/product-tags/product-tag-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -1051,7 +1219,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("workflowExecutions.domain"), + breadcrumb: () => t("workflowExecutions.domain"), }, children: [ { @@ -1063,18 +1231,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import( + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( "../../routes/workflow-executions/workflow-execution-detail" - ), - handle: { - crumb: (data: { workflow: any }) => { - if (!data) { - return "" - } + ) - return data.workflow.name - }, + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, }, ], @@ -1084,7 +1254,7 @@ export const RouteMap: RouteObject[] = [ errorElement: , element: , handle: { - crumb: () => t("productTypes.domain"), + breadcrumb: () => t("productTypes.domain"), }, children: [ { @@ -1101,11 +1271,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import("../../routes/product-types/product-type-detail"), - handle: { - crumb: (data: HttpTypes.AdminProductTypeResponse) => - data.product_type.value, + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( + "../../routes/product-types/product-type-detail" + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -1121,7 +1300,7 @@ export const RouteMap: RouteObject[] = [ path: "publishable-api-keys", element: , handle: { - crumb: () => t("apiKeyManagement.domain.publishable"), + breadcrumb: () => t("apiKeyManagement.domain.publishable"), }, children: [ { @@ -1148,14 +1327,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import( + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( "../../routes/api-key-management/api-key-management-detail" - ), - handle: { - crumb: (data: HttpTypes.AdminApiKeyResponse) => { - return data.api_key.title - }, + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -1180,7 +1365,7 @@ export const RouteMap: RouteObject[] = [ path: "secret-api-keys", element: , handle: { - crumb: () => t("apiKeyManagement.domain.secret"), + breadcrumb: () => t("apiKeyManagement.domain.secret"), }, children: [ { @@ -1207,14 +1392,20 @@ export const RouteMap: RouteObject[] = [ }, { path: ":id", - lazy: () => - import( + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( "../../routes/api-key-management/api-key-management-detail" - ), - handle: { - crumb: (data: HttpTypes.AdminApiKeyResponse) => { - return data.api_key.title - }, + ) + + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -1232,7 +1423,7 @@ export const RouteMap: RouteObject[] = [ path: "tax-regions", element: , handle: { - crumb: () => t("taxRegions.domain"), + breadcrumb: () => t("taxRegions.domain"), }, children: [ { @@ -1251,19 +1442,22 @@ export const RouteMap: RouteObject[] = [ Component: Outlet, loader: taxRegionLoader, handle: { - crumb: (data: HttpTypes.AdminTaxRegionResponse) => { - return ( - getCountryByIso2(data.tax_region.country_code) - ?.display_name || - data.tax_region.country_code?.toUpperCase() - ) - }, + breadcrumb: ( + match: UIMatch + ) => , }, children: [ { path: "", - lazy: () => - import("../../routes/tax-regions/tax-region-detail"), + lazy: async () => { + const { Component } = await import( + "../../routes/tax-regions/tax-region-detail" + ) + + return { + Component, + } + }, children: [ { path: "provinces/create", @@ -1304,26 +1498,20 @@ export const RouteMap: RouteObject[] = [ }, { path: "provinces/:province_id", - lazy: () => - import( + lazy: async () => { + const { Component, Breadcrumb, loader } = await import( "../../routes/tax-regions/tax-region-province-detail" - ), - handle: { - crumb: (data: HttpTypes.AdminTaxRegionResponse) => { - const countryCode = - data.tax_region.country_code?.toUpperCase() - const provinceCode = - data.tax_region.province_code?.toUpperCase() - - const isValid = isProvinceInCountry( - countryCode, - provinceCode - ) + ) - return isValid - ? getProvinceByIso2(provinceCode) - : provinceCode - }, + return { + Component, + loader, + handle: { + breadcrumb: ( + match: UIMatch + ) => , + }, + } }, children: [ { @@ -1364,7 +1552,7 @@ export const RouteMap: RouteObject[] = [ path: "return-reasons", element: , handle: { - crumb: () => t("returnReasons.domain"), + breadcrumb: () => t("returnReasons.domain"), }, children: [ { diff --git a/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..255f7cc7aa1b0 --- /dev/null +++ b/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/breadcrumb.tsx @@ -0,0 +1,24 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useApiKey } from "../../../hooks/api" + +type ApiKeyManagementDetailBreadcrumbProps = + UIMatch + +export const ApiKeyManagementDetailBreadcrumb = ( + props: ApiKeyManagementDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { api_key } = useApiKey(id!, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!api_key) { + return null + } + + return {api_key.title} +} diff --git a/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/index.ts b/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/index.ts index 710a7b5175286..7ee3210445dea 100644 --- a/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/index.ts +++ b/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/index.ts @@ -1,2 +1,3 @@ export { ApiKeyManagementDetail as Component } from "./api-key-management-detail" +export { ApiKeyManagementDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { apiKeyLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/loader.ts b/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/loader.ts index 34c06f43d7e74..f7daf2c83704c 100644 --- a/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/api-key-management/api-key-management-detail/loader.ts @@ -1,6 +1,5 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { apiKeysQueryKeys } from "../../../hooks/api/api-keys" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" @@ -14,8 +13,5 @@ export const apiKeyLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = apiKeyDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..9b46fc0129281 --- /dev/null +++ b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/breadcrumb.tsx @@ -0,0 +1,29 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useCampaign } from "../../../hooks/api" +import { CAMPAIGN_DETAIL_FIELDS } from "./constants" + +type CampaignDetailBreadcrumbProps = UIMatch + +export const CampaignDetailBreadcrumb = ( + props: CampaignDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { campaign } = useCampaign( + id!, + { + fields: CAMPAIGN_DETAIL_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!campaign) { + return null + } + + return {campaign.name} +} diff --git a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/campaign-detail.tsx b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/campaign-detail.tsx index 4c1d588d51678..a802e4e998521 100644 --- a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/campaign-detail.tsx +++ b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/campaign-detail.tsx @@ -11,6 +11,7 @@ import { TwoColumnPageSkeleton } from "../../../components/common/skeleton" import { TwoColumnPage } from "../../../components/layout/pages" import { useDashboardExtension } from "../../../extensions" import { CampaignConfigurationSection } from "./components/campaign-configuration-section" +import { CAMPAIGN_DETAIL_FIELDS } from "./constants" export const CampaignDetail = () => { const initialData = useLoaderData() as Awaited< @@ -20,7 +21,7 @@ export const CampaignDetail = () => { const { id } = useParams() const { campaign, isLoading, isError, error } = useCampaign( id!, - { fields: "+promotions.id" }, + { fields: CAMPAIGN_DETAIL_FIELDS }, { initialData } ) diff --git a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/constants.ts b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/constants.ts new file mode 100644 index 0000000000000..d449a2d442412 --- /dev/null +++ b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/constants.ts @@ -0,0 +1 @@ +export const CAMPAIGN_DETAIL_FIELDS = "+promotions.id" diff --git a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/index.ts b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/index.ts index d44b9e434d12b..4cf39a3b47660 100644 --- a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/index.ts +++ b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/index.ts @@ -1,2 +1,3 @@ +export { CampaignDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { CampaignDetail as Component } from "./campaign-detail" export { campaignLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/loader.ts b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/loader.ts index 0361e3bc633e0..852f79af0b850 100644 --- a/packages/admin/dashboard/src/routes/campaigns/campaign-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/campaigns/campaign-detail/loader.ts @@ -1,15 +1,15 @@ -import { AdminCampaignResponse } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { campaignsQueryKeys } from "../../../hooks/api/campaigns" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" +import { CAMPAIGN_DETAIL_FIELDS } from "./constants" const campaignDetailQuery = (id: string) => ({ queryKey: campaignsQueryKeys.detail(id), queryFn: async () => sdk.admin.campaign.retrieve(id, { - fields: "+promotions.id", + fields: CAMPAIGN_DETAIL_FIELDS, }), }) @@ -17,8 +17,5 @@ export const campaignLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = campaignDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/categories/category-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/categories/category-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..9eb94decc0313 --- /dev/null +++ b/packages/admin/dashboard/src/routes/categories/category-detail/breadcrumb.tsx @@ -0,0 +1,29 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useProductCategory } from "../../../hooks/api" + +type CategoryDetailBreadcrumbProps = + UIMatch + +export const CategoryDetailBreadcrumb = ( + props: CategoryDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { product_category } = useProductCategory( + id!, + { + fields: "name", + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!product_category) { + return null + } + + return {product_category.name} +} diff --git a/packages/admin/dashboard/src/routes/categories/category-detail/index.ts b/packages/admin/dashboard/src/routes/categories/category-detail/index.ts index 8e305c712c96a..f69036910917d 100644 --- a/packages/admin/dashboard/src/routes/categories/category-detail/index.ts +++ b/packages/admin/dashboard/src/routes/categories/category-detail/index.ts @@ -1,2 +1,3 @@ +export { CategoryDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { CategoryDetail as Component } from "./category-detail" export { categoryLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/categories/category-detail/loader.ts b/packages/admin/dashboard/src/routes/categories/category-detail/loader.ts index 979cb76d8adb2..27c62b55615e6 100644 --- a/packages/admin/dashboard/src/routes/categories/category-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/categories/category-detail/loader.ts @@ -1,4 +1,3 @@ -import { AdminProductCategoryResponse } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { categoriesQueryKeys } from "../../../hooks/api/categories" @@ -14,8 +13,5 @@ export const categoryLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = categoryDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/collections/collection-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/collections/collection-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..677ca48b009b1 --- /dev/null +++ b/packages/admin/dashboard/src/routes/collections/collection-detail/breadcrumb.tsx @@ -0,0 +1,23 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useCollection } from "../../../hooks/api" + +type CollectionDetailBreadcrumbProps = + UIMatch + +export const CollectionDetailBreadcrumb = ( + props: CollectionDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { collection } = useCollection(id!, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!collection) { + return null + } + + return {collection.title} +} diff --git a/packages/admin/dashboard/src/routes/collections/collection-detail/index.ts b/packages/admin/dashboard/src/routes/collections/collection-detail/index.ts index d993cc2b01ec0..c4932aae15a14 100644 --- a/packages/admin/dashboard/src/routes/collections/collection-detail/index.ts +++ b/packages/admin/dashboard/src/routes/collections/collection-detail/index.ts @@ -1,2 +1,3 @@ +export { CollectionDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { CollectionDetail as Component } from "./collection-detail" export { collectionLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/collections/collection-detail/loader.ts b/packages/admin/dashboard/src/routes/collections/collection-detail/loader.ts index 7d5dee3b23ffb..e922557266ee4 100644 --- a/packages/admin/dashboard/src/routes/collections/collection-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/collections/collection-detail/loader.ts @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { collectionsQueryKeys } from "../../../hooks/api/collections" @@ -14,9 +13,5 @@ export const collectionLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = collectionDetailQuery(id!) - return ( - queryClient.getQueryData<{ collection: HttpTypes.AdminCollection }>( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..de9232747b011 --- /dev/null +++ b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/breadcrumb.tsx @@ -0,0 +1,31 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useCustomerGroup } from "../../../hooks/api" +import { CUSTOMER_GROUP_DETAIL_FIELDS } from "./constants" + +type CustomerGroupDetailBreadcrumbProps = + UIMatch + +export const CustomerGroupDetailBreadcrumb = ( + props: CustomerGroupDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { customer_group } = useCustomerGroup( + id!, + { + fields: CUSTOMER_GROUP_DETAIL_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!customer_group) { + return null + } + + return {customer_group.name} +} diff --git a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/constants.ts b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/constants.ts new file mode 100644 index 0000000000000..bd1ebdf4baa6e --- /dev/null +++ b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/constants.ts @@ -0,0 +1 @@ +export const CUSTOMER_GROUP_DETAIL_FIELDS = "+customers.id" diff --git a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/customer-group-detail.tsx b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/customer-group-detail.tsx index 17d79633ac746..65c81efaabf60 100644 --- a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/customer-group-detail.tsx +++ b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/customer-group-detail.tsx @@ -8,6 +8,7 @@ import { customerGroupLoader } from "./loader" import { SingleColumnPageSkeleton } from "../../../components/common/skeleton" import { useDashboardExtension } from "../../../extensions" +import { CUSTOMER_GROUP_DETAIL_FIELDS } from "./constants" export const CustomerGroupDetail = () => { const initialData = useLoaderData() as Awaited< @@ -18,7 +19,7 @@ export const CustomerGroupDetail = () => { const { customer_group, isLoading, isError, error } = useCustomerGroup( id!, { - fields: "+customers.id", + fields: CUSTOMER_GROUP_DETAIL_FIELDS, }, { initialData } ) diff --git a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/index.ts b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/index.ts index c1f4e53399bd9..2765f2827faa6 100644 --- a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/index.ts +++ b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/index.ts @@ -1,2 +1,3 @@ +export { CustomerGroupDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { CustomerGroupDetail as Component } from "./customer-group-detail" export { customerGroupLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/loader.ts b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/loader.ts index 8538ead153f83..7d43ad6d4fa8d 100644 --- a/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/customer-groups/customer-group-detail/loader.ts @@ -1,14 +1,14 @@ -import { HttpTypes } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { productsQueryKeys } from "../../../hooks/api/products" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" +import { CUSTOMER_GROUP_DETAIL_FIELDS } from "./constants" const customerGroupDetailQuery = (id: string) => ({ queryKey: productsQueryKeys.detail(id), queryFn: async () => sdk.admin.customerGroup.retrieve(id, { - fields: "+customers.id", + fields: CUSTOMER_GROUP_DETAIL_FIELDS, }), }) @@ -16,9 +16,5 @@ export const customerGroupLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = customerGroupDetailQuery(id!) - return ( - queryClient.getQueryData<{ - customer_group: HttpTypes.AdminCustomerGroup - }>(query.queryKey) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/customers/customer-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..8278c4250f3cc --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/breadcrumb.tsx @@ -0,0 +1,29 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useCustomer } from "../../../hooks/api" + +type CustomerDetailBreadcrumbProps = UIMatch + +export const CustomerDetailBreadcrumb = ( + props: CustomerDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { customer } = useCustomer(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!customer) { + return null + } + + const name = [customer.first_name, customer.last_name] + .filter(Boolean) + .join(" ") + + const display = name || customer.email + + return {display} +} diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/index.ts b/packages/admin/dashboard/src/routes/customers/customer-detail/index.ts index bed23b93e7de2..edd694dd101eb 100644 --- a/packages/admin/dashboard/src/routes/customers/customer-detail/index.ts +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/index.ts @@ -1,2 +1,3 @@ +export { CustomerDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { CustomerDetail as Component } from "./customer-detail" export { customerLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts b/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts index 51e19f22f24c0..eebfaa84f9378 100644 --- a/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts @@ -2,7 +2,6 @@ import { LoaderFunctionArgs } from "react-router-dom" import { productsQueryKeys } from "../../../hooks/api/products" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" -import { HttpTypes } from "@medusajs/types" const customerDetailQuery = (id: string) => ({ queryKey: productsQueryKeys.detail(id), @@ -13,9 +12,5 @@ export const customerLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = customerDetailQuery(id!) - return ( - queryClient.getQueryData<{ customer: HttpTypes.AdminCustomer }>( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/inventory/inventory-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/inventory/inventory-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..9e9f7a30b1188 --- /dev/null +++ b/packages/admin/dashboard/src/routes/inventory/inventory-detail/breadcrumb.tsx @@ -0,0 +1,31 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useInventoryItem } from "../../../hooks/api" +import { INVENTORY_DETAIL_FIELDS } from "./constants" + +type InventoryDetailBreadcrumbProps = + UIMatch + +export const InventoryDetailBreadcrumb = ( + props: InventoryDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { inventory_item } = useInventoryItem( + id!, + { + fields: INVENTORY_DETAIL_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!inventory_item) { + return null + } + + return {inventory_item.title ?? inventory_item.sku ?? id} +} diff --git a/packages/admin/dashboard/src/routes/inventory/inventory-detail/constants.ts b/packages/admin/dashboard/src/routes/inventory/inventory-detail/constants.ts new file mode 100644 index 0000000000000..5e4988ae0e4d9 --- /dev/null +++ b/packages/admin/dashboard/src/routes/inventory/inventory-detail/constants.ts @@ -0,0 +1,2 @@ +export const INVENTORY_DETAIL_FIELDS = + "*variants,*variants.product,*variants.options" diff --git a/packages/admin/dashboard/src/routes/inventory/inventory-detail/index.ts b/packages/admin/dashboard/src/routes/inventory/inventory-detail/index.ts index 730d046ba45b1..205367ecd5948 100644 --- a/packages/admin/dashboard/src/routes/inventory/inventory-detail/index.ts +++ b/packages/admin/dashboard/src/routes/inventory/inventory-detail/index.ts @@ -1,2 +1,3 @@ +export { InventoryDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { InventoryDetail as Component } from "./inventory-detail" export { inventoryItemLoader as loader } from "./loader" diff --git a/packages/admin/dashboard/src/routes/inventory/inventory-detail/inventory-detail.tsx b/packages/admin/dashboard/src/routes/inventory/inventory-detail/inventory-detail.tsx index d68a413a90489..43a6b0dd5146b 100644 --- a/packages/admin/dashboard/src/routes/inventory/inventory-detail/inventory-detail.tsx +++ b/packages/admin/dashboard/src/routes/inventory/inventory-detail/inventory-detail.tsx @@ -11,6 +11,7 @@ import { InventoryItemVariantsSection } from "./components/inventory-item-varian import { inventoryItemLoader } from "./loader" import { useDashboardExtension } from "../../../extensions" +import { INVENTORY_DETAIL_FIELDS } from "./constants" export const InventoryDetail = () => { const { id } = useParams() @@ -27,7 +28,7 @@ export const InventoryDetail = () => { } = useInventoryItem( id!, { - fields: "*variants,*variants.product,*variants.options", + fields: INVENTORY_DETAIL_FIELDS, }, { initialData, diff --git a/packages/admin/dashboard/src/routes/inventory/inventory-detail/loader.ts b/packages/admin/dashboard/src/routes/inventory/inventory-detail/loader.ts index d51f996d2edbc..7c55e44035054 100644 --- a/packages/admin/dashboard/src/routes/inventory/inventory-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/inventory/inventory-detail/loader.ts @@ -1,15 +1,15 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { inventoryItemsQueryKeys } from "../../../hooks/api/inventory" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" +import { INVENTORY_DETAIL_FIELDS } from "./constants" const inventoryDetailQuery = (id: string) => ({ queryKey: inventoryItemsQueryKeys.detail(id), queryFn: async () => sdk.admin.inventoryItem.retrieve(id, { - fields: "*variants,*variants.product,*variants.options", + fields: INVENTORY_DETAIL_FIELDS, }), }) @@ -17,9 +17,5 @@ export const inventoryItemLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = inventoryDetailQuery(id!) - return ( - queryClient.getQueryData( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/locations/location-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/locations/location-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..efdff21669584 --- /dev/null +++ b/packages/admin/dashboard/src/routes/locations/location-detail/breadcrumb.tsx @@ -0,0 +1,31 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useStockLocation } from "../../../hooks/api/stock-locations" +import { LOCATION_DETAILS_FIELD } from "./constants" + +type LocationDetailBreadcrumbProps = + UIMatch + +export const LocationDetailBreadcrumb = ( + props: LocationDetailBreadcrumbProps +) => { + const { location_id } = props.params || {} + + const { stock_location } = useStockLocation( + location_id!, + { + fields: LOCATION_DETAILS_FIELD, + }, + { + initialData: props.data, + enabled: Boolean(location_id), + } + ) + + if (!stock_location) { + return null + } + + return {stock_location.name} +} diff --git a/packages/admin/dashboard/src/routes/locations/location-detail/const.ts b/packages/admin/dashboard/src/routes/locations/location-detail/constants.ts similarity index 90% rename from packages/admin/dashboard/src/routes/locations/location-detail/const.ts rename to packages/admin/dashboard/src/routes/locations/location-detail/constants.ts index c15c45a951360..78f963317d20a 100644 --- a/packages/admin/dashboard/src/routes/locations/location-detail/const.ts +++ b/packages/admin/dashboard/src/routes/locations/location-detail/constants.ts @@ -1,2 +1,2 @@ -export const detailsFields = +export const LOCATION_DETAILS_FIELD = "name,*sales_channels,*address,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,*fulfillment_providers" diff --git a/packages/admin/dashboard/src/routes/locations/location-detail/index.ts b/packages/admin/dashboard/src/routes/locations/location-detail/index.ts index 7aab908774ceb..4f04ed8d2487e 100644 --- a/packages/admin/dashboard/src/routes/locations/location-detail/index.ts +++ b/packages/admin/dashboard/src/routes/locations/location-detail/index.ts @@ -1,2 +1,3 @@ +export { LocationDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { locationLoader as loader } from "./loader" export { LocationDetail as Component } from "./location-detail" diff --git a/packages/admin/dashboard/src/routes/locations/location-detail/loader.ts b/packages/admin/dashboard/src/routes/locations/location-detail/loader.ts index 07d435ef146ce..7c1bc859a2272 100644 --- a/packages/admin/dashboard/src/routes/locations/location-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/locations/location-detail/loader.ts @@ -1,38 +1,23 @@ -import { HttpTypes } from "@medusajs/types" -import { LoaderFunctionArgs, redirect } from "react-router-dom" +import { LoaderFunctionArgs } from "react-router-dom" -import { FetchError } from "@medusajs/js-sdk" import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" -import { detailsFields } from "./const" +import { LOCATION_DETAILS_FIELD } from "./constants" const locationQuery = (id: string) => ({ queryKey: stockLocationsQueryKeys.detail(id, { - fields: detailsFields, + fields: LOCATION_DETAILS_FIELD, }), - queryFn: async () => { - return await sdk.admin.stockLocation - .retrieve(id, { - fields: detailsFields, - }) - .catch((error: FetchError) => { - if (error.status === 401) { - throw redirect("/login") - } - - throw error - }) - }, + queryFn: async () => + sdk.admin.stockLocation.retrieve(id, { + fields: LOCATION_DETAILS_FIELD, + }), }) export const locationLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.location_id const query = locationQuery(id!) - return ( - queryClient.getQueryData<{ stock_location: HttpTypes.AdminStockLocation }>( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/locations/location-detail/location-detail.tsx b/packages/admin/dashboard/src/routes/locations/location-detail/location-detail.tsx index 9af3ee7389deb..e7a60dbb57ddf 100644 --- a/packages/admin/dashboard/src/routes/locations/location-detail/location-detail.tsx +++ b/packages/admin/dashboard/src/routes/locations/location-detail/location-detail.tsx @@ -9,7 +9,7 @@ import { TwoColumnPageSkeleton } from "../../../components/common/skeleton" import { TwoColumnPage } from "../../../components/layout/pages" import { useDashboardExtension } from "../../../extensions" import LocationsFulfillmentProvidersSection from "./components/location-fulfillment-providers-section/location-fulfillment-providers-section" -import { detailsFields } from "./const" +import { LOCATION_DETAILS_FIELD } from "./constants" export const LocationDetail = () => { const initialData = useLoaderData() as Awaited< @@ -22,7 +22,11 @@ export const LocationDetail = () => { isPending: isLoading, isError, error, - } = useStockLocation(location_id!, { fields: detailsFields }, { initialData }) + } = useStockLocation( + location_id!, + { fields: LOCATION_DETAILS_FIELD }, + { initialData } + ) const { getWidgets } = useDashboardExtension() diff --git a/packages/admin/dashboard/src/routes/orders/order-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/orders/order-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..36ca4db27792c --- /dev/null +++ b/packages/admin/dashboard/src/routes/orders/order-detail/breadcrumb.tsx @@ -0,0 +1,27 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useOrder } from "../../../hooks/api" +import { DEFAULT_FIELDS } from "./constants" + +type OrderDetailBreadcrumbProps = UIMatch + +export const OrderDetailBreadcrumb = (props: OrderDetailBreadcrumbProps) => { + const { id } = props.params || {} + + const { order } = useOrder( + id!, + { + fields: DEFAULT_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!order) { + return null + } + + return #{order.display_id} +} diff --git a/packages/admin/dashboard/src/routes/orders/order-detail/index.ts b/packages/admin/dashboard/src/routes/orders/order-detail/index.ts index 72ffee80e5c53..bff005715aa3b 100644 --- a/packages/admin/dashboard/src/routes/orders/order-detail/index.ts +++ b/packages/admin/dashboard/src/routes/orders/order-detail/index.ts @@ -1,2 +1,3 @@ +export { OrderDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { orderLoader as loader } from "./loader" export { OrderDetail as Component } from "./order-detail" diff --git a/packages/admin/dashboard/src/routes/orders/order-detail/loader.ts b/packages/admin/dashboard/src/routes/orders/order-detail/loader.ts index febd09579e565..036cb5aecb0cb 100644 --- a/packages/admin/dashboard/src/routes/orders/order-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/orders/order-detail/loader.ts @@ -1,6 +1,5 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { ordersQueryKeys } from "../../../hooks/api/orders" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" @@ -18,8 +17,5 @@ export const orderLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = orderDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..89c3f1fa236f4 --- /dev/null +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/breadcrumb.tsx @@ -0,0 +1,23 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { usePriceList } from "../../../hooks/api" + +type PriceListDetailBreadcrumbProps = UIMatch + +export const PriceListDetailBreadcrumb = ( + props: PriceListDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { price_list } = usePriceList(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!price_list) { + return null + } + + return {price_list.title} +} diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/index.ts b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/index.ts index a15288c41fe42..0b5c80147e975 100644 --- a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/index.ts +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/index.ts @@ -1,2 +1,3 @@ +export { PriceListDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { pricingLoader as loader } from "./loader" export { PriceListDetails as Component } from "./price-list-detail" diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/loader.ts b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/loader.ts index 08c23dc251653..4a03d34672c49 100644 --- a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/loader.ts @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { priceListsQueryKeys } from "../../../hooks/api/price-lists" import { sdk } from "../../../lib/client" @@ -13,9 +12,5 @@ export const pricingLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = pricingDetailQuery(id!) - return ( - queryClient.getQueryData( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..34a5fa2344d75 --- /dev/null +++ b/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/breadcrumb.tsx @@ -0,0 +1,23 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useProductTag } from "../../../hooks/api" + +type ProductTagDetailBreadcrumbProps = + UIMatch + +export const ProductTagDetailBreadcrumb = ( + props: ProductTagDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { product_tag } = useProductTag(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!product_tag) { + return null + } + + return {product_tag.value} +} diff --git a/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/index.ts b/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/index.ts index 5464ac6b2e0d1..67290d2aae17c 100644 --- a/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/index.ts +++ b/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/index.ts @@ -1,2 +1,3 @@ +export { ProductTagDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { productTagLoader as loader } from "./loader" export { ProductTagDetail as Component } from "./product-tag-detail" diff --git a/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/loader.ts b/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/loader.ts index 896faa6016bb6..d11a3a7f235f1 100644 --- a/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/product-tags/product-tag-detail/loader.ts @@ -13,8 +13,5 @@ export const productTagLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = productTagDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/product-types/product-type-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/product-types/product-type-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..dcc0e8353b147 --- /dev/null +++ b/packages/admin/dashboard/src/routes/product-types/product-type-detail/breadcrumb.tsx @@ -0,0 +1,24 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useProductType } from "../../../hooks/api" + +type ProductTypeDetailBreadcrumbProps = + UIMatch + +export const ProductTypeDetailBreadcrumb = ( + props: ProductTypeDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { product_type } = useProductType(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!product_type) { + return null + } + + return {product_type.value} +} diff --git a/packages/admin/dashboard/src/routes/product-types/product-type-detail/index.ts b/packages/admin/dashboard/src/routes/product-types/product-type-detail/index.ts index 098bfbc1edcb1..83df010a8bb04 100644 --- a/packages/admin/dashboard/src/routes/product-types/product-type-detail/index.ts +++ b/packages/admin/dashboard/src/routes/product-types/product-type-detail/index.ts @@ -1,2 +1,3 @@ +export { ProductTypeDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { productTypeLoader as loader } from "./loader" export { ProductTypeDetail as Component } from "./product-type-detail" diff --git a/packages/admin/dashboard/src/routes/product-types/product-type-detail/loader.ts b/packages/admin/dashboard/src/routes/product-types/product-type-detail/loader.ts index 15f5142903e07..9bdbd5595f6fd 100644 --- a/packages/admin/dashboard/src/routes/product-types/product-type-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/product-types/product-type-detail/loader.ts @@ -1,6 +1,5 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { productTypesQueryKeys } from "../../../hooks/api/product-types" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" @@ -14,9 +13,5 @@ export const productTypeLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = productTypeDetailQuery(id!) - return ( - queryClient.getQueryData( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..730ee435ab399 --- /dev/null +++ b/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/breadcrumb.tsx @@ -0,0 +1,31 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useProductVariant } from "../../../hooks/api" +import { VARIANT_DETAIL_FIELDS } from "./constants" + +type ProductVariantDetailBreadcrumbProps = + UIMatch + +export const ProductVariantDetailBreadcrumb = ( + props: ProductVariantDetailBreadcrumbProps +) => { + const { id, variant_id } = props.params || {} + + const { variant } = useProductVariant( + id!, + variant_id!, + { + fields: VARIANT_DETAIL_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id) && Boolean(variant_id), + } + ) + + if (!variant) { + return null + } + + return {variant.title} +} diff --git a/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/index.ts b/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/index.ts index aca57b8850722..73598d513f71e 100644 --- a/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/index.ts +++ b/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/index.ts @@ -1,2 +1,3 @@ +export { ProductVariantDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { variantLoader as loader } from "./loader" export { ProductVariantDetail as Component } from "./product-variant-detail" diff --git a/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/loader.ts b/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/loader.ts index 0a9167c330e17..973bcb3a91b23 100644 --- a/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/product-variants/product-variant-detail/loader.ts @@ -21,8 +21,5 @@ export const variantLoader = async ({ params }: LoaderFunctionArgs) => { const query = variantDetailQuery(productId!, variantId!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/products/product-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/products/product-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..1abc880571c47 --- /dev/null +++ b/packages/admin/dashboard/src/routes/products/product-detail/breadcrumb.tsx @@ -0,0 +1,29 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useProduct } from "../../../hooks/api" +import { PRODUCT_DETAIL_FIELDS } from "./constants" + +type ProductDetailBreadcrumbProps = UIMatch + +export const ProductDetailBreadcrumb = ( + props: ProductDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { product } = useProduct( + id!, + { + fields: PRODUCT_DETAIL_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!product) { + return null + } + + return {product.title} +} diff --git a/packages/admin/dashboard/src/routes/products/product-detail/index.ts b/packages/admin/dashboard/src/routes/products/product-detail/index.ts index afc54745c9306..eeb6d5461b1b8 100644 --- a/packages/admin/dashboard/src/routes/products/product-detail/index.ts +++ b/packages/admin/dashboard/src/routes/products/product-detail/index.ts @@ -1,2 +1,3 @@ +export { ProductDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { productLoader as loader } from "./loader" export { ProductDetail as Component } from "./product-detail" diff --git a/packages/admin/dashboard/src/routes/products/product-detail/loader.ts b/packages/admin/dashboard/src/routes/products/product-detail/loader.ts index bf659944a71ea..fa1375a7004d6 100644 --- a/packages/admin/dashboard/src/routes/products/product-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/products/product-detail/loader.ts @@ -6,7 +6,7 @@ import { queryClient } from "../../../lib/query-client" import { PRODUCT_DETAIL_FIELDS } from "./constants" const productDetailQuery = (id: string) => ({ - queryKey: productsQueryKeys.detail(id), + queryKey: productsQueryKeys.detail(id, { fields: PRODUCT_DETAIL_FIELDS }), queryFn: async () => sdk.admin.product.retrieve(id, { fields: PRODUCT_DETAIL_FIELDS }), }) @@ -15,8 +15,10 @@ export const productLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = productDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + const response = await queryClient.ensureQueryData({ + ...query, + staleTime: 90000, + }) + + return response } diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/promotions/promotion-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..4cc936851412e --- /dev/null +++ b/packages/admin/dashboard/src/routes/promotions/promotion-detail/breadcrumb.tsx @@ -0,0 +1,22 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { usePromotion } from "../../../hooks/api" + +type PromotionDetailBreadcrumbProps = UIMatch + +export const PromotionDetailBreadcrumb = ( + props: PromotionDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { promotion } = usePromotion(id!, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!promotion) { + return null + } + + return {promotion.code} +} diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-detail/index.ts b/packages/admin/dashboard/src/routes/promotions/promotion-detail/index.ts index a40fbfe52a56a..d2ee0b33b3004 100644 --- a/packages/admin/dashboard/src/routes/promotions/promotion-detail/index.ts +++ b/packages/admin/dashboard/src/routes/promotions/promotion-detail/index.ts @@ -1,2 +1,3 @@ +export { PromotionDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { promotionLoader as loader } from "./loader.ts" export { PromotionDetail as Component } from "./promotion-detail.tsx" diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-detail/loader.ts b/packages/admin/dashboard/src/routes/promotions/promotion-detail/loader.ts index b476693132825..49d53da68b731 100644 --- a/packages/admin/dashboard/src/routes/promotions/promotion-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/promotions/promotion-detail/loader.ts @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { promotionsQueryKeys } from "../../../hooks/api/promotions" import { sdk } from "../../../lib/client" @@ -13,9 +12,5 @@ export const promotionLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = promotionDetailQuery(id!) - return ( - queryClient.getQueryData( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/regions/region-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/regions/region-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..c6673aaae3b24 --- /dev/null +++ b/packages/admin/dashboard/src/routes/regions/region-detail/breadcrumb.tsx @@ -0,0 +1,27 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useRegion } from "../../../hooks/api/regions" +import { REGION_DETAIL_FIELDS } from "./constants" + +type RegionDetailBreadcrumbProps = UIMatch + +export const RegionDetailBreadcrumb = (props: RegionDetailBreadcrumbProps) => { + const { id } = props.params || {} + + const { region } = useRegion( + id!, + { + fields: REGION_DETAIL_FIELDS, + }, + { + initialData: props.data, + enabled: Boolean(id), + } + ) + + if (!region) { + return null + } + + return {region.name} +} diff --git a/packages/admin/dashboard/src/routes/regions/region-detail/constants.ts b/packages/admin/dashboard/src/routes/regions/region-detail/constants.ts new file mode 100644 index 0000000000000..f8b82634f55e3 --- /dev/null +++ b/packages/admin/dashboard/src/routes/regions/region-detail/constants.ts @@ -0,0 +1,2 @@ +export const REGION_DETAIL_FIELDS = + "*payment_providers,*countries,+automatic_taxes" diff --git a/packages/admin/dashboard/src/routes/regions/region-detail/index.ts b/packages/admin/dashboard/src/routes/regions/region-detail/index.ts index 17c680a88e302..c72b9957582ce 100644 --- a/packages/admin/dashboard/src/routes/regions/region-detail/index.ts +++ b/packages/admin/dashboard/src/routes/regions/region-detail/index.ts @@ -1,2 +1,3 @@ +export { RegionDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { regionLoader as loader } from "./loader" export { RegionDetail as Component } from "./region-detail" diff --git a/packages/admin/dashboard/src/routes/regions/region-detail/loader.ts b/packages/admin/dashboard/src/routes/regions/region-detail/loader.ts index 4a0bb909a1e5d..470d78c7e2815 100644 --- a/packages/admin/dashboard/src/routes/regions/region-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/regions/region-detail/loader.ts @@ -3,12 +3,13 @@ import { LoaderFunctionArgs } from "react-router-dom" import { regionsQueryKeys } from "../../../hooks/api/regions" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" +import { REGION_DETAIL_FIELDS } from "./constants" const regionQuery = (id: string) => ({ queryKey: regionsQueryKeys.detail(id), queryFn: async () => sdk.admin.region.retrieve(id, { - fields: "*payment_providers,*countries,+automatic_taxes", + fields: REGION_DETAIL_FIELDS, }), }) diff --git a/packages/admin/dashboard/src/routes/regions/region-detail/region-detail.tsx b/packages/admin/dashboard/src/routes/regions/region-detail/region-detail.tsx index 386c2e7b90e91..b9d20d5160cdd 100644 --- a/packages/admin/dashboard/src/routes/regions/region-detail/region-detail.tsx +++ b/packages/admin/dashboard/src/routes/regions/region-detail/region-detail.tsx @@ -9,6 +9,7 @@ import { SingleColumnPageSkeleton } from "../../../components/common/skeleton" import { SingleColumnPage } from "../../../components/layout/pages" import { useDashboardExtension } from "../../../extensions" import { usePricePreferences } from "../../../hooks/api/price-preferences" +import { REGION_DETAIL_FIELDS } from "./constants" export const RegionDetail = () => { const initialData = useLoaderData() as Awaited< @@ -23,7 +24,7 @@ export const RegionDetail = () => { error: regionError, } = useRegion( id!, - { fields: "*payment_providers,*countries,+automatic_taxes" }, + { fields: REGION_DETAIL_FIELDS }, { initialData, } diff --git a/packages/admin/dashboard/src/routes/reservations/reservation-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/reservations/reservation-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..b3076f0e84482 --- /dev/null +++ b/packages/admin/dashboard/src/routes/reservations/reservation-detail/breadcrumb.tsx @@ -0,0 +1,29 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useReservationItem } from "../../../hooks/api" + +type ReservationDetailBreadcrumbProps = + UIMatch + +export const ReservationDetailBreadcrumb = ( + props: ReservationDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { reservation } = useReservationItem(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!reservation) { + return null + } + + const display = + reservation?.inventory_item?.title ?? + reservation?.inventory_item?.sku ?? + reservation.id + + return {display} +} diff --git a/packages/admin/dashboard/src/routes/reservations/reservation-detail/index.ts b/packages/admin/dashboard/src/routes/reservations/reservation-detail/index.ts index ef0430880c833..bffc3f474fff7 100644 --- a/packages/admin/dashboard/src/routes/reservations/reservation-detail/index.ts +++ b/packages/admin/dashboard/src/routes/reservations/reservation-detail/index.ts @@ -1,2 +1,3 @@ +export { ReservationDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { reservationItemLoader as loader } from "./loader" export { ReservationDetail as Component } from "./reservation-detail" diff --git a/packages/admin/dashboard/src/routes/reservations/reservation-detail/loader.ts b/packages/admin/dashboard/src/routes/reservations/reservation-detail/loader.ts index dedf1c4622ba0..1088cfb4f8c25 100644 --- a/packages/admin/dashboard/src/routes/reservations/reservation-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/reservations/reservation-detail/loader.ts @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { reservationItemsQueryKeys } from "../../../hooks/api/reservations" import { sdk } from "../../../lib/client" @@ -13,9 +12,5 @@ export const reservationItemLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = reservationDetailQuery(id!) - return ( - queryClient.getQueryData( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..1c2254c8a4d8f --- /dev/null +++ b/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/breadcrumb.tsx @@ -0,0 +1,23 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useSalesChannel } from "../../../hooks/api/sales-channels" + +type SalesChannelDetailBreadcrumbProps = + UIMatch + +export const SalesChannelDetailBreadcrumb = ( + props: SalesChannelDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { sales_channel } = useSalesChannel(id!, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!sales_channel) { + return null + } + + return {sales_channel.name} +} diff --git a/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts b/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts index 8d5a59045f7b8..a56aa3df7dfd0 100644 --- a/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts +++ b/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts @@ -1,2 +1,3 @@ +export { SalesChannelDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { salesChannelLoader as loader } from "./loader" export { SalesChannelDetail as Component } from "./sales-channel-detail" diff --git a/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts b/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts index 5c58f64764ad5..7fdea88a57ac5 100644 --- a/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts @@ -1,6 +1,5 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { AdminSalesChannelResponse } from "@medusajs/types" import { productsQueryKeys } from "../../../hooks/api/products" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" @@ -14,8 +13,5 @@ export const salesChannelLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = salesChannelDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..af6337d943ec8 --- /dev/null +++ b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/breadcrumb.tsx @@ -0,0 +1,27 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useShippingProfile } from "../../../hooks/api/shipping-profiles" + +type ShippingProfileDetailBreadcrumbProps = + UIMatch + +export const ShippingProfileDetailBreadcrumb = ( + props: ShippingProfileDetailBreadcrumbProps +) => { + const { shipping_profile_id } = props.params || {} + + const { shipping_profile } = useShippingProfile( + shipping_profile_id!, + undefined, + { + initialData: props.data, + enabled: Boolean(shipping_profile_id), + } + ) + + if (!shipping_profile) { + return null + } + + return {shipping_profile.name} +} diff --git a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/index.ts b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/index.ts index c3508861c895f..5bc8051bf19ac 100644 --- a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/index.ts +++ b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/index.ts @@ -1,2 +1,3 @@ +export { ShippingProfileDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { shippingProfileLoader as loader } from "./loader" export { ShippingProfileDetail as Component } from "./shipping-profile-detail" diff --git a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/loader.ts b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/loader.ts index 2c5115afc2f4f..9e215a59cd9b3 100644 --- a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/loader.ts @@ -1,9 +1,8 @@ -import { HttpTypes } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" +import { shippingProfileQueryKeys } from "../../../hooks/api/shipping-profiles" 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), @@ -11,12 +10,8 @@ const shippingProfileQuery = (id: string) => ({ }) export const shippingProfileLoader = async ({ params }: LoaderFunctionArgs) => { - const id = params.id + const id = params.shipping_profile_id const query = shippingProfileQuery(id!) - return ( - queryClient.getQueryData<{ - shipping_profile: HttpTypes.AdminShippingProfile - }>(query.queryKey) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/shipping-profile-detail.tsx b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/shipping-profile-detail.tsx index 7210c26f9be57..1103360114a9c 100644 --- a/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/shipping-profile-detail.tsx +++ b/packages/admin/dashboard/src/routes/shipping-profiles/shipping-profile-detail/shipping-profile-detail.tsx @@ -1,4 +1,4 @@ -import { useParams } from "react-router-dom" +import { useLoaderData, useParams } from "react-router-dom" import { SingleColumnPageSkeleton } from "../../../components/common/skeleton" import { useShippingProfile } from "../../../hooks/api/shipping-profiles" @@ -6,12 +6,19 @@ import { ShippingProfileGeneralSection } from "./components/shipping-profile-gen import { SingleColumnPage } from "../../../components/layout/pages" import { useDashboardExtension } from "../../../extensions" +import { shippingProfileLoader } from "./loader" export const ShippingProfileDetail = () => { - const { id } = useParams() + const { shipping_profile_id } = useParams() + + const initialData = useLoaderData() as Awaited< + ReturnType + > const { shipping_profile, isLoading, isError, error } = useShippingProfile( - id! + shipping_profile_id!, + undefined, + { initialData } ) const { getWidgets } = useDashboardExtension() diff --git a/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..df240d548c0c4 --- /dev/null +++ b/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/breadcrumb.tsx @@ -0,0 +1,29 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useTaxRegion } from "../../../hooks/api" +import { getCountryByIso2 } from "../../../lib/data/countries" + +type TaxRegionDetailBreadcrumbProps = UIMatch + +export const TaxRegionDetailBreadcrumb = ( + props: TaxRegionDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { tax_region } = useTaxRegion(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!tax_region) { + return null + } + + return ( + + {getCountryByIso2(tax_region.country_code)?.display_name || + tax_region.country_code?.toUpperCase()} + + ) +} diff --git a/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/index.ts b/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/index.ts index 38f10d7514a1a..5da2958b44616 100644 --- a/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/index.ts +++ b/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/index.ts @@ -1,4 +1,5 @@ export * from "./tax-region-detail" +export { TaxRegionDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { taxRegionLoader as loader } from "./loader" export { TaxRegionDetail as Component } from "./tax-region-detail" diff --git a/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/loader.ts b/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/loader.ts index f9acdc3e6f58c..e7a57fbd7c2e1 100644 --- a/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/tax-regions/tax-region-detail/loader.ts @@ -1,4 +1,3 @@ -import { AdminTaxRegionResponse } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { taxRegionsQueryKeys } from "../../../hooks/api/tax-regions" import { sdk } from "../../../lib/client" @@ -13,8 +12,5 @@ export const taxRegionLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = taxRegionDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..fd8ab170f52e1 --- /dev/null +++ b/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/breadcrumb.tsx @@ -0,0 +1,32 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useTaxRegion } from "../../../hooks/api" +import { + getProvinceByIso2, + isProvinceInCountry, +} from "../../../lib/data/country-states" + +type TaxRegionDetailBreadcrumbProps = UIMatch + +export const TaxRegionDetailBreadcrumb = ( + props: TaxRegionDetailBreadcrumbProps +) => { + const { province_id } = props.params || {} + + const { tax_region } = useTaxRegion(province_id!, undefined, { + initialData: props.data, + enabled: Boolean(province_id), + }) + + if (!tax_region) { + return null + } + + const countryCode = tax_region.country_code?.toUpperCase() + const provinceCode = tax_region.province_code?.toUpperCase() + + const isValid = isProvinceInCountry(countryCode, provinceCode) + + return {isValid ? getProvinceByIso2(provinceCode) : provinceCode} +} diff --git a/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/index.ts b/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/index.ts index 38f10d7514a1a..5da2958b44616 100644 --- a/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/index.ts +++ b/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/index.ts @@ -1,4 +1,5 @@ export * from "./tax-region-detail" +export { TaxRegionDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { taxRegionLoader as loader } from "./loader" export { TaxRegionDetail as Component } from "./tax-region-detail" diff --git a/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/loader.ts b/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/loader.ts index 5d8a3155e8aa9..84d2f4d182851 100644 --- a/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/tax-regions/tax-region-province-detail/loader.ts @@ -1,4 +1,3 @@ -import { AdminTaxRegionResponse } from "@medusajs/types" import { LoaderFunctionArgs } from "react-router-dom" import { taxRegionsQueryKeys } from "../../../hooks/api/tax-regions" import { sdk } from "../../../lib/client" @@ -13,8 +12,5 @@ export const taxRegionLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.province_id const query = taxRegionDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/users/user-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/users/user-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..0262e874ead10 --- /dev/null +++ b/packages/admin/dashboard/src/routes/users/user-detail/breadcrumb.tsx @@ -0,0 +1,24 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" +import { useUser } from "../../../hooks/api/users" + +type UserDetailBreadcrumbProps = UIMatch + +export const UserDetailBreadcrumb = (props: UserDetailBreadcrumbProps) => { + const { id } = props.params || {} + + const { user } = useUser(id!, undefined, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!user) { + return null + } + + const name = [user.first_name, user.last_name].filter(Boolean).join(" ") + + const display = name || user.email + + return {display} +} diff --git a/packages/admin/dashboard/src/routes/users/user-detail/index.ts b/packages/admin/dashboard/src/routes/users/user-detail/index.ts index d91450c4f035f..2d6cb45999d7a 100644 --- a/packages/admin/dashboard/src/routes/users/user-detail/index.ts +++ b/packages/admin/dashboard/src/routes/users/user-detail/index.ts @@ -1,2 +1,3 @@ +export { UserDetailBreadcrumb as Breadcrumb } from "./breadcrumb" export { userLoader as loader } from "./loader" export { UserDetail as Component } from "./user-detail" diff --git a/packages/admin/dashboard/src/routes/users/user-detail/loader.ts b/packages/admin/dashboard/src/routes/users/user-detail/loader.ts index 4851460d64a76..1f4ed87891842 100644 --- a/packages/admin/dashboard/src/routes/users/user-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/users/user-detail/loader.ts @@ -1,6 +1,5 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { productsQueryKeys } from "../../../hooks/api/products" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" @@ -14,8 +13,5 @@ export const userLoader = async ({ params }: LoaderFunctionArgs) => { const id = params.id const query = userDetailQuery(id!) - return ( - queryClient.getQueryData(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) } diff --git a/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx b/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx new file mode 100644 index 0000000000000..4da7426c5692c --- /dev/null +++ b/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx @@ -0,0 +1,26 @@ +import { HttpTypes } from "@medusajs/types" +import { UIMatch } from "react-router-dom" + +import { useWorkflowExecution } from "../../../hooks/api" + +type WorkflowExecutionDetailBreadcrumbProps = + UIMatch + +export const WorkflowExecutionDetailBreadcrumb = ( + props: WorkflowExecutionDetailBreadcrumbProps +) => { + const { id } = props.params || {} + + const { workflow_execution } = useWorkflowExecution(id!, { + initialData: props.data, + enabled: Boolean(id), + }) + + if (!workflow_execution) { + return null + } + + const cleanId = workflow_execution.id.replace("wf_exec_", "") + + return {cleanId} +} diff --git a/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/index.ts b/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/index.ts index 2289190872954..d243329092374 100644 --- a/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/index.ts +++ b/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/index.ts @@ -1 +1,3 @@ +export { WorkflowExecutionDetailBreadcrumb as Breadcrumb } from "./breadcrumb" +export { workflowExecutionLoader as loader } from "./loader" export { ExecutionDetail as Component } from "./workflow-detail" diff --git a/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/loader.ts b/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/loader.ts index d8f331dfc06bd..816063aed43af 100644 --- a/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/workflow-executions/workflow-execution-detail/loader.ts @@ -1,6 +1,5 @@ import { LoaderFunctionArgs } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { workflowExecutionsQueryKeys } from "../../../hooks/api/workflow-executions" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" @@ -10,13 +9,11 @@ const executionDetailQuery = (id: string) => ({ queryFn: async () => sdk.admin.workflowExecution.retrieve(id), }) -export const executionLoader = async ({ params }: LoaderFunctionArgs) => { +export const workflowExecutionLoader = async ({ + params, +}: LoaderFunctionArgs) => { const id = params.id const query = executionDetailQuery(id!) - return ( - queryClient.getQueryData( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) - ) + return queryClient.ensureQueryData(query) }