feat: Add tax inclusivity management to currenices (#8112)
This commit is contained in:
@@ -31,7 +31,7 @@ export const usePricePreference = (
|
|||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryFn: () => sdk.admin.pricePreference.retrieve(id, query),
|
queryFn: () => sdk.admin.pricePreference.retrieve(id, query),
|
||||||
queryKey: pricePreferencesQueryKeys.detail(id),
|
queryKey: pricePreferencesQueryKeys.detail(),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ export const usePricePreferences = (
|
|||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryFn: () => sdk.admin.pricePreference.list(query),
|
queryFn: () => sdk.admin.pricePreference.list(query),
|
||||||
queryKey: pricePreferencesQueryKeys.list(query),
|
queryKey: pricePreferencesQueryKeys.list(),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
import { sdk } from "../../lib/client"
|
import { sdk } from "../../lib/client"
|
||||||
import { queryClient } from "../../lib/query-client"
|
import { queryClient } from "../../lib/query-client"
|
||||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||||
|
import { pricePreferencesQueryKeys } from "./price-preferences"
|
||||||
|
|
||||||
const REGIONS_QUERY_KEY = "regions" as const
|
const REGIONS_QUERY_KEY = "regions" as const
|
||||||
export const regionsQueryKeys = queryKeysFactory(REGIONS_QUERY_KEY)
|
export const regionsQueryKeys = queryKeysFactory(REGIONS_QUERY_KEY)
|
||||||
@@ -67,6 +68,14 @@ export const useCreateRegion = (
|
|||||||
mutationFn: (payload) => sdk.admin.region.create(payload),
|
mutationFn: (payload) => sdk.admin.region.create(payload),
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() })
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: pricePreferencesQueryKeys.list(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: pricePreferencesQueryKeys.details(),
|
||||||
|
})
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
@@ -87,6 +96,13 @@ export const useUpdateRegion = (
|
|||||||
queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() })
|
||||||
queryClient.invalidateQueries({ queryKey: regionsQueryKeys.detail(id) })
|
queryClient.invalidateQueries({ queryKey: regionsQueryKeys.detail(id) })
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: pricePreferencesQueryKeys.list(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: pricePreferencesQueryKeys.details(),
|
||||||
|
})
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { HttpTypes } from "@medusajs/types"
|
|||||||
import { sdk } from "../../lib/client"
|
import { sdk } from "../../lib/client"
|
||||||
import { queryClient } from "../../lib/query-client"
|
import { queryClient } from "../../lib/query-client"
|
||||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||||
|
import { pricePreferencesQueryKeys } from "./price-preferences"
|
||||||
|
|
||||||
const STORE_QUERY_KEY = "store" as const
|
const STORE_QUERY_KEY = "store" as const
|
||||||
export const storeQueryKeys = queryKeysFactory(STORE_QUERY_KEY)
|
export const storeQueryKeys = queryKeysFactory(STORE_QUERY_KEY)
|
||||||
@@ -67,7 +68,14 @@ export const useUpdateStore = (
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (payload) => sdk.admin.store.update(id, payload),
|
mutationFn: (payload) => sdk.admin.store.update(id, payload),
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: pricePreferencesQueryKeys.list(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: pricePreferencesQueryKeys.details(),
|
||||||
|
})
|
||||||
queryClient.invalidateQueries({ queryKey: storeQueryKeys.details() })
|
queryClient.invalidateQueries({ queryKey: storeQueryKeys.details() })
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -1710,6 +1710,8 @@
|
|||||||
"inviteLinkTemplate": "Invite link template",
|
"inviteLinkTemplate": "Invite link template",
|
||||||
"currencies": "Currencies",
|
"currencies": "Currencies",
|
||||||
"addCurrencies": "Add currencies",
|
"addCurrencies": "Add currencies",
|
||||||
|
"enableTaxInclusivePricing": "Enable tax inclusive pricing",
|
||||||
|
"disableTaxInclusivePricing": "Disable tax inclusive pricing",
|
||||||
"removeCurrencyWarning_one": "You are about to remove {{count}} currency from your store. Ensure that you have removed all prices using the currency before proceeding.",
|
"removeCurrencyWarning_one": "You are about to remove {{count}} currency from your store. Ensure that you have removed all prices using the currency before proceeding.",
|
||||||
"removeCurrencyWarning_other": "You are about to remove {{count}} currencies from your store. Ensure that you have removed all prices using the currencies before proceeding.",
|
"removeCurrencyWarning_other": "You are about to remove {{count}} currencies from your store. Ensure that you have removed all prices using the currencies before proceeding.",
|
||||||
"currencyAlreadyAdded": "The currency has already been added to your store.",
|
"currencyAlreadyAdded": "The currency has already been added to your store.",
|
||||||
@@ -1719,7 +1721,8 @@
|
|||||||
"toast": {
|
"toast": {
|
||||||
"update": "Store successfully updated",
|
"update": "Store successfully updated",
|
||||||
"currenciesUpdated": "Currencies updated successfully",
|
"currenciesUpdated": "Currencies updated successfully",
|
||||||
"currenciesRemoved": "Removed currencies from the store successfully"
|
"currenciesRemoved": "Removed currencies from the store successfully",
|
||||||
|
"updatedTaxInclusivitySuccessfully": "Tax inclusive pricing updated successfully"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regions": {
|
"regions": {
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
import { CurrencyDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<CurrencyDTO>()
|
const columnHelper = createColumnHelper<HttpTypes.AdminCurrency>()
|
||||||
|
|
||||||
export const useCurrenciesTableColumns = () => {
|
export const useCurrenciesTableColumns = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|||||||
+89
-35
@@ -1,16 +1,15 @@
|
|||||||
import { Currency } from "@medusajs/medusa"
|
import { Button, Checkbox, Hint, Switch, toast, Tooltip } from "@medusajs/ui"
|
||||||
import { Button, Checkbox, Hint, toast, Tooltip } from "@medusajs/ui"
|
|
||||||
import {
|
import {
|
||||||
createColumnHelper,
|
createColumnHelper,
|
||||||
OnChangeFn,
|
OnChangeFn,
|
||||||
RowSelectionState,
|
RowSelectionState,
|
||||||
} from "@tanstack/react-table"
|
} from "@tanstack/react-table"
|
||||||
import { useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { StoreDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import {
|
import {
|
||||||
@@ -23,13 +22,15 @@ import { useUpdateStore } from "../../../../../hooks/api/store"
|
|||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
import { useCurrenciesTableColumns } from "../../../common/hooks/use-currencies-table-columns"
|
import { useCurrenciesTableColumns } from "../../../common/hooks/use-currencies-table-columns"
|
||||||
import { useCurrenciesTableQuery } from "../../../common/hooks/use-currencies-table-query"
|
import { useCurrenciesTableQuery } from "../../../common/hooks/use-currencies-table-query"
|
||||||
|
import { usePricePreferences } from "../../../../../hooks/api/price-preferences"
|
||||||
|
|
||||||
type AddCurrenciesFormProps = {
|
type AddCurrenciesFormProps = {
|
||||||
store: StoreDTO
|
store: HttpTypes.AdminStore
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddCurrenciesSchema = zod.object({
|
const AddCurrenciesSchema = zod.object({
|
||||||
currencies: zod.array(zod.string()).min(1),
|
currencies: zod.array(zod.string()).min(1),
|
||||||
|
pricePreferences: zod.record(zod.boolean()),
|
||||||
})
|
})
|
||||||
|
|
||||||
const PAGE_SIZE = 50
|
const PAGE_SIZE = 50
|
||||||
@@ -39,30 +40,6 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { handleSuccess } = useRouteModal()
|
const { handleSuccess } = useRouteModal()
|
||||||
|
|
||||||
const form = useForm<zod.infer<typeof AddCurrenciesSchema>>({
|
|
||||||
defaultValues: {
|
|
||||||
currencies: [],
|
|
||||||
},
|
|
||||||
resolver: zodResolver(AddCurrenciesSchema),
|
|
||||||
})
|
|
||||||
|
|
||||||
const { setValue } = form
|
|
||||||
|
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
|
||||||
|
|
||||||
const updater: OnChangeFn<RowSelectionState> = (fn) => {
|
|
||||||
const updated = typeof fn === "function" ? fn(rowSelection) : fn
|
|
||||||
|
|
||||||
const ids = Object.keys(updated)
|
|
||||||
|
|
||||||
setValue("currencies", ids, {
|
|
||||||
shouldDirty: true,
|
|
||||||
shouldTouch: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
setRowSelection(updated)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { raw, searchParams } = useCurrenciesTableQuery({
|
const { raw, searchParams } = useCurrenciesTableQuery({
|
||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
prefix: PREFIX,
|
prefix: PREFIX,
|
||||||
@@ -78,10 +55,61 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => {
|
|||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
price_preferences: pricePreferences,
|
||||||
|
isPending: isPricePreferencesPending,
|
||||||
|
isError: isPricePreferencesError,
|
||||||
|
error: pricePreferencesError,
|
||||||
|
} = usePricePreferences({
|
||||||
|
attribute: "currency_code",
|
||||||
|
value: store.supported_currencies?.map((c) => c.currency_code),
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = useForm<zod.infer<typeof AddCurrenciesSchema>>({
|
||||||
|
defaultValues: {
|
||||||
|
currencies: [],
|
||||||
|
pricePreferences: {},
|
||||||
|
},
|
||||||
|
resolver: zodResolver(AddCurrenciesSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
|
|
||||||
|
const { setValue, watch } = form
|
||||||
|
const pricePreferenceValues = watch("pricePreferences")
|
||||||
|
|
||||||
|
const updater: OnChangeFn<RowSelectionState> = (fn) => {
|
||||||
|
const updated = typeof fn === "function" ? fn(rowSelection) : fn
|
||||||
|
|
||||||
|
const ids = Object.keys(updated)
|
||||||
|
setValue("currencies", ids, {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
setRowSelection(updated)
|
||||||
|
}
|
||||||
|
|
||||||
const preSelectedRows =
|
const preSelectedRows =
|
||||||
store.supported_currencies?.map((c) => c.currency_code) ?? []
|
store.supported_currencies?.map((c) => c.currency_code) ?? []
|
||||||
|
|
||||||
const columns = useColumns()
|
const setPricePreferences = useCallback(
|
||||||
|
(values: Record<string, boolean>) => {
|
||||||
|
setValue("pricePreferences", values)
|
||||||
|
},
|
||||||
|
[setValue]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPricePreferences(
|
||||||
|
pricePreferences?.reduce((acc: Record<string, boolean>, curr) => {
|
||||||
|
acc[curr.value] = curr.is_tax_inclusive
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
}, [pricePreferences, setPricePreferences])
|
||||||
|
|
||||||
|
const columns = useColumns(pricePreferenceValues, setPricePreferences)
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
data: currencies ?? [],
|
data: currencies ?? [],
|
||||||
@@ -118,8 +146,7 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => {
|
|||||||
supported_currencies: currencies.map((c) => ({
|
supported_currencies: currencies.map((c) => ({
|
||||||
currency_code: c,
|
currency_code: c,
|
||||||
is_default: c === defaultCurrency,
|
is_default: c === defaultCurrency,
|
||||||
// TODO: Add UI to manage this
|
is_tax_inclusive: data.pricePreferences[c],
|
||||||
is_tax_inclsuive: false,
|
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -185,9 +212,12 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Currency>()
|
const columnHelper = createColumnHelper<HttpTypes.AdminCurrency>()
|
||||||
|
|
||||||
const useColumns = () => {
|
const useColumns = (
|
||||||
|
pricePreferences: Record<string, boolean>,
|
||||||
|
setPricePreferences: any
|
||||||
|
) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const base = useCurrenciesTableColumns()
|
const base = useCurrenciesTableColumns()
|
||||||
|
|
||||||
@@ -236,7 +266,31 @@ const useColumns = () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...base,
|
...base,
|
||||||
|
columnHelper.display({
|
||||||
|
id: "select",
|
||||||
|
header: () => (
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{t("fields.taxInclusivePricing")}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isPreSelected = !row.getCanSelect()
|
||||||
|
const isTaxInclusive = pricePreferences[row.original.code]
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
disabled={isPreSelected}
|
||||||
|
checked={isTaxInclusive ?? false}
|
||||||
|
onCheckedChange={(val) => {
|
||||||
|
setPricePreferences({
|
||||||
|
...pricePreferences,
|
||||||
|
[row.original.code]: val,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
[t, base]
|
[t, base, pricePreferences, setPricePreferences]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-15
@@ -1,5 +1,5 @@
|
|||||||
import { Plus, Trash } from "@medusajs/icons"
|
import { CheckCircle, Plus, Trash, XCircle } from "@medusajs/icons"
|
||||||
import { CurrencyDTO, StoreCurrencyDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CommandBar,
|
CommandBar,
|
||||||
@@ -20,6 +20,8 @@ import { useDataTable } from "../../../../../../hooks/use-data-table"
|
|||||||
import { ExtendedStoreDTO } from "../../../../../../types/api-responses"
|
import { ExtendedStoreDTO } from "../../../../../../types/api-responses"
|
||||||
import { useCurrenciesTableColumns } from "../../../../common/hooks/use-currencies-table-columns"
|
import { useCurrenciesTableColumns } from "../../../../common/hooks/use-currencies-table-columns"
|
||||||
import { useCurrenciesTableQuery } from "../../../../common/hooks/use-currencies-table-query"
|
import { useCurrenciesTableQuery } from "../../../../common/hooks/use-currencies-table-query"
|
||||||
|
import { usePricePreferences } from "../../../../../../hooks/api/price-preferences"
|
||||||
|
import { StatusCell } from "../../../../../../components/table/table-cells/common/status-cell"
|
||||||
|
|
||||||
type StoreCurrencySectionProps = {
|
type StoreCurrencySectionProps = {
|
||||||
store: ExtendedStoreDTO
|
store: ExtendedStoreDTO
|
||||||
@@ -32,7 +34,13 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
|
|
||||||
const { searchParams, raw } = useCurrenciesTableQuery({ pageSize: PAGE_SIZE })
|
const { searchParams, raw } = useCurrenciesTableQuery({ pageSize: PAGE_SIZE })
|
||||||
|
|
||||||
const { currencies, count, isPending, isError, error } = useCurrencies(
|
const {
|
||||||
|
currencies,
|
||||||
|
count,
|
||||||
|
isPending: isCurrenciesPending,
|
||||||
|
isError: isCurrenciesError,
|
||||||
|
error: currenciesError,
|
||||||
|
} = useCurrencies(
|
||||||
{
|
{
|
||||||
code: store.supported_currencies?.map((c) => c.currency_code),
|
code: store.supported_currencies?.map((c) => c.currency_code),
|
||||||
...searchParams,
|
...searchParams,
|
||||||
@@ -43,10 +51,33 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const {
|
||||||
|
price_preferences: pricePreferences,
|
||||||
|
isPending: isPricePreferencesPending,
|
||||||
|
isError: isPricePreferencesError,
|
||||||
|
error: pricePreferencesError,
|
||||||
|
} = usePricePreferences(
|
||||||
|
{
|
||||||
|
attribute: "currency_code",
|
||||||
|
value: store.supported_currencies?.map((c) => c.currency_code),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!store.supported_currencies?.length,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const columns = useColumns()
|
const columns = useColumns()
|
||||||
|
const prefMap = useMemo(() => {
|
||||||
|
return new Map(pricePreferences?.map((pref) => [pref.value!, pref]))
|
||||||
|
}, [pricePreferences])
|
||||||
|
|
||||||
|
const withTaxInclusivity = currencies?.map((c) => ({
|
||||||
|
...c,
|
||||||
|
is_tax_inclusive: prefMap.get(c.code)?.is_tax_inclusive,
|
||||||
|
}))
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
data: currencies ?? [],
|
data: withTaxInclusivity ?? [],
|
||||||
columns,
|
columns,
|
||||||
count: count,
|
count: count,
|
||||||
getRowId: (row) => row.code,
|
getRowId: (row) => row.code,
|
||||||
@@ -62,6 +93,7 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
supportedCurrencies: store.supported_currencies,
|
supportedCurrencies: store.supported_currencies,
|
||||||
defaultCurrencyCode: store.supported_currencies?.find((c) => c.is_default)
|
defaultCurrencyCode: store.supported_currencies?.find((c) => c.is_default)
|
||||||
?.currency_code,
|
?.currency_code,
|
||||||
|
preferencesMap: prefMap,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -104,10 +136,16 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
if (isCurrenciesError) {
|
||||||
throw error
|
throw currenciesError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPricePreferencesError) {
|
||||||
|
throw pricePreferencesError
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLoading = isCurrenciesPending || isPricePreferencesPending
|
||||||
|
|
||||||
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">
|
||||||
@@ -134,7 +172,7 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
|
|||||||
pageSize={PAGE_SIZE}
|
pageSize={PAGE_SIZE}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
count={!store.supported_currencies?.length ? 0 : count}
|
count={!store.supported_currencies?.length ? 0 : count}
|
||||||
isLoading={!store.supported_currencies?.length ? false : isPending}
|
isLoading={!store.supported_currencies?.length ? false : isLoading}
|
||||||
queryObject={raw}
|
queryObject={raw}
|
||||||
/>
|
/>
|
||||||
<CommandBar open={!!Object.keys(rowSelection).length}>
|
<CommandBar open={!!Object.keys(rowSelection).length}>
|
||||||
@@ -161,14 +199,15 @@ const CurrencyActions = ({
|
|||||||
currency,
|
currency,
|
||||||
supportedCurrencies,
|
supportedCurrencies,
|
||||||
defaultCurrencyCode,
|
defaultCurrencyCode,
|
||||||
|
preferencesMap,
|
||||||
}: {
|
}: {
|
||||||
storeId: string
|
storeId: string
|
||||||
currency: CurrencyDTO
|
currency: HttpTypes.AdminCurrency
|
||||||
supportedCurrencies: StoreCurrencyDTO[]
|
supportedCurrencies: HttpTypes.AdminStoreCurrency[]
|
||||||
defaultCurrencyCode: string
|
defaultCurrencyCode: string
|
||||||
|
preferencesMap: Map<string, HttpTypes.AdminPricePreference>
|
||||||
}) => {
|
}) => {
|
||||||
const { mutateAsync } = useUpdateStore(storeId)
|
const { mutateAsync } = useUpdateStore(storeId)
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
|
|
||||||
@@ -205,6 +244,31 @@ const CurrencyActions = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleToggleTaxInclusivity = async () => {
|
||||||
|
await mutateAsync(
|
||||||
|
{
|
||||||
|
supported_currencies: supportedCurrencies.map((c) => {
|
||||||
|
const pref = preferencesMap.get(c.currency_code)
|
||||||
|
return {
|
||||||
|
...c,
|
||||||
|
is_tax_inclusive:
|
||||||
|
c.currency_code === currency.code
|
||||||
|
? !pref?.is_tax_inclusive
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(t("store.toast.updatedTaxInclusivitySuccessfully"))
|
||||||
|
},
|
||||||
|
onError: (e) => {
|
||||||
|
toast.error(e.message)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionMenu
|
<ActionMenu
|
||||||
groups={[
|
groups={[
|
||||||
@@ -216,6 +280,17 @@ const CurrencyActions = ({
|
|||||||
onClick: handleRemove,
|
onClick: handleRemove,
|
||||||
disabled: currency.code === defaultCurrencyCode,
|
disabled: currency.code === defaultCurrencyCode,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: preferencesMap.get(currency.code)?.is_tax_inclusive ? (
|
||||||
|
<XCircle />
|
||||||
|
) : (
|
||||||
|
<CheckCircle />
|
||||||
|
),
|
||||||
|
label: preferencesMap.get(currency.code)?.is_tax_inclusive
|
||||||
|
? t("store.disableTaxInclusivePricing")
|
||||||
|
: t("store.enableTaxInclusivePricing"),
|
||||||
|
onClick: handleToggleTaxInclusivity,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
@@ -223,10 +298,13 @@ const CurrencyActions = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<CurrencyDTO>()
|
const columnHelper = createColumnHelper<
|
||||||
|
HttpTypes.AdminCurrency & { is_tax_inclusive?: boolean }
|
||||||
|
>()
|
||||||
|
|
||||||
const useColumns = () => {
|
const useColumns = () => {
|
||||||
const base = useCurrenciesTableColumns()
|
const base = useCurrenciesTableColumns()
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
@@ -259,14 +337,30 @@ const useColumns = () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...base,
|
...base,
|
||||||
|
columnHelper.accessor("is_tax_inclusive", {
|
||||||
|
header: t("fields.taxInclusivePricing"),
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const isTaxInclusive = getValue()
|
||||||
|
return (
|
||||||
|
<StatusCell color={isTaxInclusive ? "green" : "grey"}>
|
||||||
|
{isTaxInclusive ? t("fields.true") : t("fields.false")}
|
||||||
|
</StatusCell>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}),
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: "actions",
|
id: "actions",
|
||||||
cell: ({ row, table }) => {
|
cell: ({ row, table }) => {
|
||||||
const { supportedCurrencies, storeId, defaultCurrencyCode } = table
|
const {
|
||||||
.options.meta as {
|
supportedCurrencies,
|
||||||
supportedCurrencies: StoreCurrencyDTO[]
|
storeId,
|
||||||
|
defaultCurrencyCode,
|
||||||
|
preferencesMap,
|
||||||
|
} = table.options.meta as {
|
||||||
|
supportedCurrencies: HttpTypes.AdminStoreCurrency[]
|
||||||
storeId: string
|
storeId: string
|
||||||
defaultCurrencyCode: string
|
defaultCurrencyCode: string
|
||||||
|
preferencesMap: Map<string, HttpTypes.AdminPricePreference>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -275,11 +369,12 @@ const useColumns = () => {
|
|||||||
currency={row.original}
|
currency={row.original}
|
||||||
supportedCurrencies={supportedCurrencies}
|
supportedCurrencies={supportedCurrencies}
|
||||||
defaultCurrencyCode={defaultCurrencyCode}
|
defaultCurrencyCode={defaultCurrencyCode}
|
||||||
|
preferencesMap={preferencesMap}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[base]
|
[base, t]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cleanup, render, screen } from "@testing-library/react"
|
||||||
|
|
||||||
|
import CheckCircle from "../check-circle"
|
||||||
|
|
||||||
|
describe("CheckCircle", () => {
|
||||||
|
it("should render the icon without errors", async () => {
|
||||||
|
render(<CheckCircle data-testid="icon" />)
|
||||||
|
|
||||||
|
const svgElement = screen.getByTestId("icon")
|
||||||
|
|
||||||
|
expect(svgElement).toBeInTheDocument()
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import type { IconProps } from "../types"
|
||||||
|
const CheckCircle = React.forwardRef<SVGSVGElement, IconProps>(
|
||||||
|
({ color = "currentColor", ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="15"
|
||||||
|
viewBox="0 0 16 15"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8.41666 13.9446C11.9758 13.9446 14.8611 11.0593 14.8611 7.50011C14.8611 3.94094 11.9758 1.05566 8.41666 1.05566C4.85749 1.05566 1.97221 3.94094 1.97221 7.50011C1.97221 11.0593 4.85749 13.9446 8.41666 13.9446Z"
|
||||||
|
stroke={color}
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.52777 7.72233L7.52777 9.94455L11.3055 5.05566"
|
||||||
|
stroke={color}
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
CheckCircle.displayName = "CheckCircle"
|
||||||
|
export default CheckCircle
|
||||||
@@ -66,6 +66,7 @@ export { default as ChatBubbleLeftRight } from "./chat-bubble-left-right"
|
|||||||
export { default as ChatBubble } from "./chat-bubble"
|
export { default as ChatBubble } from "./chat-bubble"
|
||||||
export { default as CheckCircleMiniSolid } from "./check-circle-mini-solid"
|
export { default as CheckCircleMiniSolid } from "./check-circle-mini-solid"
|
||||||
export { default as CheckCircleSolid } from "./check-circle-solid"
|
export { default as CheckCircleSolid } from "./check-circle-solid"
|
||||||
|
export { default as CheckCircle } from "./check-circle"
|
||||||
export { default as CheckMini } from "./check-mini"
|
export { default as CheckMini } from "./check-mini"
|
||||||
export { default as Check } from "./check"
|
export { default as Check } from "./check"
|
||||||
export { default as ChevronDoubleLeftMiniSolid } from "./chevron-double-left-mini-solid"
|
export { default as ChevronDoubleLeftMiniSolid } from "./chevron-double-left-mini-solid"
|
||||||
|
|||||||
@@ -737,8 +737,9 @@ export default class PricingModuleService
|
|||||||
| PricingTypes.CreatePricePreferenceDTO[],
|
| PricingTypes.CreatePricePreferenceDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<PricePreferenceDTO | PricePreferenceDTO[]> {
|
): Promise<PricePreferenceDTO | PricePreferenceDTO[]> {
|
||||||
const preferences = await this.pricePreferenceService_.create(
|
const normalized = Array.isArray(data) ? data : [data]
|
||||||
data,
|
const preferences = await this.createPricePreferences_(
|
||||||
|
normalized,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -777,14 +778,10 @@ export default class PricingModuleService
|
|||||||
const operations: Promise<PricePreference[]>[] = []
|
const operations: Promise<PricePreference[]>[] = []
|
||||||
|
|
||||||
if (forCreate.length) {
|
if (forCreate.length) {
|
||||||
operations.push(
|
operations.push(this.createPricePreferences_(forCreate, sharedContext))
|
||||||
this.pricePreferenceService_.create(forCreate, sharedContext)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (forUpdate.length) {
|
if (forUpdate.length) {
|
||||||
operations.push(
|
operations.push(this.updatePricePreferences_(forUpdate, sharedContext))
|
||||||
this.pricePreferenceService_.update(forUpdate, sharedContext)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = (await promiseAll(operations)).flat()
|
const result = (await promiseAll(operations)).flat()
|
||||||
@@ -833,7 +830,7 @@ export default class PricingModuleService
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateResult = await this.pricePreferenceService_.update(
|
const updateResult = await this.updatePricePreferences_(
|
||||||
normalizedInput,
|
normalizedInput,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
@@ -845,6 +842,35 @@ export default class PricingModuleService
|
|||||||
return isString(idOrSelector) ? pricePreferences[0] : pricePreferences
|
return isString(idOrSelector) ? pricePreferences[0] : pricePreferences
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
protected async createPricePreferences_(
|
||||||
|
data: PricingTypes.CreatePricePreferenceDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const preferences = await this.pricePreferenceService_.create(
|
||||||
|
data.map((d) => ({
|
||||||
|
...d,
|
||||||
|
is_tax_inclusive: d.is_tax_inclusive ?? false,
|
||||||
|
})),
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return preferences
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
protected async updatePricePreferences_(
|
||||||
|
data: PricingTypes.UpdatePricePreferenceDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const preferences = await this.pricePreferenceService_.update(
|
||||||
|
data,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return preferences
|
||||||
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
protected async createPriceSets_(
|
protected async createPriceSets_(
|
||||||
data: PricingTypes.CreatePriceSetDTO[],
|
data: PricingTypes.CreatePriceSetDTO[],
|
||||||
|
|||||||
Reference in New Issue
Block a user