Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dashboard,icons,types,js-sdk): add providers to location UI #8328

Merged
merged 6 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { HttpTypes } from "@medusajs/types"
import { sdk } from "../../lib/client"
import { queryClient } from "../../lib/query-client"
import { queryKeysFactory } from "../../lib/query-key-factory"
import { fulfillmentProvidersQueryKeys } from "./fulfillment-providers"

const STOCK_LOCATIONS_QUERY_KEY = "stock_locations" as const
export const stockLocationsQueryKeys = queryKeysFactory(
Expand Down Expand Up @@ -175,3 +176,28 @@ export const useCreateStockLocationFulfillmentSet = (
...options,
})
}

export const useUpdateStockLocationFulfillmentProviders = (
id: string,
options?: UseMutationOptions<
HttpTypes.AdminStockLocationResponse,
FetchError,
HttpTypes.AdminBatchLink
>
) => {
return useMutation({
mutationFn: (payload) =>
sdk.admin.stockLocation.updateFulfillmentProviders(id, payload),
onSuccess: async (data, variables, context) => {
await queryClient.invalidateQueries({
queryKey: stockLocationsQueryKeys.details(),
})
await queryClient.invalidateQueries({
queryKey: fulfillmentProvidersQueryKeys.all,
})

options?.onSuccess?.(data, variables, context)
},
...options,
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { HttpTypes } from "@medusajs/types"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import {
TextCell,
TextHeader,
} from "../../../components/table/table-cells/common/text-cell"
import { formatProvider } from "../../../lib/format-provider"

const columnHelper = createColumnHelper<HttpTypes.AdminFulfillmentProvider>()

export const useFulfillmentProviderTableColumns = () => {
const { t } = useTranslation()

return useMemo(
() => [
columnHelper.accessor("id", {
header: () => <TextHeader text={"Provider"} />,
cell: ({ getValue }) => <TextCell text={formatProvider(getValue())} />,
}),
],
[t]
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { HttpTypes } from "@medusajs/types"
import { useQueryParams } from "../../use-query-params"

type UseFulfillmentProviderTableQueryProps = {
prefix?: string
pageSize?: number
}

export const useFulfillmentProvidersTableQuery = ({
prefix,
pageSize = 20,
}: UseFulfillmentProviderTableQueryProps) => {
const queryObject = useQueryParams(
["offset", "q", "stock_location_id"],
prefix
)

const { offset, q, stock_location_id } = queryObject

const searchParams: HttpTypes.AdminFulfillmentProviderListParams = {
limit: pageSize,
offset: offset ? Number(offset) : 0,
stock_location_id,
q,
}

return {
searchParams,
raw: queryObject,
}
}
9 changes: 9 additions & 0 deletions packages/admin-next/dashboard/src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,15 @@
"delete": {
"confirmation": "You are about to delete the stock location {{name}}. This action cannot be undone."
},
"fulfillmentProviders": {
"header": "Fulfillment Providers",
"shippingOptionsTooltip": "This dropdown will only consist of providers enabled for this location. Add them to the location if the dropdown is disabled.",
"label": "Connected fulfillment providers",
"connectedTo": "Connected to {{count}} of {{total}} fulfillment providers",
"noProviders": "This Stock Location is not connected to any fulfillment providers.",
"action": "Connect Providers",
"successToast": "Fulfillment providers for stock location were successfully updated."
},
"fulfillmentSets": {
"pickup": {
"header": "Pickup"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,13 @@ export const RouteMap: RouteObject[] = [
lazy: () =>
import("../../routes/locations/location-sales-channels"),
},
{
path: "fulfillment-providers",
lazy: () =>
import(
"../../routes/locations/location-fulfillment-providers"
),
},
{
path: "fulfillment-set/:fset_id",
children: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./location-fulfillment-providers-section"
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { HandTruck, PencilSquare } from "@medusajs/icons"
import { HttpTypes } from "@medusajs/types"
import { Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"

import { ActionMenu } from "../../../../../components/common/action-menu"
import { NoRecords } from "../../../../../components/common/empty-table-content"
import { IconAvatar } from "../../../../../components/common/icon-avatar"
import { useFulfillmentProviders } from "../../../../../hooks/api"
import { formatProvider } from "../../../../../lib/format-provider"

type LocationsFulfillmentProvidersSectionProps = {
location: HttpTypes.AdminStockLocation
}

function LocationsFulfillmentProvidersSection({
location,
}: LocationsFulfillmentProvidersSectionProps) {
const { t } = useTranslation()
const { fulfillment_providers } = useFulfillmentProviders({
stock_location_id: location.id,
fields: "id",
})

return (
<Container className="flex flex-col px-6 py-4">
<div className="flex items-center justify-between">
<Heading level="h2">
{t("stockLocations.fulfillmentProviders.header")}
</Heading>

<ActionMenu
groups={[
{
actions: [
{
label: t("actions.edit"),
to: "fulfillment-providers",
icon: <PencilSquare />,
},
],
},
]}
/>
</div>

{fulfillment_providers?.length ? (
<div className="flex flex-col gap-y-4 pt-4">
<div className="grid grid-cols-[28px_1fr] items-center gap-x-3 gap-y-3">
{fulfillment_providers?.map((fulfillmentProvider) => {
return (
<>
<IconAvatar>
<HandTruck className="text-ui-fg-subtle" />
</IconAvatar>

<div className="txt-compact-small">
{formatProvider(fulfillmentProvider.id)}
</div>
</>
)
})}
</div>
</div>
) : (
<NoRecords
className="h-fit pb-2 pt-6 text-center"
action={{
label: t("stockLocations.fulfillmentProviders.action"),
to: "fulfillment-providers",
}}
message={t("stockLocations.fulfillmentProviders.noProviders")}
/>
)}
</Container>
)
}

export default LocationsFulfillmentProvidersSection
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const detailsFields =
"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"
"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"
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import after from "virtual:medusa/widgets/location/details/after"
import before from "virtual:medusa/widgets/location/details/before"
import sideAfter from "virtual:medusa/widgets/location/details/side/after"
import sideBefore from "virtual:medusa/widgets/location/details/side/before"
import LocationsFulfillmentProvidersSection from "./components/location-fulfillment-providers-section/location-fulfillment-providers-section"
import { detailsFields } from "./const"

export const LocationDetail = () => {
Expand All @@ -23,15 +24,7 @@ export const LocationDetail = () => {
isPending: isLoading,
isError,
error,
} = useStockLocation(
location_id!,
{
fields: detailsFields,
},
{
initialData,
}
)
} = useStockLocation(location_id!, { fields: detailsFields }, { initialData })

// TODO: Move to loading.tsx and set as Suspense fallback for the route
if (isLoading || !location) {
Expand Down Expand Up @@ -73,7 +66,10 @@ export const LocationDetail = () => {
</div>
)
})}

<LocationsSalesChannelsSection location={location} />
<LocationsFulfillmentProvidersSection location={location} />

{sideAfter.widgets.map((w, i) => {
return (
<div key={i}>
Expand Down
Loading
Loading