refactor(dashboard): refresh domains (#7087)
This commit is contained in:
1 parent
40686ba980
commit
fe68b5c0f2
49 files changed
+624
-228
No files matched your search
@@ -280,7 +280,12 @@
|
|||||||
"manageInventoryLabel": "Manage inventory",
|
"manageInventoryLabel": "Manage inventory",
|
||||||
"manageInventoryHint": "When enabled the inventory level will be regulated when orders and returns are created.",
|
"manageInventoryHint": "When enabled the inventory level will be regulated when orders and returns are created.",
|
||||||
"allowBackordersLabel": "Allow backorders",
|
"allowBackordersLabel": "Allow backorders",
|
||||||
"allowBackordersHint": "When enabled the variant can be sold even if the inventory level is below zero."
|
"allowBackordersHint": "When enabled the variant can be sold even if the inventory level is below zero.",
|
||||||
|
"toast": {
|
||||||
|
"levelsBatch": "Inventory levels updated.",
|
||||||
|
"update": "Inventory item updated successfully",
|
||||||
|
"updateLevel": "Inventory level updated successfully"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -1018,6 +1023,9 @@
|
|||||||
"languageHint": "The language you want to use in the admin dashboard. This will not change the language of your store.",
|
"languageHint": "The language you want to use in the admin dashboard. This will not change the language of your store.",
|
||||||
"languagePlaceholder": "Select language",
|
"languagePlaceholder": "Select language",
|
||||||
"usageInsightsHint": "Share usage insights and help us improve Medusa. You can read more about what we collect and how we use it in our <0>documentation</0>."
|
"usageInsightsHint": "Share usage insights and help us improve Medusa. You can read more about what we collect and how we use it in our <0>documentation</0>."
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"edit": "Profiles changes saved"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
@@ -1051,6 +1059,7 @@
|
|||||||
"manageYourStoresDetails": "Manage your store's details",
|
"manageYourStoresDetails": "Manage your store's details",
|
||||||
"editStore": "Edit store",
|
"editStore": "Edit store",
|
||||||
"defaultCurrency": "Default currency",
|
"defaultCurrency": "Default currency",
|
||||||
|
"defaultRegion": "Default region",
|
||||||
"swapLinkTemplate": "Swap link template",
|
"swapLinkTemplate": "Swap link template",
|
||||||
"paymentLinkTemplate": "Payment link template",
|
"paymentLinkTemplate": "Payment link template",
|
||||||
"inviteLinkTemplate": "Invite link template",
|
"inviteLinkTemplate": "Invite link template",
|
||||||
@@ -1061,6 +1070,11 @@
|
|||||||
"currencyAlreadyAdded": "The currency has already been added to your store.",
|
"currencyAlreadyAdded": "The currency has already been added to your store.",
|
||||||
"edit": {
|
"edit": {
|
||||||
"header": "Edit Store"
|
"header": "Edit Store"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"update": "Store successfully updated",
|
||||||
|
"currenciesUpdated": "Currencies updated successfully",
|
||||||
|
"currenciesRemoved": "Removed currencies from the store successfully"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regions": {
|
"regions": {
|
||||||
@@ -1171,7 +1185,12 @@
|
|||||||
"selectLocations": "Select locations that stock the item.",
|
"selectLocations": "Select locations that stock the item.",
|
||||||
"deleteLocationWarning": "You are about to delete the location {{name}}. This action cannot be undone.",
|
"deleteLocationWarning": "You are about to delete the location {{name}}. This action cannot be undone.",
|
||||||
"removeSalesChannelsWarning_one": "You are about to remove {{count}} sales channel from the location.",
|
"removeSalesChannelsWarning_one": "You are about to remove {{count}} sales channel from the location.",
|
||||||
"removeSalesChannelsWarning_other": "You are about to remove {{count}} sales channels from the location."
|
"removeSalesChannelsWarning_other": "You are about to remove {{count}} sales channels from the location.",
|
||||||
|
"toast": {
|
||||||
|
"create": "Location created sucessfully",
|
||||||
|
"update": "Location updated sucessfully",
|
||||||
|
"removeChannel": "Sales channel removed sucessfully"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"reservations": {
|
"reservations": {
|
||||||
"domain": "Reservations",
|
"domain": "Reservations",
|
||||||
@@ -1187,7 +1206,12 @@
|
|||||||
"addProducts": "Add Products",
|
"addProducts": "Add Products",
|
||||||
"editSalesChannel": "Edit sales channel",
|
"editSalesChannel": "Edit sales channel",
|
||||||
"productAlreadyAdded": "The product has already been added to the sales channel.",
|
"productAlreadyAdded": "The product has already been added to the sales channel.",
|
||||||
"deleteSalesChannelWarning": "You are about to delete the sales channel {{name}}. This action cannot be undone."
|
"deleteSalesChannelWarning": "You are about to delete the sales channel {{name}}. This action cannot be undone.",
|
||||||
|
"toast": {
|
||||||
|
"create": "Sales channel created successfully",
|
||||||
|
"update": "Sales channel updated successfully",
|
||||||
|
"delete": "Sales channel deleted successfully"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"apiKeyManagement": {
|
"apiKeyManagement": {
|
||||||
"domain": {
|
"domain": {
|
||||||
@@ -1279,7 +1303,10 @@
|
|||||||
"successAction": "Sign in to start using Medusa",
|
"successAction": "Sign in to start using Medusa",
|
||||||
"invalidTokenTitle": "Your invite token is invalid",
|
"invalidTokenTitle": "Your invite token is invalid",
|
||||||
"invalidTokenHint": "Try requesting a new invite link.",
|
"invalidTokenHint": "Try requesting a new invite link.",
|
||||||
"passwordMismatch": "Passwords do not match"
|
"passwordMismatch": "Passwords do not match",
|
||||||
|
"toast": {
|
||||||
|
"accepted": "Invite successfully accepted"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resetPassword": {
|
"resetPassword": {
|
||||||
"title": "Reset password",
|
"title": "Reset password",
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ export type InventoryItemRes = {
|
|||||||
stocked_quantity: number
|
stocked_quantity: number
|
||||||
reserved_quantity: number
|
reserved_quantity: number
|
||||||
location_levels?: InventoryNext.InventoryLevelDTO[]
|
location_levels?: InventoryNext.InventoryLevelDTO[]
|
||||||
|
variant?: ProductVariantDTO | ProductVariantDTO[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ export const AdjustInventoryDrawer = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
inventory_item: inventoryItem,
|
inventory_item: inventoryItem,
|
||||||
isLoading,
|
isPending: isLoading,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
} = useInventoryItem(id!)
|
} = useInventoryItem(id!)
|
||||||
|
|||||||
+18
-6
@@ -1,6 +1,6 @@
|
|||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { Button, Input, Text } from "@medusajs/ui"
|
import { Button, Input, Text, toast } from "@medusajs/ui"
|
||||||
import { InventoryLevelDTO, StockLocationDTO } from "@medusajs/types"
|
import { InventoryLevelDTO, StockLocationDTO } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
@@ -61,7 +61,7 @@ export const AdjustInventoryForm = ({
|
|||||||
|
|
||||||
const stockedQuantityUpdate = form.watch("stocked_quantity")
|
const stockedQuantityUpdate = form.watch("stocked_quantity")
|
||||||
|
|
||||||
const { mutateAsync } = useUpdateInventoryItemLevel(
|
const { mutateAsync, isPending: isLoading } = useUpdateInventoryItemLevel(
|
||||||
item.id,
|
item.id,
|
||||||
level.location_id
|
level.location_id
|
||||||
)
|
)
|
||||||
@@ -71,9 +71,21 @@ export const AdjustInventoryForm = ({
|
|||||||
return handleSuccess()
|
return handleSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync({
|
try {
|
||||||
stocked_quantity: value.stocked_quantity,
|
await mutateAsync({
|
||||||
})
|
stocked_quantity: value.stocked_quantity,
|
||||||
|
})
|
||||||
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("inventory.toast.updateLevel"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return handleSuccess()
|
return handleSuccess()
|
||||||
})
|
})
|
||||||
@@ -141,7 +153,7 @@ export const AdjustInventoryForm = ({
|
|||||||
{t("actions.cancel")}
|
{t("actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</RouteDrawer.Close>
|
</RouteDrawer.Close>
|
||||||
<Button type="submit" size="small" isLoading={false}>
|
<Button type="submit" size="small" isLoading={isLoading}>
|
||||||
{t("actions.save")}
|
{t("actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+13
-4
@@ -1,6 +1,6 @@
|
|||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { Button, Input } from "@medusajs/ui"
|
import { Button, Input, toast } from "@medusajs/ui"
|
||||||
import {
|
import {
|
||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
@@ -52,13 +52,22 @@ export const EditInventoryItemAttributesForm = ({
|
|||||||
resolver: zodResolver(EditInventoryItemAttributesSchema),
|
resolver: zodResolver(EditInventoryItemAttributesSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync } = useUpdateInventoryItem(item.id)
|
const { mutateAsync, isPending: isLoading } = useUpdateInventoryItem(item.id)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
mutateAsync(values, {
|
await mutateAsync(values, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("inventory.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
handleSuccess()
|
handleSuccess()
|
||||||
},
|
},
|
||||||
|
onError: (error) =>
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: error.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -241,7 +250,7 @@ export const EditInventoryItemAttributesForm = ({
|
|||||||
{t("actions.cancel")}
|
{t("actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</RouteDrawer.Close>
|
</RouteDrawer.Close>
|
||||||
<Button type="submit" size="small" isLoading={false}>
|
<Button type="submit" size="small" isLoading={isLoading}>
|
||||||
{t("actions.save")}
|
{t("actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ export const InventoryItemAttributesEdit = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
inventory_item: inventoryItem,
|
inventory_item: inventoryItem,
|
||||||
isLoading,
|
isPending: isLoading,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
} = useInventoryItem(id!)
|
} = useInventoryItem(id!)
|
||||||
|
|||||||
+12
-3
@@ -1,6 +1,6 @@
|
|||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { Button, Input } from "@medusajs/ui"
|
import { Button, Input, toast } from "@medusajs/ui"
|
||||||
import {
|
import {
|
||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
@@ -39,13 +39,22 @@ export const EditInventoryItemForm = ({ item }: EditInventoryItemFormProps) => {
|
|||||||
resolver: zodResolver(EditInventoryItemSchema),
|
resolver: zodResolver(EditInventoryItemSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync } = useUpdateInventoryItem(item.id)
|
const { mutateAsync, isPending: isLoading } = useUpdateInventoryItem(item.id)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
mutateAsync(values as any, {
|
mutateAsync(values as any, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("inventory.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
handleSuccess()
|
handleSuccess()
|
||||||
},
|
},
|
||||||
|
onError: (e) =>
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,7 +103,7 @@ export const EditInventoryItemForm = ({ item }: EditInventoryItemFormProps) => {
|
|||||||
{t("actions.cancel")}
|
{t("actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</RouteDrawer.Close>
|
</RouteDrawer.Close>
|
||||||
<Button type="submit" size="small" isLoading={false}>
|
<Button type="submit" size="small" isLoading={isLoading}>
|
||||||
{t("actions.save")}
|
{t("actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ export const InventoryItemEdit = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
inventory_item: inventoryItem,
|
inventory_item: inventoryItem,
|
||||||
isLoading,
|
isPending: isLoading,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
} = useInventoryItem(id!)
|
} = useInventoryItem(id!)
|
||||||
|
|||||||
+5
-5
@@ -1,5 +1,5 @@
|
|||||||
import { Container, Heading, Text } from "@medusajs/ui"
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
import { InventoryNext, ProductVariantDTO } from "@medusajs/types"
|
import { ProductVariantDTO } from "@medusajs/types"
|
||||||
|
|
||||||
import { ActionMenu } from "../../../../components/common/action-menu"
|
import { ActionMenu } from "../../../../components/common/action-menu"
|
||||||
import { InventoryItemRes } from "../../../../types/api-responses"
|
import { InventoryItemRes } from "../../../../types/api-responses"
|
||||||
@@ -52,7 +52,7 @@ export const InventoryItemGeneralSection = ({
|
|||||||
title={t("fields.inStock")}
|
title={t("fields.inStock")}
|
||||||
value={getQuantityFormat(
|
value={getQuantityFormat(
|
||||||
inventoryItem.stocked_quantity,
|
inventoryItem.stocked_quantity,
|
||||||
inventoryItem.location_levels.length
|
inventoryItem.location_levels?.length
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -60,14 +60,14 @@ export const InventoryItemGeneralSection = ({
|
|||||||
title={t("inventory.reserved")}
|
title={t("inventory.reserved")}
|
||||||
value={getQuantityFormat(
|
value={getQuantityFormat(
|
||||||
inventoryItem.reserved_quantity,
|
inventoryItem.reserved_quantity,
|
||||||
inventoryItem.location_levels.length
|
inventoryItem.location_levels?.length
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<SectionRow
|
<SectionRow
|
||||||
title={t("inventory.available")}
|
title={t("inventory.available")}
|
||||||
value={getQuantityFormat(
|
value={getQuantityFormat(
|
||||||
inventoryItem.stocked_quantity - inventoryItem.reserved_quantity,
|
inventoryItem.stocked_quantity - inventoryItem.reserved_quantity,
|
||||||
inventoryItem.location_levels.length
|
inventoryItem.location_levels?.length
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@ import { Button, Container, Heading } from "@medusajs/ui"
|
|||||||
import { InventoryItemRes } from "../../../../types/api-responses"
|
import { InventoryItemRes } from "../../../../types/api-responses"
|
||||||
import { ItemLocationListTable } from "./location-levels-table/location-list-table"
|
import { ItemLocationListTable } from "./location-levels-table/location-list-table"
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
import { ReservationItemTable } from "./reservations-table/reservation-list-table"
|
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
type InventoryItemLocationLevelsSectionProps = {
|
type InventoryItemLocationLevelsSectionProps = {
|
||||||
|
|||||||
+10
-5
@@ -15,11 +15,16 @@ export const ItemLocationListTable = ({
|
|||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { inventory_levels, count, isLoading, isError, error } =
|
const {
|
||||||
useInventoryItemLevels(inventory_item_id, {
|
inventory_levels,
|
||||||
...searchParams,
|
count,
|
||||||
fields: "*stock_locations",
|
isPending: isLoading,
|
||||||
})
|
isError,
|
||||||
|
error,
|
||||||
|
} = useInventoryItemLevels(inventory_item_id, {
|
||||||
|
...searchParams,
|
||||||
|
fields: "*stock_locations",
|
||||||
|
})
|
||||||
|
|
||||||
const columns = useLocationListTableColumns()
|
const columns = useLocationListTableColumns()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ export const LocationItem = ({
|
|||||||
}: LocationItemProps) => {
|
}: LocationItemProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clx("flex w-full rounded-lg border px-2 py-2 gap-x-2", {
|
className={clx("flex w-full gap-x-2 rounded-lg border px-2 py-2", {
|
||||||
"border-ui-border-interactive ": selected,
|
"border-ui-border-interactive ": selected,
|
||||||
})}
|
})}
|
||||||
onClick={() => onSelect(!selected)}
|
onClick={() => onSelect(!selected)}
|
||||||
|
|||||||
+25
-16
@@ -1,14 +1,11 @@
|
|||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { Button, Table, Text } from "@medusajs/ui"
|
import { Button, Text, toast } from "@medusajs/ui"
|
||||||
import {
|
import {
|
||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../../components/route-modal"
|
} from "../../../../../../components/route-modal"
|
||||||
import {
|
import { useBatchInventoryItemLevels } from "../../../../../../hooks/api/inventory"
|
||||||
useBatchInventoryItemLevels,
|
|
||||||
useUpdateInventoryItem,
|
|
||||||
} from "../../../../../../hooks/api/inventory"
|
|
||||||
import { useFieldArray, useForm } from "react-hook-form"
|
import { useFieldArray, useForm } from "react-hook-form"
|
||||||
|
|
||||||
import { InventoryItemRes } from "../../../../../../types/api-responses"
|
import { InventoryItemRes } from "../../../../../../types/api-responses"
|
||||||
@@ -35,13 +32,13 @@ const EditInventoryItemAttributesSchema = z.object({
|
|||||||
|
|
||||||
const getDefaultValues = (
|
const getDefaultValues = (
|
||||||
allLocations: StockLocationDTO[],
|
allLocations: StockLocationDTO[],
|
||||||
existinLevels: Set<string>
|
existingLevels: Set<string>
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
locations: allLocations.map((location) => ({
|
locations: allLocations.map((location) => ({
|
||||||
...location,
|
...location,
|
||||||
location_id: location.id,
|
location_id: location.id,
|
||||||
selected: existinLevels.has(location.id),
|
selected: existingLevels.has(location.id),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,14 +94,26 @@ export const ManageLocationsForm = ({
|
|||||||
return handleSuccess()
|
return handleSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync({
|
try {
|
||||||
creates: selectedLocations.map((location_id) => ({
|
await mutateAsync({
|
||||||
location_id,
|
creates: selectedLocations.map((location_id) => ({
|
||||||
})),
|
location_id,
|
||||||
deletes: unselectedLocations,
|
})),
|
||||||
})
|
deletes: unselectedLocations,
|
||||||
|
})
|
||||||
|
|
||||||
return handleSuccess()
|
handleSuccess()
|
||||||
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("inventory.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -114,7 +123,7 @@ export const ManageLocationsForm = ({
|
|||||||
className="flex flex-1 flex-col overflow-hidden"
|
className="flex flex-1 flex-col overflow-hidden"
|
||||||
>
|
>
|
||||||
<RouteDrawer.Body className="flex flex-1 flex-col gap-y-4 overflow-auto">
|
<RouteDrawer.Body className="flex flex-1 flex-col gap-y-4 overflow-auto">
|
||||||
<div className="grid grid-rows-2 divide-y rounded-lg border text-ui-fg-subtle shadow-elevation-card-rest">
|
<div className="text-ui-fg-subtle shadow-elevation-card-rest grid grid-rows-2 divide-y rounded-lg border">
|
||||||
<div className="grid grid-cols-2 divide-x">
|
<div className="grid grid-cols-2 divide-x">
|
||||||
<Text className="px-2 py-1.5" size="small" leading="compact">
|
<Text className="px-2 py-1.5" size="small" leading="compact">
|
||||||
{t("fields.title")}
|
{t("fields.title")}
|
||||||
@@ -136,7 +145,7 @@ export const ManageLocationsForm = ({
|
|||||||
<Text size="small" weight="plus" leading="compact">
|
<Text size="small" weight="plus" leading="compact">
|
||||||
{t("locations.domain")}
|
{t("locations.domain")}
|
||||||
</Text>
|
</Text>
|
||||||
<div className="flex w-full justify-between text-ui-fg-subtle">
|
<div className="text-ui-fg-subtle flex w-full justify-between">
|
||||||
<Text size="small" leading="compact">
|
<Text size="small" leading="compact">
|
||||||
{t("locations.selectLocations")}
|
{t("locations.selectLocations")}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ export const ManageLocationsDrawer = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
inventory_item: inventoryItem,
|
inventory_item: inventoryItem,
|
||||||
isLoading,
|
isPending: isLoading,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
} = useInventoryItem(id!)
|
} = useInventoryItem(id!)
|
||||||
|
|||||||
+10
-5
@@ -16,11 +16,16 @@ export const ReservationItemTable = ({
|
|||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { reservations, count, isLoading, isError, error } =
|
const {
|
||||||
useReservationItems({
|
reservations,
|
||||||
...searchParams,
|
count,
|
||||||
inventory_item_id: [inventoryItem.id],
|
isPending: isLoading,
|
||||||
})
|
isError,
|
||||||
|
error,
|
||||||
|
} = useReservationItems({
|
||||||
|
...searchParams,
|
||||||
|
inventory_item_id: [inventoryItem.id],
|
||||||
|
})
|
||||||
|
|
||||||
const columns = useInventoryTableColumns({ sku: inventoryItem.sku! })
|
const columns = useInventoryTableColumns({ sku: inventoryItem.sku! })
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { InventoryDeatil as Component } from "./inventory-detail"
|
export { InventoryDetail as Component } from "./inventory-detail"
|
||||||
export { inventoryItemLoader as loader } from "./loader"
|
export { inventoryItemLoader as loader } from "./loader"
|
||||||
+13
-6
@@ -5,19 +5,26 @@ import { InventoryItemGeneralSection } from "./components/inventory-item-general
|
|||||||
import { InventoryItemLocationLevelsSection } from "./components/inventory-item-location-levels"
|
import { InventoryItemLocationLevelsSection } from "./components/inventory-item-location-levels"
|
||||||
import { InventoryItemReservationsSection } from "./components/inventory-item-reservations"
|
import { InventoryItemReservationsSection } from "./components/inventory-item-reservations"
|
||||||
import { JsonViewSection } from "../../../components/common/json-view-section"
|
import { JsonViewSection } from "../../../components/common/json-view-section"
|
||||||
import { inventoryItemLoader } from "./loader"
|
|
||||||
import { useInventoryItem } from "../../../hooks/api/inventory"
|
import { useInventoryItem } from "../../../hooks/api/inventory"
|
||||||
|
import { inventoryItemLoader } from "./loader"
|
||||||
|
|
||||||
export const InventoryDeatil = () => {
|
export const InventoryDetail = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
|
|
||||||
const initialData = useLoaderData() as Awaited<
|
const initialData = useLoaderData() as Awaited<
|
||||||
ReturnType<typeof inventoryItemLoader>
|
ReturnType<typeof inventoryItemLoader>
|
||||||
>
|
>
|
||||||
|
|
||||||
const { inventory_item, isLoading, isError, error } = useInventoryItem(
|
const {
|
||||||
|
inventory_item,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useInventoryItem(
|
||||||
id!,
|
id!,
|
||||||
{},
|
{
|
||||||
|
fields: "*variants",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
initialData,
|
initialData,
|
||||||
}
|
}
|
||||||
@@ -38,7 +45,7 @@ export const InventoryDeatil = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
<div className="flex flex-col gap-x-4 lg:flex-row lg:items-start">
|
<div className="flex flex-col gap-x-4 lg:flex-row lg:items-start">
|
||||||
<div className="w-full flex flex-col gap-y-2">
|
<div className="flex w-full flex-col gap-y-2">
|
||||||
<InventoryItemGeneralSection inventoryItem={inventory_item} />
|
<InventoryItemGeneralSection inventoryItem={inventory_item} />
|
||||||
<InventoryItemLocationLevelsSection inventoryItem={inventory_item} />
|
<InventoryItemLocationLevelsSection inventoryItem={inventory_item} />
|
||||||
<InventoryItemReservationsSection inventoryItem={inventory_item} />
|
<InventoryItemReservationsSection inventoryItem={inventory_item} />
|
||||||
@@ -47,7 +54,7 @@ export const InventoryDeatil = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full lg:max-w-[400px] max-w-[100%] mt-2 lg:mt-0 flex flex-col gap-y-2">
|
<div className="mt-2 flex w-full max-w-[100%] flex-col gap-y-2 lg:mt-0 lg:max-w-[400px]">
|
||||||
<InventoryItemAttributeSection inventoryItem={inventory_item} />
|
<InventoryItemAttributeSection inventoryItem={inventory_item} />
|
||||||
<div className="lg:hidden">
|
<div className="lg:hidden">
|
||||||
<JsonViewSection data={inventory_item} />
|
<JsonViewSection data={inventory_item} />
|
||||||
|
|||||||
+11
-5
@@ -1,7 +1,7 @@
|
|||||||
import { Container, Heading } from "@medusajs/ui"
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
|
import { InventoryNext } from "@medusajs/types"
|
||||||
|
|
||||||
import { DataTable } from "../../../../components/table/data-table"
|
import { DataTable } from "../../../../components/table/data-table"
|
||||||
import { InventoryNext } from "@medusajs/types"
|
|
||||||
import { useDataTable } from "../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../hooks/use-data-table"
|
||||||
import { useInventoryItems } from "../../../../hooks/api/inventory"
|
import { useInventoryItems } from "../../../../hooks/api/inventory"
|
||||||
import { useInventoryTableColumns } from "./use-inventory-table-columns"
|
import { useInventoryTableColumns } from "./use-inventory-table-columns"
|
||||||
@@ -17,10 +17,16 @@ export const InventoryListTable = () => {
|
|||||||
const { searchParams, raw } = useInventoryTableQuery({
|
const { searchParams, raw } = useInventoryTableQuery({
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
const { inventory_items, count, isLoading, isError, error } =
|
|
||||||
useInventoryItems({
|
const {
|
||||||
...searchParams,
|
inventory_items,
|
||||||
})
|
count,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useInventoryItems({
|
||||||
|
...searchParams,
|
||||||
|
})
|
||||||
|
|
||||||
const filters = useInventoryTableFilters()
|
const filters = useInventoryTableFilters()
|
||||||
const columns = useInventoryTableColumns()
|
const columns = useInventoryTableColumns()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { UserRoles } from "@medusajs/medusa"
|
import { UserRoles } from "@medusajs/medusa"
|
||||||
import { Alert, Button, Heading, Input, Text } from "@medusajs/ui"
|
import { Alert, Button, Heading, Input, Text, toast } from "@medusajs/ui"
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
import { Trans, useTranslation } from "react-i18next"
|
import { Trans, useTranslation } from "react-i18next"
|
||||||
import { Link, useSearchParams } from "react-router-dom"
|
import { Link, useSearchParams } from "react-router-dom"
|
||||||
@@ -49,7 +49,7 @@ export const Invite = () => {
|
|||||||
const isValidInvite = invite && validateDecodedInvite(invite)
|
const isValidInvite = invite && validateDecodedInvite(invite)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-dvh w-dvw bg-ui-bg-base relative flex items-center justify-center p-4">
|
<div className="bg-ui-bg-base relative flex min-h-dvh w-dvw items-center justify-center p-4">
|
||||||
<div className="flex w-full max-w-[300px] flex-col items-center">
|
<div className="flex w-full max-w-[300px] flex-col items-center">
|
||||||
<LogoBox
|
<LogoBox
|
||||||
className="mb-4"
|
className="mb-4"
|
||||||
@@ -204,10 +204,10 @@ const CreateView = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync: createAuthUser, isLoading: isCreatingAuthUser } =
|
const { mutateAsync: createAuthUser, isPending: isCreatingAuthUser } =
|
||||||
useV2CreateAuthUser()
|
useV2CreateAuthUser()
|
||||||
|
|
||||||
const { mutateAsync: acceptInvite, isLoading: isAcceptingInvite } =
|
const { mutateAsync: acceptInvite, isPending: isAcceptingInvite } =
|
||||||
useV2AcceptInvite(token)
|
useV2AcceptInvite(token)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (data) => {
|
const handleSubmit = form.handleSubmit(async (data) => {
|
||||||
@@ -228,6 +228,10 @@ const CreateView = ({
|
|||||||
token: authToken,
|
token: authToken,
|
||||||
})
|
})
|
||||||
onSuccess()
|
onSuccess()
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("invite.toast.accepted"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isAxiosError(error) && error.response?.status === 400) {
|
if (isAxiosError(error) && error.response?.status === 400) {
|
||||||
form.setError("root", {
|
form.setError("root", {
|
||||||
|
|||||||
+17
-10
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Heading, Input, Text } from "@medusajs/ui"
|
import { Button, Heading, Input, Text, toast } from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
@@ -49,17 +49,24 @@ export const CreateLocationForm = () => {
|
|||||||
const { mutateAsync, isPending } = useCreateStockLocation()
|
const { mutateAsync, isPending } = useCreateStockLocation()
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({
|
||||||
name: values.name,
|
name: values.name,
|
||||||
address: values.address,
|
address: values.address,
|
||||||
},
|
})
|
||||||
{
|
|
||||||
onSuccess: () => {
|
handleSuccess("/settings/locations")
|
||||||
handleSuccess("/settings/locations")
|
|
||||||
},
|
toast.success(t("general.success"), {
|
||||||
}
|
description: t("locations.toast.create"),
|
||||||
)
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+17
-5
@@ -1,6 +1,6 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { SalesChannelDTO } from "@medusajs/types"
|
import { SalesChannelDTO } from "@medusajs/types"
|
||||||
import { Button, Container, Heading, usePrompt } from "@medusajs/ui"
|
import { Button, Container, Heading, toast, usePrompt } from "@medusajs/ui"
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useAdminRemoveLocationFromSalesChannel } from "medusa-react"
|
import { useAdminRemoveLocationFromSalesChannel } from "medusa-react"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
@@ -82,10 +82,22 @@ const SalesChannelActions = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync({
|
try {
|
||||||
location_id: locationId,
|
await mutateAsync({
|
||||||
sales_channel_id: salesChannel.id,
|
location_id: locationId,
|
||||||
})
|
sales_channel_id: salesChannel.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("locations.toast.removeChannel"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+6
-1
@@ -7,7 +7,12 @@ import { LocationSalesChannelSection } from "./components/location-sales-channel
|
|||||||
|
|
||||||
export const LocationDetail = () => {
|
export const LocationDetail = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { stock_location, isLoading, isError, error } = useStockLocation(id!, {
|
const {
|
||||||
|
stock_location,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useStockLocation(id!, {
|
||||||
fields: "*address,*sales_channels",
|
fields: "*address,*sales_channels",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+16
-10
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Input } from "@medusajs/ui"
|
import { Button, Input, toast } from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
@@ -55,17 +55,23 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => {
|
|||||||
const { mutateAsync, isPending } = useUpdateStockLocation(location.id)
|
const { mutateAsync, isPending } = useUpdateStockLocation(location.id)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({
|
||||||
name: values.name,
|
name: values.name,
|
||||||
address: values.address,
|
address: values.address,
|
||||||
},
|
})
|
||||||
{
|
handleSuccess()
|
||||||
onSuccess: () => {
|
|
||||||
handleSuccess()
|
toast.success(t("general.success"), {
|
||||||
},
|
description: t("locations.toast.update"),
|
||||||
}
|
dismissLabel: t("actions.close"),
|
||||||
)
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+7
-5
@@ -2,14 +2,18 @@ import { Heading } from "@medusajs/ui"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
import { RouteDrawer } from "../../../components/route-modal"
|
import { RouteDrawer } from "../../../components/route-modal"
|
||||||
import { useStockLocations } from "../../../hooks/api/stock-locations"
|
import { useStockLocation } from "../../../hooks/api/stock-locations"
|
||||||
import { EditLocationForm } from "./components/edit-location-form"
|
import { EditLocationForm } from "./components/edit-location-form"
|
||||||
|
|
||||||
export const LocationEdit = () => {
|
export const LocationEdit = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
|
|
||||||
const { stock_locations, isLoading, isError, error } = useStockLocations({
|
const {
|
||||||
id,
|
stock_location,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useStockLocation(id, {
|
||||||
fields: "*address",
|
fields: "*address",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -19,8 +23,6 @@ export const LocationEdit = () => {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
const stock_location = stock_locations?.[0]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteDrawer>
|
<RouteDrawer>
|
||||||
<RouteDrawer.Header>
|
<RouteDrawer.Header>
|
||||||
|
|||||||
+13
-7
@@ -19,16 +19,21 @@ export const LocationsListTable = () => {
|
|||||||
* Note: The endpoint is bugged and does not return count, causing the table to not render
|
* Note: The endpoint is bugged and does not return count, causing the table to not render
|
||||||
* any rows.
|
* any rows.
|
||||||
*/
|
*/
|
||||||
const { stock_locations, count, isLoading, isError, error } =
|
const {
|
||||||
useStockLocations({
|
stock_locations = [],
|
||||||
...searchParams,
|
count,
|
||||||
fields: "*address",
|
isLoading,
|
||||||
})
|
isError,
|
||||||
|
error,
|
||||||
|
} = useStockLocations({
|
||||||
|
...searchParams,
|
||||||
|
fields: "*address",
|
||||||
|
})
|
||||||
|
|
||||||
const columns = useLocationTableColumns()
|
const columns = useLocationTableColumns()
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
data: stock_locations ?? [],
|
data: stock_locations,
|
||||||
columns,
|
columns,
|
||||||
count,
|
count,
|
||||||
enablePagination: true,
|
enablePagination: true,
|
||||||
@@ -56,7 +61,8 @@ export const LocationsListTable = () => {
|
|||||||
count={count || 1}
|
count={count || 1}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
navigateTo={(row) => row.id}
|
navigateTo={(row) => row.id}
|
||||||
isLoading={isLoading}
|
// TODO: revisit loader - on query change this will cause unmounting of the table, rendering loader briefly and again rendering table which will make search input unfocused
|
||||||
|
// isLoading={isLoading}
|
||||||
queryObject={raw}
|
queryObject={raw}
|
||||||
pagination
|
pagination
|
||||||
search
|
search
|
||||||
|
|||||||
+2
-1
@@ -7,11 +7,12 @@ export const useLocationTableQuery = ({
|
|||||||
pageSize?: number
|
pageSize?: number
|
||||||
prefix?: string
|
prefix?: string
|
||||||
}) => {
|
}) => {
|
||||||
const raw = useQueryParams(["offset"], prefix)
|
const raw = useQueryParams(["q", "offset"], prefix)
|
||||||
|
|
||||||
const searchParams = {
|
const searchParams = {
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
offset: raw.offset,
|
offset: raw.offset,
|
||||||
|
q: raw.q,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Heading, Input, Text } from "@medusajs/ui"
|
import { Alert, Button, Heading, Input, Text } from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { Trans, useTranslation } from "react-i18next"
|
import { Trans, useTranslation } from "react-i18next"
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom"
|
import { Link, useLocation, useNavigate } from "react-router-dom"
|
||||||
@@ -34,40 +34,38 @@ export const Login = () => {
|
|||||||
const { mutateAsync, isPending } = useEmailPassLogin()
|
const { mutateAsync, isPending } = useEmailPassLogin()
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async ({ email, password }) => {
|
const handleSubmit = form.handleSubmit(async ({ email, password }) => {
|
||||||
await mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
},
|
})
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
navigate(from, { replace: true })
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
if (isAxiosError(error)) {
|
|
||||||
if (error.response?.status === 401) {
|
|
||||||
form.setError("email", {
|
|
||||||
type: "manual",
|
|
||||||
})
|
|
||||||
|
|
||||||
form.setError("password", {
|
navigate(from, { replace: true })
|
||||||
type: "manual",
|
} catch (error) {
|
||||||
message: t("errors.invalidCredentials"),
|
if (isAxiosError(error)) {
|
||||||
})
|
if (error.response?.status === 401) {
|
||||||
|
form.setError("email", {
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
form.setError("root.serverError", {
|
|
||||||
type: "manual",
|
type: "manual",
|
||||||
message: t("errors.serverError"),
|
|
||||||
})
|
})
|
||||||
},
|
|
||||||
|
form.setError("password", {
|
||||||
|
type: "manual",
|
||||||
|
message: t("errors.invalidCredentials"),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
form.setError("root.serverError", {
|
||||||
|
type: "manual",
|
||||||
|
message: t("errors.serverError"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const serverError = form.formState.errors?.root?.serverError?.message
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-ui-bg-base flex min-h-dvh w-dvw items-center justify-center">
|
<div className="bg-ui-bg-base flex min-h-dvh w-dvw items-center justify-center">
|
||||||
<div className="m-4 flex w-full max-w-[300px] flex-col items-center">
|
<div className="m-4 flex w-full max-w-[300px] flex-col items-center">
|
||||||
@@ -123,6 +121,11 @@ export const Login = () => {
|
|||||||
{t("actions.continue")}
|
{t("actions.continue")}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
{serverError && (
|
||||||
|
<Alert className="mt-4" dismissible variant="error">
|
||||||
|
{serverError}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
<div className="my-6 h-px w-full border-b border-dotted" />
|
<div className="my-6 h-px w-full border-b border-dotted" />
|
||||||
<span className="text-ui-fg-subtle txt-small">
|
<span className="text-ui-fg-subtle txt-small">
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@ import { useMe } from "../../../hooks/api/users"
|
|||||||
import { ProfileGeneralSection } from "./components/profile-general-section"
|
import { ProfileGeneralSection } from "./components/profile-general-section"
|
||||||
|
|
||||||
export const ProfileDetail = () => {
|
export const ProfileDetail = () => {
|
||||||
const { user, isLoading, isError, error } = useMe()
|
const { user, isPending: isLoading, isError, error } = useMe()
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <div>Loading...</div>
|
return <div>Loading...</div>
|
||||||
@@ -14,7 +14,7 @@ export const ProfileDetail = () => {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
throw json("An unknown error has occured", 500)
|
throw json("An unknown error has occurred", 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+19
-14
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Input, Select, Switch } from "@medusajs/ui"
|
import { Button, Input, Select, Switch, toast } from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { Trans, useTranslation } from "react-i18next"
|
import { Trans, useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
@@ -39,8 +39,8 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => {
|
|||||||
resolver: zodResolver(EditProfileSchema),
|
resolver: zodResolver(EditProfileSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const changeLanguage = (code: string) => {
|
const changeLanguage = async (code: string) => {
|
||||||
i18n.changeLanguage(code)
|
await i18n.changeLanguage(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedLanguages = languages.sort((a, b) =>
|
const sortedLanguages = languages.sort((a, b) =>
|
||||||
@@ -50,21 +50,26 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => {
|
|||||||
const { mutateAsync, isPending } = useUpdateUser(user.id!)
|
const { mutateAsync, isPending } = useUpdateUser(user.id!)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
await mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({
|
||||||
first_name: values.first_name,
|
first_name: values.first_name,
|
||||||
last_name: values.last_name,
|
last_name: values.last_name,
|
||||||
},
|
})
|
||||||
{
|
|
||||||
onError: () => {
|
|
||||||
return
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
changeLanguage(values.language)
|
await changeLanguage(values.language)
|
||||||
|
|
||||||
handleSuccess()
|
handleSuccess()
|
||||||
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("profile.toast.edit"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useMe } from "../../../hooks/api/users"
|
|||||||
import { EditProfileForm } from "./components/edit-profile-form/edit-profile-form"
|
import { EditProfileForm } from "./components/edit-profile-form/edit-profile-form"
|
||||||
|
|
||||||
export const ProfileEdit = () => {
|
export const ProfileEdit = () => {
|
||||||
const { user, isLoading, isError, error } = useMe()
|
const { user, isPending: isLoading, isError, error } = useMe()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|||||||
+17
-2
@@ -1,6 +1,6 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { SalesChannelDTO } from "@medusajs/types"
|
import { SalesChannelDTO } from "@medusajs/types"
|
||||||
import { Button, Checkbox, Hint, Tooltip } from "@medusajs/ui"
|
import { Button, Checkbox, Hint, toast, Tooltip } from "@medusajs/ui"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
import {
|
import {
|
||||||
OnChangeFn,
|
OnChangeFn,
|
||||||
@@ -67,7 +67,13 @@ export const AddProductsToSalesChannelForm = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE })
|
const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE })
|
||||||
const { products, count, isLoading, isError, error } = useProducts(
|
const {
|
||||||
|
products,
|
||||||
|
count,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useProducts(
|
||||||
{
|
{
|
||||||
expand: "variants,sales_channels",
|
expand: "variants,sales_channels",
|
||||||
...searchParams,
|
...searchParams,
|
||||||
@@ -108,8 +114,17 @@ export const AddProductsToSalesChannelForm = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
handleSuccess()
|
handleSuccess()
|
||||||
},
|
},
|
||||||
|
onError: (error) =>
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: error.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
+6
-1
@@ -5,7 +5,12 @@ import { AddProductsToSalesChannelForm } from "./components"
|
|||||||
|
|
||||||
export const SalesChannelAddProducts = () => {
|
export const SalesChannelAddProducts = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { sales_channel, isLoading, isError, error } = useSalesChannel(id!)
|
const {
|
||||||
|
sales_channel,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useSalesChannel(id!)
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
+18
-1
@@ -1,5 +1,13 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Heading, Input, Switch, Text, Textarea } from "@medusajs/ui"
|
import {
|
||||||
|
Button,
|
||||||
|
Heading,
|
||||||
|
Input,
|
||||||
|
Switch,
|
||||||
|
Text,
|
||||||
|
Textarea,
|
||||||
|
toast,
|
||||||
|
} from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
@@ -40,8 +48,17 @@ export const CreateSalesChannelForm = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: ({ sales_channel }) => {
|
onSuccess: ({ sales_channel }) => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.create"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
handleSuccess(`../${sales_channel.id}`)
|
handleSuccess(`../${sales_channel.id}`)
|
||||||
},
|
},
|
||||||
|
onError: (error) =>
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: error.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
+23
-6
@@ -1,5 +1,12 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { Container, Heading, StatusBadge, Text, usePrompt } from "@medusajs/ui"
|
import {
|
||||||
|
Container,
|
||||||
|
Heading,
|
||||||
|
StatusBadge,
|
||||||
|
Text,
|
||||||
|
toast,
|
||||||
|
usePrompt,
|
||||||
|
} from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { SalesChannelDTO } from "@medusajs/types"
|
import { SalesChannelDTO } from "@medusajs/types"
|
||||||
@@ -36,11 +43,21 @@ export const SalesChannelGeneralSection = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync(undefined, {
|
try {
|
||||||
onSuccess: () => {
|
await mutateAsync()
|
||||||
navigate("/settings/sales-channels", { replace: true })
|
|
||||||
},
|
navigate("/settings/sales-channels", { replace: true })
|
||||||
})
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.delete"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+39
-5
@@ -1,5 +1,12 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { Button, Checkbox, Container, Heading, usePrompt } from "@medusajs/ui"
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Container,
|
||||||
|
Heading,
|
||||||
|
toast,
|
||||||
|
usePrompt,
|
||||||
|
} from "@medusajs/ui"
|
||||||
import { RowSelectionState, createColumnHelper } from "@tanstack/react-table"
|
import { RowSelectionState, createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
@@ -29,7 +36,13 @@ export const SalesChannelProductSection = ({
|
|||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
|
|
||||||
const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE })
|
const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE })
|
||||||
const { products, count, isLoading, isError, error } = useProducts(
|
const {
|
||||||
|
products,
|
||||||
|
count,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useProducts(
|
||||||
{
|
{
|
||||||
...searchParams,
|
...searchParams,
|
||||||
sales_channel_id: [salesChannel.id],
|
sales_channel_id: [salesChannel.id],
|
||||||
@@ -87,8 +100,18 @@ export const SalesChannelProductSection = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
setRowSelection({})
|
setRowSelection({})
|
||||||
},
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: error.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -199,9 +222,20 @@ const ProductListCellActions = ({
|
|||||||
const { mutateAsync } = useSalesChannelRemoveProducts(salesChannelId)
|
const { mutateAsync } = useSalesChannelRemoveProducts(salesChannelId)
|
||||||
|
|
||||||
const onRemove = async () => {
|
const onRemove = async () => {
|
||||||
await mutateAsync({
|
try {
|
||||||
product_ids: [productId],
|
await mutateAsync({
|
||||||
})
|
product_ids: [productId],
|
||||||
|
})
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ export const SalesChannelDetail = () => {
|
|||||||
>
|
>
|
||||||
|
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { sales_channel, isLoading } = useSalesChannel(id!, {
|
const { sales_channel, isPending: isLoading } = useSalesChannel(id!, {
|
||||||
initialData,
|
initialData,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Input, Switch, Textarea } from "@medusajs/ui"
|
import { Button, Input, Switch, Textarea, toast } from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
@@ -48,8 +48,18 @@ export const EditSalesChannelForm = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
handleSuccess()
|
handleSuccess()
|
||||||
},
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: error.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
+6
-1
@@ -10,7 +10,12 @@ export const SalesChannelEdit = () => {
|
|||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { sales_channel, isLoading, isError, error } = useSalesChannel(id!)
|
const {
|
||||||
|
sales_channel,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useSalesChannel(id!)
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
+22
-8
@@ -1,6 +1,6 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { SalesChannelDTO } from "@medusajs/types"
|
import { SalesChannelDTO } from "@medusajs/types"
|
||||||
import { Button, Container, Heading, usePrompt } from "@medusajs/ui"
|
import { Button, Container, Heading, toast, usePrompt } from "@medusajs/ui"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
@@ -24,12 +24,15 @@ export const SalesChannelListTable = () => {
|
|||||||
const { raw, searchParams } = useSalesChannelTableQuery({
|
const { raw, searchParams } = useSalesChannelTableQuery({
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
const { sales_channels, count, isLoading, isError, error } = useSalesChannels(
|
const {
|
||||||
searchParams,
|
sales_channels,
|
||||||
{
|
count,
|
||||||
placeholderData: keepPreviousData,
|
isPending: isLoading,
|
||||||
}
|
isError,
|
||||||
)
|
error,
|
||||||
|
} = useSalesChannels(searchParams, {
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
})
|
||||||
|
|
||||||
const columns = useColumns()
|
const columns = useColumns()
|
||||||
|
|
||||||
@@ -97,7 +100,18 @@ const SalesChannelActions = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync()
|
try {
|
||||||
|
await mutateAsync()
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("salesChannels.toast.delete"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+24
-16
@@ -1,5 +1,5 @@
|
|||||||
import { Currency } from "@medusajs/medusa"
|
import { Currency } from "@medusajs/medusa"
|
||||||
import { Button, Checkbox, Hint, Tooltip } from "@medusajs/ui"
|
import { Button, Checkbox, Hint, toast, Tooltip } from "@medusajs/ui"
|
||||||
import {
|
import {
|
||||||
OnChangeFn,
|
OnChangeFn,
|
||||||
RowSelectionState,
|
RowSelectionState,
|
||||||
@@ -68,12 +68,15 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => {
|
|||||||
prefix: PREFIX,
|
prefix: PREFIX,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { currencies, count, isLoading, isError, error } = useCurrencies(
|
const {
|
||||||
searchParams,
|
currencies,
|
||||||
{
|
count,
|
||||||
placeholderData: keepPreviousData,
|
isPending: isLoading,
|
||||||
}
|
isError,
|
||||||
)
|
error,
|
||||||
|
} = useCurrencies(searchParams, {
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
})
|
||||||
|
|
||||||
const preSelectedRows = store.supported_currency_codes.map((c) => c)
|
const preSelectedRows = store.supported_currency_codes.map((c) => c)
|
||||||
|
|
||||||
@@ -101,16 +104,21 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => {
|
|||||||
new Set([...data.currencies, ...preSelectedRows])
|
new Set([...data.currencies, ...preSelectedRows])
|
||||||
) as string[]
|
) as string[]
|
||||||
|
|
||||||
await mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({
|
||||||
supported_currency_codes: currencies,
|
supported_currency_codes: currencies,
|
||||||
},
|
})
|
||||||
{
|
toast.success(t("general.success"), {
|
||||||
onSuccess: () => {
|
description: t("store.toast.currenciesUpdated"),
|
||||||
handleSuccess()
|
dismissLabel: t("actions.close"),
|
||||||
},
|
})
|
||||||
}
|
handleSuccess()
|
||||||
)
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import { useStore } from "../../../hooks/api/store"
|
|||||||
import { AddCurrenciesForm } from "./components/add-currencies-form/add-currencies-form"
|
import { AddCurrenciesForm } from "./components/add-currencies-form/add-currencies-form"
|
||||||
|
|
||||||
export const StoreAddCurrencies = () => {
|
export const StoreAddCurrencies = () => {
|
||||||
const { store, isLoading, isError, error } = useStore()
|
const { store, isPending: isLoading, isError, error } = useStore()
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
+49
-17
@@ -5,6 +5,7 @@ import {
|
|||||||
CommandBar,
|
CommandBar,
|
||||||
Container,
|
Container,
|
||||||
Heading,
|
Heading,
|
||||||
|
toast,
|
||||||
usePrompt,
|
usePrompt,
|
||||||
} from "@medusajs/ui"
|
} from "@medusajs/ui"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
@@ -31,7 +32,13 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
|
|
||||||
const { searchParams, raw } = useCurrenciesTableQuery({ pageSize: PAGE_SIZE })
|
const { searchParams, raw } = useCurrenciesTableQuery({ pageSize: PAGE_SIZE })
|
||||||
|
|
||||||
const { currencies, count, isLoading, isError, error } = useCurrencies(
|
const {
|
||||||
|
currencies,
|
||||||
|
count,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useCurrencies(
|
||||||
{
|
{
|
||||||
code: store.supported_currency_codes,
|
code: store.supported_currency_codes,
|
||||||
...searchParams,
|
...searchParams,
|
||||||
@@ -56,8 +63,9 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
enableRowSelection: true,
|
enableRowSelection: true,
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
meta: {
|
meta: {
|
||||||
currencyCodes: store.supported_currency_codes,
|
|
||||||
storeId: store.id,
|
storeId: store.id,
|
||||||
|
currencyCodes: store.supported_currency_codes,
|
||||||
|
defaultCurrencyCode: store.default_currency_code,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -81,18 +89,24 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({
|
||||||
supported_currency_codes: store.supported_currency_codes.filter(
|
supported_currency_codes: store.supported_currency_codes.filter(
|
||||||
(c) => !ids.includes(c)
|
(c) => !ids.includes(c)
|
||||||
),
|
),
|
||||||
},
|
})
|
||||||
{
|
setRowSelection({})
|
||||||
onSuccess: () => {
|
|
||||||
setRowSelection({})
|
toast.success(t("general.success"), {
|
||||||
},
|
description: t("store.toast.currenciesRemoved"),
|
||||||
}
|
dismissLabel: t("actions.close"),
|
||||||
)
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
@@ -151,10 +165,12 @@ const CurrencyActions = ({
|
|||||||
storeId,
|
storeId,
|
||||||
currency,
|
currency,
|
||||||
currencyCodes,
|
currencyCodes,
|
||||||
|
defaultCurrencyCode,
|
||||||
}: {
|
}: {
|
||||||
storeId: string
|
storeId: string
|
||||||
currency: CurrencyDTO
|
currency: CurrencyDTO
|
||||||
currencyCodes: string[]
|
currencyCodes: string[]
|
||||||
|
defaultCurrencyCode: string
|
||||||
}) => {
|
}) => {
|
||||||
const { mutateAsync } = useUpdateStore(storeId)
|
const { mutateAsync } = useUpdateStore(storeId)
|
||||||
|
|
||||||
@@ -177,11 +193,23 @@ const CurrencyActions = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync({
|
try {
|
||||||
supported_currency_codes: currencyCodes.filter(
|
await mutateAsync({
|
||||||
(c) => c !== currency.code
|
supported_currency_codes: currencyCodes.filter(
|
||||||
),
|
(c) => c !== currency.code
|
||||||
})
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("store.toast.currenciesRemoved"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -193,6 +221,7 @@ const CurrencyActions = ({
|
|||||||
icon: <Trash />,
|
icon: <Trash />,
|
||||||
label: t("actions.remove"),
|
label: t("actions.remove"),
|
||||||
onClick: handleRemove,
|
onClick: handleRemove,
|
||||||
|
disabled: currency.code === defaultCurrencyCode,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -240,9 +269,11 @@ const useColumns = () => {
|
|||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: "actions",
|
id: "actions",
|
||||||
cell: ({ row, table }) => {
|
cell: ({ row, table }) => {
|
||||||
const { currencyCodes, storeId } = table.options.meta as {
|
const { currencyCodes, storeId, defaultCurrencyCode } = table.options
|
||||||
|
.meta as {
|
||||||
currencyCodes: string[]
|
currencyCodes: string[]
|
||||||
storeId: string
|
storeId: string
|
||||||
|
defaultCurrencyCode: string
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -250,6 +281,7 @@ const useColumns = () => {
|
|||||||
storeId={storeId}
|
storeId={storeId}
|
||||||
currency={row.original}
|
currency={row.original}
|
||||||
currencyCodes={currencyCodes}
|
currencyCodes={currencyCodes}
|
||||||
|
defaultCurrencyCode={defaultCurrencyCode}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
+18
@@ -3,6 +3,7 @@ import { Badge, Container, Heading, Text } from "@medusajs/ui"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
import { ExtendedStoreDTO } from "../../../../../types/api-responses"
|
import { ExtendedStoreDTO } from "../../../../../types/api-responses"
|
||||||
|
import { useRegion } from "../../../../../hooks/api/regions"
|
||||||
|
|
||||||
type StoreGeneralSectionProps = {
|
type StoreGeneralSectionProps = {
|
||||||
store: ExtendedStoreDTO
|
store: ExtendedStoreDTO
|
||||||
@@ -11,6 +12,10 @@ type StoreGeneralSectionProps = {
|
|||||||
export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => {
|
export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const { region } = useRegion(store.default_region_id, undefined, {
|
||||||
|
enabled: !!store.default_region_id,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="divide-y p-0">
|
<Container className="divide-y p-0">
|
||||||
<div className="flex items-center justify-between px-6 py-4">
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
@@ -57,6 +62,19 @@ export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{region && (
|
||||||
|
<div className="grid grid-cols-2 px-6 py-4">
|
||||||
|
<Text size="small" leading="compact" weight="plus">
|
||||||
|
{t("store.defaultRegion")}
|
||||||
|
</Text>
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
{region.name}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,12 @@ import { storeLoader } from "./loader"
|
|||||||
export const StoreDetail = () => {
|
export const StoreDetail = () => {
|
||||||
const initialData = useLoaderData() as Awaited<ReturnType<typeof storeLoader>>
|
const initialData = useLoaderData() as Awaited<ReturnType<typeof storeLoader>>
|
||||||
|
|
||||||
const { store, isLoading, isError, error } = useStore({
|
const {
|
||||||
|
store,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useStore({
|
||||||
initialData,
|
initialData,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+78
-9
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Button, Input } from "@medusajs/ui"
|
import { Button, Input, Select, toast } from "@medusajs/ui"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
import { useUpdateStore } from "../../../../../hooks/api/store"
|
import { useUpdateStore } from "../../../../../hooks/api/store"
|
||||||
import { ExtendedStoreDTO } from "../../../../../types/api-responses"
|
import { ExtendedStoreDTO } from "../../../../../types/api-responses"
|
||||||
|
import { useRegions } from "../../../../../hooks/api/regions"
|
||||||
|
|
||||||
type EditStoreFormProps = {
|
type EditStoreFormProps = {
|
||||||
store: ExtendedStoreDTO
|
store: ExtendedStoreDTO
|
||||||
@@ -18,8 +19,8 @@ type EditStoreFormProps = {
|
|||||||
|
|
||||||
const EditStoreSchema = z.object({
|
const EditStoreSchema = z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
// default_currency_code: z.string().optional(),
|
default_currency_code: z.string().optional(),
|
||||||
// default_region_id: z.string().optional(),
|
default_region_id: z.string().optional(),
|
||||||
// default_location_id: z.string().optional(),
|
// default_location_id: z.string().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -30,18 +31,32 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => {
|
|||||||
const form = useForm<z.infer<typeof EditStoreSchema>>({
|
const form = useForm<z.infer<typeof EditStoreSchema>>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: store.name,
|
name: store.name,
|
||||||
|
default_region_id: store.default_region_id || undefined,
|
||||||
|
default_currency_code: store.default_currency_code || undefined,
|
||||||
},
|
},
|
||||||
resolver: zodResolver(EditStoreSchema),
|
resolver: zodResolver(EditStoreSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync, isPending } = useUpdateStore(store.id)
|
const { mutateAsync, isPending } = useUpdateStore(store.id)
|
||||||
|
|
||||||
|
const { regions, isPending: isRegionsLoading } = useRegions({ limit: 999 })
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
mutateAsync(values, {
|
try {
|
||||||
onSuccess: () => {
|
await mutateAsync(values)
|
||||||
handleSuccess()
|
|
||||||
},
|
handleSuccess()
|
||||||
})
|
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("store.toast.update"),
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -62,7 +77,61 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{/* TODO: Add comboboxes for default region, location, and currency. `q` is currently missing on all v2 endpoints */}
|
{/* TODO: Add comboboxes for default sales channel and location */}
|
||||||
|
<Form.Field
|
||||||
|
control={form.control}
|
||||||
|
name="default_currency_code"
|
||||||
|
render={({ field: { onChange, ...field } }) => {
|
||||||
|
return (
|
||||||
|
<Form.Item>
|
||||||
|
<Form.Label>{t("store.defaultCurrency")}</Form.Label>
|
||||||
|
<Form.Control>
|
||||||
|
<Select {...field} onValueChange={onChange}>
|
||||||
|
<Select.Trigger ref={field.ref}>
|
||||||
|
<Select.Value />
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
{store.supported_currency_codes.map((code) => (
|
||||||
|
<Select.Item key={code} value={code}>
|
||||||
|
{code.toUpperCase()}
|
||||||
|
</Select.Item>
|
||||||
|
))}
|
||||||
|
</Select.Content>
|
||||||
|
</Select>
|
||||||
|
</Form.Control>
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Form.Field
|
||||||
|
control={form.control}
|
||||||
|
name="default_region_id"
|
||||||
|
render={({ field: { onChange, ...field } }) => {
|
||||||
|
return (
|
||||||
|
<Form.Item>
|
||||||
|
<Form.Label>{t("store.defaultRegion")}</Form.Label>
|
||||||
|
<Form.Control>
|
||||||
|
<Select
|
||||||
|
{...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>
|
||||||
|
</Form.Control>
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</RouteDrawer.Body>
|
</RouteDrawer.Body>
|
||||||
<RouteDrawer.Footer>
|
<RouteDrawer.Footer>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { EditStoreForm } from "./components/edit-store-form/edit-store-form"
|
|||||||
|
|
||||||
export const StoreEdit = () => {
|
export const StoreEdit = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { store, isLoading, isError, error } = useStore()
|
const { store, isPending: isLoading, isError, error } = useStore()
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ export const deleteStockLocationsStep = createStep(
|
|||||||
|
|
||||||
return new StepResponse(void 0, input)
|
return new StepResponse(void 0, input)
|
||||||
},
|
},
|
||||||
async (deletedLocaitonIds, { container }) => {
|
async (deletedLocationIds, { container }) => {
|
||||||
if (!deletedLocaitonIds?.length) {
|
if (!deletedLocationIds?.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const service = container.resolve(ModuleRegistrationName.STOCK_LOCATION)
|
const service = container.resolve(ModuleRegistrationName.STOCK_LOCATION)
|
||||||
|
|
||||||
await service.restore(deletedLocaitonIds)
|
await service.restore(deletedLocationIds)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
import { BigNumberRawValue } from "@medusajs/types"
|
import { BigNumberRawValue } from "@medusajs/types"
|
||||||
import { BigNumber, MikroOrmBigNumberProperty } from "@medusajs/utils"
|
import {
|
||||||
|
BigNumber,
|
||||||
|
MikroOrmBigNumberProperty,
|
||||||
|
Searchable,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { Entity, PrimaryKey, Property } from "@mikro-orm/core"
|
import { Entity, PrimaryKey, Property } from "@mikro-orm/core"
|
||||||
|
|
||||||
@Entity({ tableName: "currency" })
|
@Entity({ tableName: "currency" })
|
||||||
class Currency {
|
class Currency {
|
||||||
|
@Searchable()
|
||||||
@PrimaryKey({ columnType: "text" })
|
@PrimaryKey({ columnType: "text" })
|
||||||
code!: string
|
code!: string
|
||||||
|
|
||||||
@@ -13,6 +18,7 @@ class Currency {
|
|||||||
@Property({ columnType: "text" })
|
@Property({ columnType: "text" })
|
||||||
symbol_native: string
|
symbol_native: string
|
||||||
|
|
||||||
|
@Searchable()
|
||||||
@Property({ columnType: "text" })
|
@Property({ columnType: "text" })
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const AdminGetCurrenciesParams = createFindParams({
|
|||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
}).merge(
|
||||||
z.object({
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
code: z.union([z.string(), z.array(z.string())]).optional(),
|
code: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
$and: z.lazy(() => AdminGetCurrenciesParams.array()).optional(),
|
$and: z.lazy(() => AdminGetCurrenciesParams.array()).optional(),
|
||||||
$or: z.lazy(() => AdminGetCurrenciesParams.array()).optional(),
|
$or: z.lazy(() => AdminGetCurrenciesParams.array()).optional(),
|
||||||
|
|||||||
Reference in new issue
Block a user