fix(dashboard): Allow admins to update default Sales Channel and Stock Location (#11196)
**Note** The huge diff is because the i18n schema wasn't formatted properly again. Not sure how that happened with the update to the script but perhaps someone didn't update their branch to include the change to format the schema on creation.
This commit is contained in:
committed by
GitHub
parent
88202761a5
commit
51d2960a57
5
.changeset/wild-trainers-sparkle.md
Normal file
5
.changeset/wild-trainers-sparkle.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/dashboard": patch
|
||||
---
|
||||
|
||||
fix(dashboard): Allow admins to update default Sales Channel and Stock Location
|
||||
@@ -1,3 +1,9 @@
|
||||
import { FetchError } from "@medusajs/js-sdk"
|
||||
import {
|
||||
AdminSalesChannelListResponse,
|
||||
AdminSalesChannelResponse,
|
||||
HttpTypes,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
QueryKey,
|
||||
UseMutationOptions,
|
||||
@@ -5,16 +11,10 @@ import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
} from "@tanstack/react-query"
|
||||
import {
|
||||
AdminSalesChannelListResponse,
|
||||
AdminSalesChannelResponse,
|
||||
HttpTypes,
|
||||
} from "@medusajs/types"
|
||||
import { sdk } from "../../lib/client"
|
||||
import { queryClient } from "../../lib/query-client"
|
||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||
import { productsQueryKeys } from "./products"
|
||||
import { FetchError } from "@medusajs/js-sdk"
|
||||
|
||||
const SALES_CHANNELS_QUERY_KEY = "sales-channels" as const
|
||||
export const salesChannelsQueryKeys = queryKeysFactory(SALES_CHANNELS_QUERY_KEY)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2321,6 +2321,8 @@
|
||||
"editStore": "Edit store",
|
||||
"defaultCurrency": "Default currency",
|
||||
"defaultRegion": "Default region",
|
||||
"defaultSalesChannel": "Default sales channel",
|
||||
"defaultLocation": "Default location",
|
||||
"swapLinkTemplate": "Swap link template",
|
||||
"paymentLinkTemplate": "Payment link template",
|
||||
"inviteLinkTemplate": "Invite link template",
|
||||
|
||||
@@ -3,7 +3,9 @@ import { AdminStore } from "@medusajs/types"
|
||||
import { Badge, Container, Heading, Text } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { Link } from "react-router-dom"
|
||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||
import { useSalesChannel, useStockLocation } from "../../../../../hooks/api"
|
||||
import { useRegion } from "../../../../../hooks/api/regions"
|
||||
|
||||
type StoreGeneralSectionProps = {
|
||||
@@ -19,6 +21,20 @@ export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => {
|
||||
|
||||
const defaultCurrency = store.supported_currencies?.find((c) => c.is_default)
|
||||
|
||||
const { sales_channel } = useSalesChannel(store.default_sales_channel_id!, {
|
||||
enabled: !!store.default_sales_channel_id,
|
||||
})
|
||||
|
||||
const { stock_location } = useStockLocation(
|
||||
store.default_location_id!,
|
||||
{
|
||||
fields: "id,name",
|
||||
},
|
||||
{
|
||||
enabled: !!store.default_location_id,
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<Container className="divide-y p-0">
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
@@ -74,9 +90,51 @@ export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => {
|
||||
{t("store.defaultRegion")}
|
||||
</Text>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Text size="small" leading="compact">
|
||||
{region?.name || "-"}
|
||||
</Text>
|
||||
{region ? (
|
||||
<Badge size="2xsmall" asChild>
|
||||
<Link to={`/settings/regions/${region.id}`}>{region.name}</Link>
|
||||
</Badge>
|
||||
) : (
|
||||
<Text size="small" leading="compact">
|
||||
-
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-ui-fg-subtle grid grid-cols-2 px-6 py-4">
|
||||
<Text size="small" leading="compact" weight="plus">
|
||||
{t("store.defaultSalesChannel")}
|
||||
</Text>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{sales_channel ? (
|
||||
<Badge size="2xsmall" asChild>
|
||||
<Link to={`/settings/sales-channels/${sales_channel.id}`}>
|
||||
{sales_channel.name}
|
||||
</Link>
|
||||
</Badge>
|
||||
) : (
|
||||
<Text size="small" leading="compact">
|
||||
-
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-ui-fg-subtle grid grid-cols-2 px-6 py-4">
|
||||
<Text size="small" leading="compact" weight="plus">
|
||||
{t("store.defaultLocation")}
|
||||
</Text>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{stock_location ? (
|
||||
<Badge size="2xsmall" asChild>
|
||||
<Link to={`/settings/locations/${stock_location.id}`}>
|
||||
{stock_location.name}
|
||||
</Link>
|
||||
</Badge>
|
||||
) : (
|
||||
<Text size="small" leading="compact">
|
||||
-
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -6,10 +6,12 @@ import { useTranslation } from "react-i18next"
|
||||
import { z } from "zod"
|
||||
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import { Combobox } from "../../../../../components/inputs/combobox"
|
||||
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
||||
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
|
||||
import { useRegions } from "../../../../../hooks/api/regions"
|
||||
import { useUpdateStore } from "../../../../../hooks/api/store"
|
||||
import { useComboboxData } from "../../../../../hooks/use-combobox-data"
|
||||
import { sdk } from "../../../../../lib/client"
|
||||
|
||||
type EditStoreFormProps = {
|
||||
store: HttpTypes.AdminStore
|
||||
@@ -19,7 +21,8 @@ const EditStoreSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
default_currency_code: z.string().optional(),
|
||||
default_region_id: z.string().optional(),
|
||||
// default_location_id: z.string().optional(),
|
||||
default_sales_channel_id: z.string().optional(),
|
||||
default_location_id: z.string().optional(),
|
||||
})
|
||||
|
||||
export const EditStoreForm = ({ store }: EditStoreFormProps) => {
|
||||
@@ -33,21 +36,49 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => {
|
||||
default_currency_code:
|
||||
store.supported_currencies?.find((c) => c.is_default)?.currency_code ||
|
||||
undefined,
|
||||
default_sales_channel_id: store.default_sales_channel_id || undefined,
|
||||
default_location_id: store.default_location_id || undefined,
|
||||
},
|
||||
resolver: zodResolver(EditStoreSchema),
|
||||
})
|
||||
|
||||
const { mutateAsync, isPending } = useUpdateStore(store.id)
|
||||
|
||||
const { regions, isPending: isRegionsLoading } = useRegions({ limit: 999 })
|
||||
const regionsCombobox = useComboboxData({
|
||||
queryKey: ["regions", "default_region_id"],
|
||||
queryFn: (params) =>
|
||||
sdk.admin.region.list({ ...params, fields: "id,name" }),
|
||||
defaultValue: store.default_region_id || undefined,
|
||||
getOptions: (data) =>
|
||||
data.regions.map((r) => ({ label: r.name, value: r.id })),
|
||||
})
|
||||
|
||||
const salesChannelsCombobox = useComboboxData({
|
||||
queryFn: (params) =>
|
||||
sdk.admin.salesChannel.list({ ...params, fields: "id,name" }),
|
||||
getOptions: (data) =>
|
||||
data.sales_channels.map((sc) => ({ label: sc.name, value: sc.id })),
|
||||
queryKey: ["sales_channels", "default_sales_channel_id"],
|
||||
defaultValue: store.default_sales_channel_id || undefined,
|
||||
})
|
||||
|
||||
const locationsCombobox = useComboboxData({
|
||||
queryFn: (params) =>
|
||||
sdk.admin.stockLocation.list({ ...params, fields: "id,name" }),
|
||||
getOptions: (data) =>
|
||||
data.stock_locations.map((l) => ({ label: l.name, value: l.id })),
|
||||
queryKey: ["stock_locations", "default_location_id"],
|
||||
defaultValue: store.default_location_id || undefined,
|
||||
})
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (values) => {
|
||||
const normalizedMutation = {
|
||||
...values,
|
||||
default_currency_code: undefined,
|
||||
const { default_currency_code, ...rest } = values
|
||||
|
||||
const normalizedMutation: HttpTypes.AdminUpdateStore = {
|
||||
...rest,
|
||||
supported_currencies: store.supported_currencies?.map((c) => ({
|
||||
...c,
|
||||
is_default: c.currency_code === values.default_currency_code,
|
||||
is_default: c.currency_code === default_currency_code,
|
||||
})),
|
||||
}
|
||||
await mutateAsync(normalizedMutation, {
|
||||
@@ -79,7 +110,6 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => {
|
||||
</Form.Item>
|
||||
)}
|
||||
/>
|
||||
{/* TODO: Add comboboxes for default sales channel and location */}
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="default_currency_code"
|
||||
@@ -111,27 +141,64 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => {
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="default_region_id"
|
||||
render={({ field: { onChange, ...field } }) => {
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.Label>{t("store.defaultRegion")}</Form.Label>
|
||||
<Form.Control>
|
||||
<Select
|
||||
<Combobox
|
||||
{...field}
|
||||
onValueChange={onChange}
|
||||
disabled={isRegionsLoading}
|
||||
>
|
||||
<Select.Trigger ref={field.ref}>
|
||||
<Select.Value />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{(regions || []).map((region) => (
|
||||
<Select.Item key={region.id} value={region.id}>
|
||||
{region.name}
|
||||
</Select.Item>
|
||||
))}
|
||||
</Select.Content>
|
||||
</Select>
|
||||
options={regionsCombobox.options}
|
||||
searchValue={regionsCombobox.searchValue}
|
||||
onSearchValueChange={
|
||||
regionsCombobox.onSearchValueChange
|
||||
}
|
||||
disabled={regionsCombobox.disabled}
|
||||
/>
|
||||
</Form.Control>
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="default_sales_channel_id"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.Label>{t("store.defaultSalesChannel")}</Form.Label>
|
||||
<Form.Control>
|
||||
<Combobox
|
||||
{...field}
|
||||
options={salesChannelsCombobox.options}
|
||||
searchValue={salesChannelsCombobox.searchValue}
|
||||
onSearchValueChange={
|
||||
salesChannelsCombobox.onSearchValueChange
|
||||
}
|
||||
disabled={salesChannelsCombobox.disabled}
|
||||
/>
|
||||
</Form.Control>
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="default_location_id"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.Label>{t("store.defaultLocation")}</Form.Label>
|
||||
<Form.Control>
|
||||
<Combobox
|
||||
{...field}
|
||||
options={locationsCombobox.options}
|
||||
searchValue={locationsCombobox.searchValue}
|
||||
onSearchValueChange={
|
||||
locationsCombobox.onSearchValueChange
|
||||
}
|
||||
disabled={locationsCombobox.disabled}
|
||||
/>
|
||||
</Form.Control>
|
||||
</Form.Item>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user