feat(dashboard): shipping option tax rate overrides UI (#13260)

* feat(dashboard): shipping option tax rate overrides UI

* feat: add location filter

* feat: show service zone in SO table

* feat: display location in the SO table
This commit is contained in:
Frane Polić
2025-08-21 12:06:36 +02:00
committed by GitHub
parent 37f372ccf7
commit 9b38b750de
14 changed files with 480 additions and 76 deletions
@@ -21,6 +21,8 @@ import {
useProductTags,
useProductTypes,
useProducts,
useShippingOptions,
useStockLocations,
} from "../../../../../hooks/api"
import {
useCollectionTableColumns,
@@ -35,6 +37,7 @@ import {
useProductTableFilters,
useProductTagTableFilters,
useProductTypeTableFilters,
useShippingOptionTableFilters,
} from "../../../../../hooks/table/filters"
import {
useCollectionTableQuery,
@@ -42,10 +45,12 @@ import {
useProductTableQuery,
useProductTagTableQuery,
useProductTypeTableQuery,
useShippingOptionTableQuery,
} from "../../../../../hooks/table/query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { TaxRateRuleReferenceType } from "../../constants"
import { TaxRateRuleReference } from "../../schemas"
import { useShippingOptionTableColumns } from "../../../../../hooks/table/columns/use-shipping-option-table-columns"
type TargetFormProps = {
referenceType: TaxRateRuleReferenceType
@@ -110,16 +115,18 @@ type TableProps = {
const Table = ({ referenceType, ...props }: TableProps) => {
switch (referenceType) {
case TaxRateRuleReferenceType.CUSTOMER_GROUP:
return <CustomerGroupTable {...props} />
// case TaxRateRuleReferenceType.CUSTOMER_GROUP:
// return <CustomerGroupTable {...props} />
case TaxRateRuleReferenceType.PRODUCT:
return <ProductTable {...props} />
case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
return <ProductCollectionTable {...props} />
// case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
// return <ProductCollectionTable {...props} />
case TaxRateRuleReferenceType.PRODUCT_TYPE:
return <ProductTypeTable {...props} />
case TaxRateRuleReferenceType.PRODUCT_TAG:
return <ProductTagTable {...props} />
// case TaxRateRuleReferenceType.PRODUCT_TAG:
// return <ProductTagTable {...props} />
case TaxRateRuleReferenceType.SHIPPING_OPTION:
return <ShippingOptionTable {...props} />
default:
return null
}
@@ -671,6 +678,150 @@ const useProductTypeColumns = () => {
)
}
const PREFIX_SHIPPING_OPTION = "so"
const ShippingOptionTable = ({
initialRowState,
intermediate,
setIntermediate,
}: TableImplementationProps) => {
const { t } = useTranslation()
const [rowSelection, setRowSelection] =
useState<RowSelectionState>(initialRowState)
useCleanupSearchParams()
const { searchParams, raw } = useShippingOptionTableQuery({
pageSize: PAGE_SIZE,
prefix: PREFIX_SHIPPING_OPTION,
})
const { shipping_options, count, isLoading, isError, error } =
useShippingOptions(
{
...searchParams,
fields: "+service_zone.fulfillment_set.location.*",
},
{
placeholderData: keepPreviousData,
}
)
const updater: OnChangeFn<RowSelectionState> = (value) => {
const state = typeof value === "function" ? value(rowSelection) : value
const currentIds = Object.keys(rowSelection)
const ids = Object.keys(state)
const newIds = ids.filter((id) => !currentIds.includes(id))
const removedIds = currentIds.filter((id) => !ids.includes(id))
const newShippingOptions =
shipping_options
?.filter((p) => newIds.includes(p.id))
.map((p) => ({
value: p.id,
label: p.name,
})) || []
const filteredIntermediate = intermediate.filter(
(p) => !removedIds.includes(p.value)
)
setIntermediate([...filteredIntermediate, ...newShippingOptions])
setRowSelection(state)
}
const { stock_locations } = useStockLocations({
limit: 1000,
})
const filters = useShippingOptionTableFilters(stock_locations || [])
const columns = useShippingOptionColumns()
const { table } = useDataTable({
data: shipping_options || [],
columns,
count,
enablePagination: true,
enableRowSelection: true,
getRowId: (row) => row.id,
rowSelection: {
state: rowSelection,
updater,
},
pageSize: PAGE_SIZE,
prefix: PREFIX_SHIPPING_OPTION,
})
if (isError) {
throw error
}
return (
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
count={count}
isLoading={isLoading}
filters={filters}
orderBy={[
{ key: "name", label: t("fields.name") },
{ key: "created_at", label: t("fields.createdAt") },
{ key: "updated_at", label: t("fields.updatedAt") },
]}
layout="fill"
pagination
search
prefix={PREFIX_SHIPPING_OPTION}
queryObject={raw}
/>
)
}
const soColumnHelper = createColumnHelper<HttpTypes.AdminShippingOption>()
const useShippingOptionColumns = () => {
const base = useShippingOptionTableColumns()
return useMemo(
() => [
soColumnHelper.display({
id: "select",
header: ({ table }) => {
return (
<Checkbox
checked={
table.getIsSomePageRowsSelected()
? "indeterminate"
: table.getIsAllPageRowsSelected()
}
onCheckedChange={(value) =>
table.toggleAllPageRowsSelected(!!value)
}
/>
)
},
cell: ({ row }) => {
return (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
onClick={(e) => {
e.stopPropagation()
}}
/>
)
},
}),
...base,
],
[base]
)
}
const PREFIX_PRODUCT_TAG = "ptag"
const ProductTagTable = ({
@@ -24,6 +24,7 @@ import { useProducts } from "../../../../../hooks/api/products"
import { formatPercentage } from "../../../../../lib/percentage-helpers"
import { TaxRateRuleReferenceType } from "../../constants"
import { useDeleteTaxRateAction } from "../../hooks"
import { useShippingOptions } from "../../../../../hooks/api"
interface TaxOverrideCardProps extends ComponentPropsWithoutRef<"div"> {
taxRate: HttpTypes.AdminTaxRate
@@ -201,6 +202,9 @@ const ReferenceBadge = ({
case TaxRateRuleReferenceType.PRODUCT_TYPE:
label = t("taxRegions.fields.targets.tags.productType")
break
case TaxRateRuleReferenceType.SHIPPING_OPTION:
label = t("taxRegions.fields.targets.tags.shippingOption")
break
// case TaxRateRuleReferenceType.CUSTOMER_GROUP:
// label = t("taxRegions.fields.targets.tags.customerGroup")
// break
@@ -303,6 +307,16 @@ const useReferenceValues = (
}
)
const shippingOptions = useShippingOptions(
{
id: ids,
limit: 10,
},
{
enabled:
!!ids.length && type === TaxRateRuleReferenceType.SHIPPING_OPTION,
}
)
// const collections = useCollections(
// {
// id: ids,
@@ -358,6 +372,17 @@ const useReferenceValues = (
isError: productTypes.isError,
error: productTypes.error,
}
case TaxRateRuleReferenceType.SHIPPING_OPTION:
return {
labels: shippingOptions.shipping_options?.map((option) => option.name),
isPending: shippingOptions.isPending,
additional: shippingOptions.count
? shippingOptions.count -
(shippingOptions.shipping_options?.length || 0)
: 0,
isError: shippingOptions.isError,
error: shippingOptions.error,
}
// case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
// return {
// labels: collections.collections?.map((collection) => collection.title!),
@@ -1,6 +1,7 @@
export enum TaxRateRuleReferenceType {
PRODUCT = "product",
PRODUCT_TYPE = "product_type",
SHIPPING_OPTION = "shipping_option",
// PRODUCT_COLLECTION = "product_collection",
// PRODUCT_TAG = "product_tag",
// CUSTOMER_GROUP = "customer_group",
@@ -50,12 +50,14 @@ const TaxRegionCreateTaxOverrideSchema = z.object({
enabled_rules: z.object({
product: z.boolean(),
product_type: z.boolean(),
shipping_option: z.boolean(),
// product_collection: z.boolean(),
// product_tag: z.boolean(),
// customer_group: z.boolean(),
}),
product: z.array(TaxRateRuleReferenceSchema).optional(),
product_type: z.array(TaxRateRuleReferenceSchema).optional(),
shipping_option: z.array(TaxRateRuleReferenceSchema).optional(),
// product_collection: z.array(TaxRateRuleReferenceSchema).optional(),
// product_tag: z.array(TaxRateRuleReferenceSchema).optional(),
// customer_group: z.array(TaxRateRuleReferenceSchema).optional(),
@@ -87,12 +89,14 @@ export const TaxRegionCreateTaxOverrideForm = ({
enabled_rules: {
product: true,
product_type: false,
shipping_option: false,
// product_collection: false,
// product_tag: false,
// customer_group: false,
},
product: [],
product_type: [],
shipping_option: [],
// product_collection: [],
// product_tag: [],
// customer_group: [],
@@ -109,6 +113,7 @@ export const TaxRegionCreateTaxOverrideForm = ({
// customer_group,
// product_collection,
// product_tag,
shipping_option,
} = values
const productRules = createTaxRulePayload({
@@ -119,6 +124,10 @@ export const TaxRegionCreateTaxOverrideForm = ({
reference_type: TaxRateRuleReferenceType.PRODUCT_TYPE,
references: product_type || [],
})
const shippingOptionRules = createTaxRulePayload({
reference_type: TaxRateRuleReferenceType.SHIPPING_OPTION,
references: shipping_option || [],
})
// const customerGroupRules = createTaxRulePayload({
// reference_type: TaxRateRuleReferenceType.CUSTOMER_GROUP,
// references: customer_group || [],
@@ -135,6 +144,7 @@ export const TaxRegionCreateTaxOverrideForm = ({
const rules = [
productRules,
productTypeRules,
shippingOptionRules,
// customerGroupRules,
// productCollectionRules,
// productTagRules,
@@ -173,6 +183,11 @@ export const TaxRegionCreateTaxOverrideForm = ({
name: TaxRateRuleReferenceType.PRODUCT_TYPE,
})
const shippingOptions = useFieldArray({
control: form.control,
name: TaxRateRuleReferenceType.SHIPPING_OPTION,
})
// const productCollections = useFieldArray({
// control: form.control,
// name: TaxRateRuleReferenceType.PRODUCT_COLLECTION,
@@ -195,6 +210,8 @@ export const TaxRegionCreateTaxOverrideForm = ({
return products
case TaxRateRuleReferenceType.PRODUCT_TYPE:
return productTypes
case TaxRateRuleReferenceType.SHIPPING_OPTION:
return shippingOptions
// case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
// return productCollections
// case TaxRateRuleReferenceType.PRODUCT_TAG:
@@ -213,6 +230,10 @@ export const TaxRegionCreateTaxOverrideForm = ({
value: TaxRateRuleReferenceType.PRODUCT_TYPE,
label: t("taxRegions.fields.targets.options.productType"),
},
{
value: TaxRateRuleReferenceType.SHIPPING_OPTION,
label: t("taxRegions.fields.targets.options.shippingOption"),
},
// {
// value: TaxRateRuleReferenceType.PRODUCT_COLLECTION,
// label: t("taxRegions.fields.targets.options.productCollection"),
@@ -234,6 +255,9 @@ export const TaxRegionCreateTaxOverrideForm = ({
[TaxRateRuleReferenceType.PRODUCT_TYPE]: t(
"taxRegions.fields.targets.placeholders.productType"
),
[TaxRateRuleReferenceType.SHIPPING_OPTION]: t(
"taxRegions.fields.targets.placeholders.shippingOption"
),
// [TaxRateRuleReferenceType.PRODUCT_COLLECTION]: t(
// "taxRegions.fields.targets.placeholders.productCollection"
// ),
@@ -58,12 +58,14 @@ const TaxRegionTaxRateEditSchema = z.object({
enabled_rules: z.object({
product: z.boolean(),
product_type: z.boolean(),
shipping_option: z.boolean(),
// product_collection: z.boolean(),
// product_tag: z.boolean(),
// customer_group: z.boolean(),
}),
product: z.array(TaxRateRuleReferenceSchema).optional(),
product_type: z.array(TaxRateRuleReferenceSchema).optional(),
shipping_option: z.array(TaxRateRuleReferenceSchema).optional(),
// product_collection: z.array(TaxRateRuleReferenceSchema).optional(),
// product_tag: z.array(TaxRateRuleReferenceSchema).optional(),
// customer_group: z.array(TaxRateRuleReferenceSchema).optional(),
@@ -89,6 +91,7 @@ export const TaxRegionTaxOverrideEditForm = ({
enabled_rules: {
product: initialValues.product.length > 0,
product_type: initialValues.product_type.length > 0,
shipping_option: initialValues.shipping_option.length > 0,
// customer_groups: initialValues.customer_group.length > 0,
// product_collections:
// initialValues.product_collections.length > 0,
@@ -96,6 +99,7 @@ export const TaxRegionTaxOverrideEditForm = ({
},
product: initialValues.product,
product_type: initialValues.product_type,
shipping_option: initialValues.shipping_option,
// product_collections: initialValues.product_collection,
// product_tags: initialValues.product_tag,
// customer_groups: initialValues.customer_group,
@@ -109,6 +113,7 @@ export const TaxRegionTaxOverrideEditForm = ({
const {
product,
product_type,
shipping_option,
// customer_groups,
// product_collections,
// product_tags,
@@ -122,6 +127,10 @@ export const TaxRegionTaxOverrideEditForm = ({
reference_type: TaxRateRuleReferenceType.PRODUCT_TYPE,
references: product_type || [],
})
const shippingOptionRules = createTaxRulePayload({
reference_type: TaxRateRuleReferenceType.SHIPPING_OPTION,
references: shipping_option || [],
})
// const customerGroupRules = createTaxRulePayload({
// reference_type: TaxRateRuleReferenceType.CUSTOMER_GROUP,
// references: customer_groups || [],
@@ -138,6 +147,7 @@ export const TaxRegionTaxOverrideEditForm = ({
const rules = [
productRules,
productTypeRules,
shippingOptionRules,
// customerGroupRules,
// productCollectionRules,
// productTagRules,
@@ -175,6 +185,11 @@ export const TaxRegionTaxOverrideEditForm = ({
name: "product_type",
})
const shippingOptions = useFieldArray({
control: form.control,
name: "shipping_option",
})
// const productCollections = useFieldArray({
// control: form.control,
// name: "product_collection",
@@ -197,6 +212,8 @@ export const TaxRegionTaxOverrideEditForm = ({
return products
case TaxRateRuleReferenceType.PRODUCT_TYPE:
return productTypes
case TaxRateRuleReferenceType.SHIPPING_OPTION:
return shippingOptions
// case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
// return productCollections
// case TaxRateRuleReferenceType.PRODUCT_TAG:
@@ -215,6 +232,10 @@ export const TaxRegionTaxOverrideEditForm = ({
value: TaxRateRuleReferenceType.PRODUCT_TYPE,
label: t("taxRegions.fields.targets.options.productType"),
},
{
value: TaxRateRuleReferenceType.SHIPPING_OPTION,
label: t("taxRegions.fields.targets.options.shippingOption"),
},
// {
// value: TaxRateRuleReferenceType.PRODUCT_COLLECTION,
// label: t("taxRegions.fields.targets.options.productCollection"),
@@ -236,6 +257,9 @@ export const TaxRegionTaxOverrideEditForm = ({
[TaxRateRuleReferenceType.PRODUCT_TYPE]: t(
"taxRegions.fields.targets.placeholders.productType"
),
[TaxRateRuleReferenceType.SHIPPING_OPTION]: t(
"taxRegions.fields.targets.placeholders.shippingOption"
),
// [TaxRateRuleReferenceType.PRODUCT_COLLECTION]: t(
// "taxRegions.fields.targets.placeholders.productCollection"
// ),
@@ -4,15 +4,12 @@ import { useTranslation } from "react-i18next"
import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals"
import { useCollections } from "../../../hooks/api/collections"
import { useCustomerGroups } from "../../../hooks/api/customer-groups"
import { useProductTypes } from "../../../hooks/api/product-types"
import { useProducts } from "../../../hooks/api/products"
import { useProductTags } from "../../../hooks/api/tags"
import { useTaxRate } from "../../../hooks/api/tax-rates"
import { TaxRateRuleReferenceType } from "../common/constants"
import { TaxRegionTaxOverrideEditForm } from "./components/tax-region-tax-override-edit-form"
import { InitialRuleValues } from "./types"
import { useShippingOptions, useTaxRate } from "../../../hooks/api"
export const TaxRegionTaxOverrideEdit = () => {
const { t } = useTranslation()
@@ -59,10 +56,11 @@ const useDefaultRulesValues = (
[key in TaxRateRuleReferenceType]: string[]
} = {
[TaxRateRuleReferenceType.PRODUCT]: [],
[TaxRateRuleReferenceType.PRODUCT_COLLECTION]: [],
[TaxRateRuleReferenceType.PRODUCT_TAG]: [],
// [TaxRateRuleReferenceType.PRODUCT_COLLECTION]: [],
// [TaxRateRuleReferenceType.PRODUCT_TAG]: [],
[TaxRateRuleReferenceType.PRODUCT_TYPE]: [],
[TaxRateRuleReferenceType.CUSTOMER_GROUP]: [],
[TaxRateRuleReferenceType.SHIPPING_OPTION]: [],
// [TaxRateRuleReferenceType.CUSTOMER_GROUP]: [],
}
rules.forEach((rule) => {
@@ -81,26 +79,26 @@ const useDefaultRulesValues = (
value: product.id,
})),
},
{
ids: idsByReferenceType[TaxRateRuleReferenceType.PRODUCT_COLLECTION],
hook: useCollections,
key: TaxRateRuleReferenceType.PRODUCT_COLLECTION,
getResult: (result: HttpTypes.AdminCollectionListResponse) =>
result.collections.map((collection) => ({
label: collection.title!,
value: collection.id!,
})),
},
{
ids: idsByReferenceType[TaxRateRuleReferenceType.PRODUCT_TAG],
hook: useProductTags,
key: TaxRateRuleReferenceType.PRODUCT_TAG,
getResult: (result: any) =>
result.tags.map((tag: any) => ({
label: tag.value,
value: tag.id,
})),
},
// {
// ids: idsByReferenceType[TaxRateRuleReferenceType.PRODUCT_COLLECTION],
// hook: useCollections,
// key: TaxRateRuleReferenceType.PRODUCT_COLLECTION,
// getResult: (result: HttpTypes.AdminCollectionListResponse) =>
// result.collections.map((collection) => ({
// label: collection.title!,
// value: collection.id!,
// })),
// },
// {
// ids: idsByReferenceType[TaxRateRuleReferenceType.PRODUCT_TAG],
// hook: useProductTags,
// key: TaxRateRuleReferenceType.PRODUCT_TAG,
// getResult: (result: any) =>
// result.tags.map((tag: any) => ({
// label: tag.value,
// value: tag.id,
// })),
// },
{
ids: idsByReferenceType[TaxRateRuleReferenceType.PRODUCT_TYPE],
hook: useProductTypes,
@@ -112,19 +110,29 @@ const useDefaultRulesValues = (
})),
},
{
ids: idsByReferenceType[TaxRateRuleReferenceType.CUSTOMER_GROUP],
hook: useCustomerGroups,
key: TaxRateRuleReferenceType.CUSTOMER_GROUP,
getResult: (
result: HttpTypes.PaginatedResponse<{
customer_groups: HttpTypes.AdminCustomerGroup[]
}>
) =>
result.customer_groups.map((customerGroup) => ({
label: customerGroup.name!,
value: customerGroup.id,
ids: idsByReferenceType[TaxRateRuleReferenceType.SHIPPING_OPTION],
hook: useShippingOptions,
key: TaxRateRuleReferenceType.SHIPPING_OPTION,
getResult: (result: HttpTypes.AdminShippingOptionListResponse) =>
result.shipping_options.map((shippingOption) => ({
label: shippingOption.name,
value: shippingOption.id,
})),
},
// {
// ids: idsByReferenceType[TaxRateRuleReferenceType.CUSTOMER_GROUP],
// hook: useCustomerGroups,
// key: TaxRateRuleReferenceType.CUSTOMER_GROUP,
// getResult: (
// result: HttpTypes.PaginatedResponse<{
// customer_groups: HttpTypes.AdminCustomerGroup[]
// }>
// ) =>
// result.customer_groups.map((customerGroup) => ({
// label: customerGroup.name!,
// value: customerGroup.id,
// })),
// },
]
const queryResults = queries.map(({ ids, hook }) => {
@@ -3,8 +3,9 @@ import { TaxRateRuleReference } from "../common/schemas"
export type InitialRuleValues = {
[TaxRateRuleReferenceType.PRODUCT]: TaxRateRuleReference[]
[TaxRateRuleReferenceType.PRODUCT_COLLECTION]: TaxRateRuleReference[]
[TaxRateRuleReferenceType.PRODUCT_TAG]: TaxRateRuleReference[]
// [TaxRateRuleReferenceType.PRODUCT_COLLECTION]: TaxRateRuleReference[]
// [TaxRateRuleReferenceType.PRODUCT_TAG]: TaxRateRuleReference[]
[TaxRateRuleReferenceType.SHIPPING_OPTION]: TaxRateRuleReference[]
[TaxRateRuleReferenceType.PRODUCT_TYPE]: TaxRateRuleReference[]
[TaxRateRuleReferenceType.CUSTOMER_GROUP]: TaxRateRuleReference[]
// [TaxRateRuleReferenceType.CUSTOMER_GROUP]: TaxRateRuleReference[]
}