feat(dashboard,types,js-sdk,ui): Tax Regions UI (#7935)
This commit is contained in:
1 parent
75e7047243
commit
046a34bdfc
232 files changed
+7555
-3818
No files matched your search
-59
@@ -1,59 +0,0 @@
|
|||||||
import { PropsWithChildren, createContext } from "react"
|
|
||||||
|
|
||||||
type ConditionOperator =
|
|
||||||
| "eq"
|
|
||||||
| "ne"
|
|
||||||
| "gt"
|
|
||||||
| "lt"
|
|
||||||
| "gte"
|
|
||||||
| "lte"
|
|
||||||
| "in"
|
|
||||||
| "nin"
|
|
||||||
|
|
||||||
type ConditionBlockValue<TValue> = {
|
|
||||||
attribute: string
|
|
||||||
operator: ConditionOperator
|
|
||||||
value: TValue
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConditionBlockState<TValue> = {
|
|
||||||
defaultValue?: ConditionBlockValue<TValue>
|
|
||||||
value?: ConditionBlockValue<TValue>
|
|
||||||
onChange: (value: ConditionBlockValue<TValue>) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConditionBlockContext = createContext<ConditionBlockState<any> | null>(
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
const useConditionBlock = () => {
|
|
||||||
const context = ConditionBlockContext
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("useConditionBlock must be used within a ConditionBlock")
|
|
||||||
}
|
|
||||||
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConditionBlockProps<TValue> = PropsWithChildren<
|
|
||||||
ConditionBlockState<TValue>
|
|
||||||
>
|
|
||||||
|
|
||||||
const Root = <TValue,>({ children, ...props }: ConditionBlockProps<TValue>) => {
|
|
||||||
return (
|
|
||||||
<ConditionBlockContext.Provider value={props}>
|
|
||||||
{children}
|
|
||||||
</ConditionBlockContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Divider = () => {}
|
|
||||||
|
|
||||||
const Operator = () => {}
|
|
||||||
|
|
||||||
const Item = () => {}
|
|
||||||
|
|
||||||
export const ConditionBlock = Object.assign(Root, {
|
|
||||||
Divider,
|
|
||||||
})
|
|
||||||
@@ -3,6 +3,7 @@ import { PropsWithChildren } from "react"
|
|||||||
|
|
||||||
type IconAvatarProps = PropsWithChildren<{
|
type IconAvatarProps = PropsWithChildren<{
|
||||||
className?: string
|
className?: string
|
||||||
|
size?: "small" | "large" | "xlarge"
|
||||||
}>
|
}>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,12 +11,24 @@ type IconAvatarProps = PropsWithChildren<{
|
|||||||
*
|
*
|
||||||
* The `<Avatar/>` component from `@medusajs/ui` does not support passing an icon as a child.
|
* The `<Avatar/>` component from `@medusajs/ui` does not support passing an icon as a child.
|
||||||
*/
|
*/
|
||||||
export const IconAvatar = ({ children, className }: IconAvatarProps) => {
|
export const IconAvatar = ({
|
||||||
|
size = "small",
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: IconAvatarProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clx(
|
className={clx(
|
||||||
"shadow-borders-base bg-ui-bg-base flex size-7 items-center justify-center rounded-md",
|
"shadow-borders-base flex size-7 items-center justify-center",
|
||||||
"[&>div]:bg-ui-bg-field [&>div]:text-ui-fg-subtle [&>div]:flex [&>div]:size-6 [&>div]:items-center [&>div]:justify-center [&>div]:rounded-[4px]",
|
"[&>div]:bg-ui-bg-field [&>div]:text-ui-fg-subtle [&>div]:flex [&>div]:size-6 [&>div]:items-center [&>div]:justify-center",
|
||||||
|
{
|
||||||
|
"size-7 rounded-md [&>div]:size-6 [&>div]:rounded-[4px]":
|
||||||
|
size === "small",
|
||||||
|
"size-10 rounded-lg [&>div]:size-9 [&>div]:rounded-[6px]":
|
||||||
|
size === "large",
|
||||||
|
"size-12 rounded-xl [&>div]:size-11 [&>div]:rounded-[10px]":
|
||||||
|
size === "xlarge",
|
||||||
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -136,6 +136,23 @@ export const GeneralSectionSkeleton = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TableFooterSkeleton = ({ layout }: { layout: "fill" | "fit" }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clx("flex items-center justify-between p-4", {
|
||||||
|
"border-t": layout === "fill",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Skeleton className="h-7 w-[138px]" />
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<Skeleton className="h-7 w-24" />
|
||||||
|
<Skeleton className="h-7 w-11" />
|
||||||
|
<Skeleton className="h-7 w-11" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type TableSkeletonProps = {
|
type TableSkeletonProps = {
|
||||||
rowCount?: number
|
rowCount?: number
|
||||||
search?: boolean
|
search?: boolean
|
||||||
@@ -182,20 +199,7 @@ export const TableSkeleton = ({
|
|||||||
<Skeleton key={row} className="h-10 w-full rounded-none" />
|
<Skeleton key={row} className="h-10 w-full rounded-none" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{pagination && (
|
{pagination && <TableFooterSkeleton layout={layout} />}
|
||||||
<div
|
|
||||||
className={clx("flex items-center justify-between p-4", {
|
|
||||||
"border-t": layout === "fill",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Skeleton className="h-7 w-[138px]" />
|
|
||||||
<div className="flex items-center gap-x-2">
|
|
||||||
<Skeleton className="h-7 w-24" />
|
|
||||||
<Skeleton className="h-7 w-11" />
|
|
||||||
<Skeleton className="h-7 w-11" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import { clx } from "@medusajs/ui"
|
|||||||
import { ComponentPropsWithoutRef, forwardRef, memo } from "react"
|
import { ComponentPropsWithoutRef, forwardRef, memo } from "react"
|
||||||
import { Controller } from "react-hook-form"
|
import { Controller } from "react-hook-form"
|
||||||
|
|
||||||
import { countries } from "../../../lib/countries"
|
import { countries } from "../../../lib/data/countries"
|
||||||
import { useDataGridCell } from "../hooks"
|
import { useDataGridCell } from "../hooks"
|
||||||
import { DataGridCellProps } from "../types"
|
import { DataGridCellProps } from "../types"
|
||||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import CurrencyInput from "react-currency-input-field"
|
import CurrencyInput from "react-currency-input-field"
|
||||||
import { Controller } from "react-hook-form"
|
import { Controller } from "react-hook-form"
|
||||||
|
|
||||||
import { currencies } from "../../../lib/currencies"
|
import { currencies } from "../../../lib/data/currencies"
|
||||||
import { useDataGridCell } from "../hooks"
|
import { useDataGridCell } from "../hooks"
|
||||||
import { DataGridCellProps } from "../types"
|
import { DataGridCellProps } from "../types"
|
||||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ import {
|
|||||||
import { TrianglesMini } from "@medusajs/icons"
|
import { TrianglesMini } from "@medusajs/icons"
|
||||||
import { clx } from "@medusajs/ui"
|
import { clx } from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { countries } from "../../../lib/countries"
|
import { countries } from "../../../lib/data/countries"
|
||||||
|
|
||||||
export const CountrySelect = forwardRef<
|
export const CountrySelect = forwardRef<
|
||||||
HTMLSelectElement,
|
HTMLSelectElement,
|
||||||
|
|||||||
+46
-3
@@ -1,7 +1,11 @@
|
|||||||
import { Input, Text } from "@medusajs/ui"
|
import { Input, Text, clx } from "@medusajs/ui"
|
||||||
import { ComponentProps, ElementRef, forwardRef } from "react"
|
import { ComponentProps, ElementRef, forwardRef } from "react"
|
||||||
|
import Primitive from "react-currency-input-field"
|
||||||
|
|
||||||
export const PercentageInput = forwardRef<
|
/**
|
||||||
|
* @deprecated Use `PercentageInput` instead
|
||||||
|
*/
|
||||||
|
export const DeprecatedPercentageInput = forwardRef<
|
||||||
ElementRef<typeof Input>,
|
ElementRef<typeof Input>,
|
||||||
Omit<ComponentProps<typeof Input>, "type">
|
Omit<ComponentProps<typeof Input>, "type">
|
||||||
>(({ min = 0, max = 100, step = 0.0001, ...props }, ref) => {
|
>(({ min = 0, max = 100, step = 0.0001, ...props }, ref) => {
|
||||||
@@ -29,4 +33,43 @@ export const PercentageInput = forwardRef<
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
PercentageInput.displayName = "HandleInput"
|
DeprecatedPercentageInput.displayName = "PercentageInput"
|
||||||
|
|
||||||
|
export const PercentageInput = forwardRef<
|
||||||
|
ElementRef<"input">,
|
||||||
|
ComponentProps<typeof Primitive>
|
||||||
|
>(({ min = 0, decimalScale = 2, className, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<Primitive
|
||||||
|
ref={ref as any} // dependency is typed incorrectly
|
||||||
|
min={min}
|
||||||
|
autoComplete="off"
|
||||||
|
decimalScale={decimalScale}
|
||||||
|
decimalsLimit={decimalScale}
|
||||||
|
{...props}
|
||||||
|
className={clx(
|
||||||
|
"caret-ui-fg-base bg-ui-bg-field shadow-buttons-neutral transition-fg txt-compact-small flex w-full select-none appearance-none items-center justify-between rounded-md px-2 py-1.5 pr-10 text-right outline-none",
|
||||||
|
"placeholder:text-ui-fg-muted text-ui-fg-base",
|
||||||
|
"hover:bg-ui-bg-field-hover",
|
||||||
|
"focus-visible:shadow-borders-interactive-with-active data-[state=open]:!shadow-borders-interactive-with-active",
|
||||||
|
"aria-[invalid=true]:border-ui-border-error aria-[invalid=true]:shadow-borders-error",
|
||||||
|
"invalid::border-ui-border-error invalid:shadow-borders-error",
|
||||||
|
"disabled:!bg-ui-bg-disabled disabled:!text-ui-fg-disabled",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-y-0 right-0 z-10 flex w-8 items-center justify-center border-l">
|
||||||
|
<Text
|
||||||
|
className="text-ui-fg-muted"
|
||||||
|
size="small"
|
||||||
|
leading="compact"
|
||||||
|
weight="plus"
|
||||||
|
>
|
||||||
|
%
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
PercentageInput.displayName = "PercentageInput"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./province-select"
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
ComponentPropsWithoutRef,
|
||||||
|
forwardRef,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
} from "react"
|
||||||
|
|
||||||
|
import { TrianglesMini } from "@medusajs/icons"
|
||||||
|
import { clx } from "@medusajs/ui"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { getCountryProvinceObjectByIso2 } from "../../../lib/data/country-states"
|
||||||
|
|
||||||
|
interface ProvinceSelectProps extends ComponentPropsWithoutRef<"select"> {
|
||||||
|
/**
|
||||||
|
* ISO 3166-1 alpha-2 country code
|
||||||
|
*/
|
||||||
|
country_code: string
|
||||||
|
/**
|
||||||
|
* Whether to use the ISO 3166-1 alpha-2 code or the name of the province as the value
|
||||||
|
*
|
||||||
|
* @default "iso_2"
|
||||||
|
*/
|
||||||
|
valueAs?: "iso_2" | "name"
|
||||||
|
placeholder?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProvinceSelect = forwardRef<
|
||||||
|
HTMLSelectElement,
|
||||||
|
ProvinceSelectProps
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
placeholder,
|
||||||
|
country_code,
|
||||||
|
valueAs = "iso_2",
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const innerRef = useRef<HTMLSelectElement>(null)
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => innerRef.current as HTMLSelectElement)
|
||||||
|
|
||||||
|
const isPlaceholder = innerRef.current?.value === ""
|
||||||
|
|
||||||
|
const provinceObject = getCountryProvinceObjectByIso2(country_code)
|
||||||
|
|
||||||
|
if (!provinceObject) {
|
||||||
|
disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = Object.entries(provinceObject?.options ?? {}).map(
|
||||||
|
([iso2, name]) => {
|
||||||
|
return (
|
||||||
|
<option key={iso2} value={valueAs === "iso_2" ? iso2 : name}>
|
||||||
|
{name}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const placeholderText = provinceObject
|
||||||
|
? t(`taxRegions.fields.sublevels.placeholders.${provinceObject.type}`)
|
||||||
|
: ""
|
||||||
|
|
||||||
|
const placeholderOption = provinceObject ? (
|
||||||
|
<option value="" disabled className="text-ui-fg-muted">
|
||||||
|
{placeholder || placeholderText}
|
||||||
|
</option>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<TrianglesMini
|
||||||
|
className={clx(
|
||||||
|
"text-ui-fg-muted transition-fg pointer-events-none absolute right-2 top-1/2 -translate-y-1/2",
|
||||||
|
{
|
||||||
|
"text-ui-fg-disabled": disabled,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
disabled={disabled}
|
||||||
|
className={clx(
|
||||||
|
"bg-ui-bg-field shadow-buttons-neutral transition-fg txt-compact-small flex w-full select-none appearance-none items-center justify-between rounded-md px-2 py-1.5 outline-none",
|
||||||
|
"placeholder:text-ui-fg-muted text-ui-fg-base",
|
||||||
|
"hover:bg-ui-bg-field-hover",
|
||||||
|
"focus-visible:shadow-borders-interactive-with-active data-[state=open]:!shadow-borders-interactive-with-active",
|
||||||
|
"aria-[invalid=true]:border-ui-border-error aria-[invalid=true]:shadow-borders-error",
|
||||||
|
"invalid::border-ui-border-error invalid:shadow-borders-error",
|
||||||
|
"disabled:!bg-ui-bg-disabled disabled:!text-ui-fg-disabled",
|
||||||
|
{
|
||||||
|
"text-ui-fg-muted": isPlaceholder,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={innerRef}
|
||||||
|
>
|
||||||
|
{/* Add an empty option so the first option is preselected */}
|
||||||
|
{placeholderOption}
|
||||||
|
{options}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ProvinceSelect.displayName = "CountrySelect"
|
||||||
@@ -107,10 +107,7 @@ export const NavItem = ({
|
|||||||
<ul>
|
<ul>
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<li
|
<li key={item.to} className="flex h-[32px] items-center pl-1">
|
||||||
key={item.to}
|
|
||||||
className="flex h-[32px] items-center gap-x-1 pl-2"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
role="presentation"
|
role="presentation"
|
||||||
className="flex h-full w-5 items-center justify-center"
|
className="flex h-full w-5 items-center justify-center"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./single-column-page"
|
||||||
|
export * from "./two-column-page"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./single-column-page"
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
import { Outlet } from "react-router-dom"
|
||||||
|
import { JsonViewSection } from "../../../common/json-view-section"
|
||||||
|
import { PageProps } from "../types"
|
||||||
|
|
||||||
|
export const SingleColumnPage = <TData,>({
|
||||||
|
children,
|
||||||
|
widgets,
|
||||||
|
data,
|
||||||
|
hasOutlet,
|
||||||
|
showJSON,
|
||||||
|
}: PageProps<TData>) => {
|
||||||
|
const { before, after } = widgets
|
||||||
|
const widgetProps = { data }
|
||||||
|
|
||||||
|
if (showJSON && !data) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.warn(
|
||||||
|
"`showJSON` is true but no data is provided. To display JSON, provide data prop."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
showJSON = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-y-3">
|
||||||
|
{before.widgets.map((w, i) => {
|
||||||
|
return <w.Component {...widgetProps} key={i} />
|
||||||
|
})}
|
||||||
|
{children}
|
||||||
|
{after.widgets.map((w, i) => {
|
||||||
|
return <w.Component {...widgetProps} key={i} />
|
||||||
|
})}
|
||||||
|
{showJSON && <JsonViewSection data={data!} />}
|
||||||
|
{hasOutlet && <Outlet />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./two-column-page"
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
import { clx } from "@medusajs/ui"
|
||||||
|
import { Children, ComponentPropsWithoutRef } from "react"
|
||||||
|
import { Outlet } from "react-router-dom"
|
||||||
|
import { JsonViewSection } from "../../../common/json-view-section"
|
||||||
|
import { PageProps, WidgetImport, WidgetProps } from "../types"
|
||||||
|
|
||||||
|
interface TwoColumnWidgetProps extends WidgetProps {
|
||||||
|
sideBefore: WidgetImport
|
||||||
|
sideAfter: WidgetImport
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TwoColumnPageProps<TData> extends PageProps<TData> {
|
||||||
|
widgets: TwoColumnWidgetProps
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = <TData,>({
|
||||||
|
children,
|
||||||
|
/**
|
||||||
|
* Widgets to be rendered in the main content area and sidebar.
|
||||||
|
*/
|
||||||
|
widgets,
|
||||||
|
/**
|
||||||
|
* Data to be passed to widgets and the JSON view.
|
||||||
|
*/
|
||||||
|
data,
|
||||||
|
/**
|
||||||
|
* Whether to show JSON view of the data. Defaults to true.
|
||||||
|
*/
|
||||||
|
showJSON = false,
|
||||||
|
/**
|
||||||
|
* Whether to render an outlet for children routes. Defaults to true.
|
||||||
|
*/
|
||||||
|
hasOutlet = true,
|
||||||
|
}: TwoColumnPageProps<TData>) => {
|
||||||
|
const widgetProps = { data }
|
||||||
|
const { before, after, sideBefore, sideAfter } = widgets
|
||||||
|
|
||||||
|
if (showJSON && !data) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.warn(
|
||||||
|
"`showJSON` is true but no data is provided. To display JSON, provide data prop."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
showJSON = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const childrenArray = Children.toArray(children)
|
||||||
|
|
||||||
|
if (childrenArray.length !== 2) {
|
||||||
|
throw new Error("TwoColumnPage expects exactly two children")
|
||||||
|
}
|
||||||
|
|
||||||
|
const [main, sidebar] = childrenArray
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-y-2">
|
||||||
|
{before.widgets.map((w, i) => {
|
||||||
|
return <w.Component {...widgetProps} key={i} />
|
||||||
|
})}
|
||||||
|
<div className="flex flex-col gap-x-4 gap-y-3 xl:flex-row xl:items-start">
|
||||||
|
<div className="flex w-full flex-col gap-y-3">
|
||||||
|
{main}
|
||||||
|
{after.widgets.map((w, i) => {
|
||||||
|
return <w.Component {...widgetProps} key={i} />
|
||||||
|
})}
|
||||||
|
{showJSON && (
|
||||||
|
<div className="hidden xl:block">
|
||||||
|
<JsonViewSection data={data!} root="product" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full max-w-[100%] flex-col gap-y-3 xl:mt-0 xl:max-w-[440px]">
|
||||||
|
{sideBefore.widgets.map((w, i) => {
|
||||||
|
return <w.Component {...widgetProps} key={i} />
|
||||||
|
})}
|
||||||
|
{sidebar}
|
||||||
|
{sideAfter.widgets.map((w, i) => {
|
||||||
|
return <w.Component {...widgetProps} key={i} />
|
||||||
|
})}
|
||||||
|
{showJSON && (
|
||||||
|
<div className="xl:hidden">
|
||||||
|
<JsonViewSection data={data!} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{hasOutlet && <Outlet />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Main = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: ComponentPropsWithoutRef<"div">) => {
|
||||||
|
return (
|
||||||
|
<div className={clx("flex w-full flex-col gap-y-3", className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Sidebar = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: ComponentPropsWithoutRef<"div">) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clx(
|
||||||
|
"flex w-full max-w-[100%] flex-col gap-y-3 xl:mt-0 xl:max-w-[400px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TwoColumnPage = Object.assign(Root, { Main, Sidebar })
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ReactNode } from "react"
|
||||||
|
|
||||||
|
export type Widget = {
|
||||||
|
Component: React.ComponentType<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WidgetImport = {
|
||||||
|
widgets: Widget[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WidgetProps {
|
||||||
|
before: WidgetImport
|
||||||
|
after: WidgetImport
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageProps<TData> {
|
||||||
|
children: ReactNode
|
||||||
|
widgets: WidgetProps
|
||||||
|
data?: TData
|
||||||
|
showJSON?: boolean
|
||||||
|
hasOutlet?: boolean
|
||||||
|
}
|
||||||
+2
-2
@@ -42,8 +42,8 @@ const useSettingRoutes = (): NavItemProps[] => {
|
|||||||
to: "/settings/regions",
|
to: "/settings/regions",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Taxes",
|
label: t("taxRegions.domain"),
|
||||||
to: "/settings/taxes",
|
to: "/settings/tax-regions",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("salesChannels.domain"),
|
label: t("salesChannels.domain"),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Keyboard,
|
Keyboard,
|
||||||
MagnifyingGlass,
|
MagnifyingGlass,
|
||||||
SidebarLeft,
|
SidebarLeft,
|
||||||
|
TriangleRightMini,
|
||||||
User as UserIcon,
|
User as UserIcon,
|
||||||
} from "@medusajs/icons"
|
} from "@medusajs/icons"
|
||||||
import {
|
import {
|
||||||
@@ -92,18 +93,17 @@ const Breadcrumbs = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ol className={clx("text-ui-fg-muted flex select-none items-center")}>
|
<ol
|
||||||
|
className={clx(
|
||||||
|
"text-ui-fg-muted txt-compact-small-plus flex select-none items-center"
|
||||||
|
)}
|
||||||
|
>
|
||||||
{crumbs.map((crumb, index) => {
|
{crumbs.map((crumb, index) => {
|
||||||
const isLast = index === crumbs.length - 1
|
const isLast = index === crumbs.length - 1
|
||||||
const isSingle = crumbs.length === 1
|
const isSingle = crumbs.length === 1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li key={index} className={clx("flex items-center")}>
|
||||||
key={index}
|
|
||||||
className={clx("txt-compact-small-plus flex items-center", {
|
|
||||||
"text-ui-fg-subtle": isLast,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{!isLast ? (
|
{!isLast ? (
|
||||||
<Link
|
<Link
|
||||||
className="transition-fg hover:text-ui-fg-subtle"
|
className="transition-fg hover:text-ui-fg-subtle"
|
||||||
@@ -124,7 +124,11 @@ const Breadcrumbs = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isLast && <span className="mx-2 -mt-0.5">›</span>}
|
{!isLast && (
|
||||||
|
<span className="mx-2">
|
||||||
|
<TriangleRightMini />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ const Content = forwardRef<
|
|||||||
return (
|
return (
|
||||||
<FocusModal.Content
|
<FocusModal.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={clx("top-6", className)}
|
className={clx("!top-6", className)}
|
||||||
overlayProps={{
|
overlayProps={{
|
||||||
className: "bg-transparent",
|
className: "bg-transparent",
|
||||||
}}
|
}}
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import { ArrowUpDown } from "@medusajs/icons"
|
import { DescendingSorting } from "@medusajs/icons"
|
||||||
import { DropdownMenu, IconButton } from "@medusajs/ui"
|
import { DropdownMenu, IconButton } from "@medusajs/ui"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
@@ -107,7 +107,7 @@ export const DataTableOrderBy = <TData,>({
|
|||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<IconButton size="small">
|
<IconButton size="small">
|
||||||
<ArrowUpDown />
|
<DescendingSorting />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content className="z-[1]" align="end">
|
<DropdownMenu.Content className="z-[1]" align="end">
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
import { Tooltip } from "@medusajs/ui"
|
import { Tooltip } from "@medusajs/ui"
|
||||||
import format from "date-fns/format"
|
import { format } from "date-fns/format"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { PlaceholderCell } from "../placeholder-cell"
|
import { PlaceholderCell } from "../placeholder-cell"
|
||||||
|
|
||||||
type DateCellProps = {
|
type DateCellProps = {
|
||||||
date: Date | string | null
|
date?: Date | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DateCell = ({ date }: DateCellProps) => {
|
export const DateCell = ({ date }: DateCellProps) => {
|
||||||
|
|||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
import type { Discount } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
type DiscountCellProps = {
|
|
||||||
discount: Discount
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CodeCell = ({ discount }: DiscountCellProps) => {
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center gap-x-3 overflow-hidden">
|
|
||||||
{/* // TODO: border color inversion*/}
|
|
||||||
<span className="bg-ui-tag-neutral-bg truncate rounded-md border border-neutral-200 p-1 text-xs">
|
|
||||||
{discount.code}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CodeHeader = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className=" flex h-full w-full items-center ">
|
|
||||||
<span>{t("fields.code")}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./code-cell"
|
|
||||||
-25
@@ -1,25 +0,0 @@
|
|||||||
import type { Discount } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
type DiscountCellProps = {
|
|
||||||
discount: Discount
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DescriptionCell = ({ discount }: DiscountCellProps) => {
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center gap-x-3 overflow-hidden">
|
|
||||||
<span className="truncate">{discount.rule.description}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DescriptionHeader = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className=" flex h-full w-full items-center ">
|
|
||||||
<span>{t("fields.description")}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./description-cell"
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./redemption-cell.tsx"
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
type DiscountCellProps = {
|
|
||||||
redemptions: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RedemptionCell = ({ redemptions }: DiscountCellProps) => {
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center justify-end gap-x-3 text-right">
|
|
||||||
<span>{redemptions}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RedemptionHeader = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center justify-end text-right">
|
|
||||||
<span className="truncate">{t("fields.totalRedemptions")}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./status-cell"
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
import { Discount } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
import { StatusCell as StatusCell_ } from "../../common/status-cell"
|
|
||||||
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import {
|
|
||||||
getDiscountStatus,
|
|
||||||
PromotionStatus,
|
|
||||||
} from "../../../../../lib/discounts.ts"
|
|
||||||
|
|
||||||
type DiscountCellProps = {
|
|
||||||
discount: Discount
|
|
||||||
}
|
|
||||||
|
|
||||||
export const StatusCell = ({ discount }: DiscountCellProps) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const [color, text] = {
|
|
||||||
[PromotionStatus.DISABLED]: [
|
|
||||||
"grey",
|
|
||||||
t("discounts.discountStatus.disabled"),
|
|
||||||
],
|
|
||||||
[PromotionStatus.ACTIVE]: ["green", t("discounts.discountStatus.active")],
|
|
||||||
[PromotionStatus.SCHEDULED]: [
|
|
||||||
"orange",
|
|
||||||
t("discounts.discountStatus.scheduled"),
|
|
||||||
],
|
|
||||||
[PromotionStatus.EXPIRED]: ["red", t("discounts.discountStatus.expired")],
|
|
||||||
}[getDiscountStatus(discount)] as [
|
|
||||||
"grey" | "orange" | "green" | "red",
|
|
||||||
string
|
|
||||||
]
|
|
||||||
|
|
||||||
return <StatusCell_ color={color}>{text}</StatusCell_>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const StatusHeader = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className=" flex h-full w-full items-center ">
|
|
||||||
<span>{t("fields.status")}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./value-cell.tsx"
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
import { DiscountRule } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { MoneyAmountCell } from "../../common/money-amount-cell"
|
|
||||||
|
|
||||||
type DiscountCellProps = {
|
|
||||||
rule: DiscountRule
|
|
||||||
currencyCode: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ValueCell = ({ currencyCode, rule }: DiscountCellProps) => {
|
|
||||||
const isFixed = rule.type === "fixed"
|
|
||||||
const isPercentage = rule.type === "percentage"
|
|
||||||
const isFreeShipping = rule.type === "free_shipping"
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center gap-x-3 overflow-hidden">
|
|
||||||
{isFreeShipping && <span>Free shipping</span>}
|
|
||||||
{isPercentage && <span className="">{rule.value}%</span>}
|
|
||||||
{isFixed && (
|
|
||||||
<MoneyAmountCell currencyCode={currencyCode} amount={rule.value} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ValueHeader = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className=" flex h-full w-full items-center ">
|
|
||||||
<span>{t("fields.value")}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { RegionCountryDTO } from "@medusajs/types"
|
import { RegionCountryDTO } from "@medusajs/types"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { PlaceholderCell } from "../../common/placeholder-cell"
|
import { countries as COUNTRIES } from "../../../../../lib/data/countries"
|
||||||
import { ListSummary } from "../../../../common/list-summary"
|
import { ListSummary } from "../../../../common/list-summary"
|
||||||
import { countries as COUNTRIES } from "../../../../../lib/countries"
|
import { PlaceholderCell } from "../../common/placeholder-cell"
|
||||||
|
|
||||||
type CountriesCellProps = {
|
type CountriesCellProps = {
|
||||||
countries?: RegionCountryDTO[] | null
|
countries?: RegionCountryDTO[] | null
|
||||||
|
|||||||
@@ -21,3 +21,5 @@ export const I18n = () => {
|
|||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { i18n }
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
import { HttpTypes, PaginatedResponse } from "@medusajs/types"
|
import { HttpTypes, PaginatedResponse } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
QueryKey,
|
QueryKey,
|
||||||
@@ -25,7 +26,7 @@ export const useCustomerGroup = (
|
|||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
{ customer_group: HttpTypes.AdminCustomerGroup },
|
{ customer_group: HttpTypes.AdminCustomerGroup },
|
||||||
Error,
|
FetchError,
|
||||||
{ customer_group: HttpTypes.AdminCustomerGroup },
|
{ customer_group: HttpTypes.AdminCustomerGroup },
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
@@ -46,7 +47,7 @@ export const useCustomerGroups = (
|
|||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
PaginatedResponse<{ customer_groups: HttpTypes.AdminCustomerGroup[] }>,
|
PaginatedResponse<{ customer_groups: HttpTypes.AdminCustomerGroup[] }>,
|
||||||
Error,
|
FetchError,
|
||||||
PaginatedResponse<{ customer_groups: HttpTypes.AdminCustomerGroup[] }>,
|
PaginatedResponse<{ customer_groups: HttpTypes.AdminCustomerGroup[] }>,
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
@@ -65,7 +66,7 @@ export const useCustomerGroups = (
|
|||||||
export const useCreateCustomerGroup = (
|
export const useCreateCustomerGroup = (
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
{ customer_group: HttpTypes.AdminCustomerGroup },
|
{ customer_group: HttpTypes.AdminCustomerGroup },
|
||||||
Error,
|
FetchError,
|
||||||
z.infer<typeof CreateCustomerGroupSchema>
|
z.infer<typeof CreateCustomerGroupSchema>
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -85,7 +86,7 @@ export const useUpdateCustomerGroup = (
|
|||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
{ customer_group: HttpTypes.AdminCustomerGroup },
|
{ customer_group: HttpTypes.AdminCustomerGroup },
|
||||||
Error,
|
FetchError,
|
||||||
z.infer<typeof EditCustomerGroupSchema>
|
z.infer<typeof EditCustomerGroupSchema>
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -109,7 +110,7 @@ export const useDeleteCustomerGroup = (
|
|||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
{ id: string; object: "customer-group"; deleted: boolean },
|
{ id: string; object: "customer-group"; deleted: boolean },
|
||||||
Error,
|
FetchError,
|
||||||
void
|
void
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -133,7 +134,7 @@ export const useAddCustomersToGroup = (
|
|||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
{ customer_group: HttpTypes.AdminCustomerGroup },
|
{ customer_group: HttpTypes.AdminCustomerGroup },
|
||||||
Error,
|
FetchError,
|
||||||
{ customer_ids: string[] }
|
{ customer_ids: string[] }
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -160,7 +161,7 @@ export const useRemoveCustomersFromGroup = (
|
|||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
{ customer_group: HttpTypes.AdminCustomerGroup },
|
{ customer_group: HttpTypes.AdminCustomerGroup },
|
||||||
Error,
|
FetchError,
|
||||||
{ customer_ids: string[] }
|
{ customer_ids: string[] }
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
export * from "./api-keys"
|
||||||
|
export * from "./auth"
|
||||||
|
export * from "./campaigns"
|
||||||
|
export * from "./categories"
|
||||||
|
export * from "./collections"
|
||||||
|
export * from "./currencies"
|
||||||
|
export * from "./customer-groups"
|
||||||
|
export * from "./customers"
|
||||||
|
export * from "./fulfillment"
|
||||||
|
export * from "./fulfillment-providers"
|
||||||
|
export * from "./fulfillment-sets"
|
||||||
|
export * from "./inventory"
|
||||||
|
export * from "./invites"
|
||||||
|
export * from "./orders"
|
||||||
|
export * from "./payments"
|
||||||
|
export * from "./price-lists"
|
||||||
|
export * from "./product-types"
|
||||||
|
export * from "./products"
|
||||||
|
export * from "./promotions"
|
||||||
|
export * from "./regions"
|
||||||
|
export * from "./reservations"
|
||||||
|
export * from "./sales-channels"
|
||||||
|
export * from "./shipping-options"
|
||||||
|
export * from "./shipping-profiles"
|
||||||
|
export * from "./stock-locations"
|
||||||
|
export * from "./store"
|
||||||
|
export * from "./tags"
|
||||||
|
export * from "./tax-rates"
|
||||||
|
export * from "./tax-regions"
|
||||||
|
export * from "./users"
|
||||||
|
export * from "./workflow-executions"
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query"
|
import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query"
|
||||||
import { client } from "../../lib/client"
|
import { sdk } from "../../lib/client"
|
||||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||||
import { TagsListRes, TagRes } from "../../types/api-responses"
|
|
||||||
|
|
||||||
const TAGS_QUERY_KEY = "tags" as const
|
const TAGS_QUERY_KEY = "tags" as const
|
||||||
export const tagsQueryKeys = queryKeysFactory(TAGS_QUERY_KEY)
|
export const tagsQueryKeys = queryKeysFactory(TAGS_QUERY_KEY)
|
||||||
@@ -9,13 +10,18 @@ export const tagsQueryKeys = queryKeysFactory(TAGS_QUERY_KEY)
|
|||||||
export const useTag = (
|
export const useTag = (
|
||||||
id: string,
|
id: string,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<TagRes, Error, TagRes, QueryKey>,
|
UseQueryOptions<
|
||||||
|
HttpTypes.AdminProductTagResponse,
|
||||||
|
FetchError,
|
||||||
|
HttpTypes.AdminProductTagResponse,
|
||||||
|
QueryKey
|
||||||
|
>,
|
||||||
"queryFn" | "queryKey"
|
"queryFn" | "queryKey"
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryKey: tagsQueryKeys.detail(id),
|
queryKey: tagsQueryKeys.detail(id),
|
||||||
queryFn: async () => client.tags.retrieve(id),
|
queryFn: async () => sdk.admin.productTag.retrieve(id),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -23,15 +29,20 @@ export const useTag = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useTags = (
|
export const useTags = (
|
||||||
query?: Record<string, any>,
|
query?: HttpTypes.AdminProductTagListParams,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<TagsListRes, Error, TagsListRes, QueryKey>,
|
UseQueryOptions<
|
||||||
|
HttpTypes.AdminProductTagListResponse,
|
||||||
|
FetchError,
|
||||||
|
HttpTypes.AdminProductTagListResponse,
|
||||||
|
QueryKey
|
||||||
|
>,
|
||||||
"queryFn" | "queryKey"
|
"queryFn" | "queryKey"
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryKey: tagsQueryKeys.list(query),
|
queryKey: tagsQueryKeys.list(query),
|
||||||
queryFn: async () => client.tags.list(query),
|
queryFn: async () => sdk.admin.productTag.list(query),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
QueryKey,
|
QueryKey,
|
||||||
@@ -9,6 +10,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 { taxRegionsQueryKeys } from "./tax-regions"
|
||||||
|
|
||||||
const TAX_RATES_QUERY_KEY = "tax_rates" as const
|
const TAX_RATES_QUERY_KEY = "tax_rates" as const
|
||||||
export const taxRatesQueryKeys = queryKeysFactory(TAX_RATES_QUERY_KEY)
|
export const taxRatesQueryKeys = queryKeysFactory(TAX_RATES_QUERY_KEY)
|
||||||
@@ -19,7 +21,7 @@ export const useTaxRate = (
|
|||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
HttpTypes.AdminTaxRateResponse,
|
HttpTypes.AdminTaxRateResponse,
|
||||||
Error,
|
FetchError,
|
||||||
HttpTypes.AdminTaxRateResponse,
|
HttpTypes.AdminTaxRateResponse,
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
@@ -72,6 +74,8 @@ export const useUpdateTaxRate = (
|
|||||||
queryKey: taxRatesQueryKeys.detail(id),
|
queryKey: taxRatesQueryKeys.detail(id),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({ queryKey: taxRegionsQueryKeys.details() })
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
@@ -81,7 +85,7 @@ export const useUpdateTaxRate = (
|
|||||||
export const useCreateTaxRate = (
|
export const useCreateTaxRate = (
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
HttpTypes.AdminTaxRateResponse,
|
HttpTypes.AdminTaxRateResponse,
|
||||||
Error,
|
FetchError,
|
||||||
HttpTypes.AdminCreateTaxRate
|
HttpTypes.AdminCreateTaxRate
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -89,6 +93,9 @@ export const useCreateTaxRate = (
|
|||||||
mutationFn: (payload) => sdk.admin.taxRate.create(payload),
|
mutationFn: (payload) => sdk.admin.taxRate.create(payload),
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
queryClient.invalidateQueries({ queryKey: taxRatesQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: taxRatesQueryKeys.lists() })
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({ queryKey: taxRegionsQueryKeys.details() })
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
@@ -111,6 +118,8 @@ export const useDeleteTaxRate = (
|
|||||||
queryKey: taxRatesQueryKeys.detail(id),
|
queryKey: taxRatesQueryKeys.detail(id),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({ queryKey: taxRegionsQueryKeys.details() })
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
QueryKey,
|
QueryKey,
|
||||||
@@ -15,11 +16,11 @@ export const taxRegionsQueryKeys = queryKeysFactory(TAX_REGIONS_QUERY_KEY)
|
|||||||
|
|
||||||
export const useTaxRegion = (
|
export const useTaxRegion = (
|
||||||
id: string,
|
id: string,
|
||||||
query?: Record<string, any>,
|
query?: HttpTypes.AdminTaxRegionParams,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
HttpTypes.AdminTaxRegionResponse,
|
HttpTypes.AdminTaxRegionResponse,
|
||||||
Error,
|
FetchError,
|
||||||
HttpTypes.AdminTaxRegionResponse,
|
HttpTypes.AdminTaxRegionResponse,
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
@@ -36,11 +37,11 @@ export const useTaxRegion = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useTaxRegions = (
|
export const useTaxRegions = (
|
||||||
query?: Record<string, any>,
|
query?: HttpTypes.AdminTaxRegionListParams,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
HttpTypes.AdminTaxRegionListResponse,
|
HttpTypes.AdminTaxRegionListResponse,
|
||||||
Error,
|
FetchError,
|
||||||
HttpTypes.AdminTaxRegionListResponse,
|
HttpTypes.AdminTaxRegionListResponse,
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
@@ -59,7 +60,7 @@ export const useTaxRegions = (
|
|||||||
export const useCreateTaxRegion = (
|
export const useCreateTaxRegion = (
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
HttpTypes.AdminTaxRegionResponse,
|
HttpTypes.AdminTaxRegionResponse,
|
||||||
Error,
|
FetchError,
|
||||||
HttpTypes.AdminCreateTaxRegion
|
HttpTypes.AdminCreateTaxRegion
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -77,7 +78,7 @@ export const useDeleteTaxRegion = (
|
|||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
HttpTypes.AdminTaxRegionDeleteResponse,
|
HttpTypes.AdminTaxRegionDeleteResponse,
|
||||||
Error,
|
FetchError,
|
||||||
void
|
void
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
@@ -89,6 +90,9 @@ export const useDeleteTaxRegion = (
|
|||||||
queryKey: taxRegionsQueryKeys.detail(id),
|
queryKey: taxRegionsQueryKeys.detail(id),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Invalidate all detail queries, as the deleted tax region may have been a sublevel region
|
||||||
|
queryClient.invalidateQueries({ queryKey: taxRegionsQueryKeys.details() })
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export * from "./use-campaign-table-columns"
|
||||||
|
export * from "./use-collection-table-columns"
|
||||||
|
export * from "./use-customer-group-table-columns"
|
||||||
|
export * from "./use-customer-table-columns"
|
||||||
|
export * from "./use-order-table-columns"
|
||||||
|
export * from "./use-product-table-columns"
|
||||||
|
export * from "./use-product-tag-table-columns"
|
||||||
|
export * from "./use-product-type-table-columns"
|
||||||
|
export * from "./use-region-table-columns"
|
||||||
|
export * from "./use-sales-channel-table-columns"
|
||||||
|
export * from "./use-shipping-option-table-columns"
|
||||||
|
export * from "./use-tax-rates-table-columns"
|
||||||
+5
-9
@@ -2,8 +2,7 @@ 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"
|
||||||
|
import { TextCell } from "../../../components/table/table-cells/common/text-cell"
|
||||||
import { CollectionRowActions } from "./collection-row-actions"
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<HttpTypes.AdminCollection>()
|
const columnHelper = createColumnHelper<HttpTypes.AdminCollection>()
|
||||||
|
|
||||||
@@ -14,23 +13,20 @@ export const useCollectionTableColumns = () => {
|
|||||||
() => [
|
() => [
|
||||||
columnHelper.accessor("title", {
|
columnHelper.accessor("title", {
|
||||||
header: t("fields.title"),
|
header: t("fields.title"),
|
||||||
|
cell: ({ getValue }) => <TextCell text={getValue()} />,
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("handle", {
|
columnHelper.accessor("handle", {
|
||||||
header: t("fields.handle"),
|
header: t("fields.handle"),
|
||||||
cell: ({ getValue }) => `/${getValue()}`,
|
cell: ({ getValue }) => <TextCell text={`/${getValue()}`} />,
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("products", {
|
columnHelper.accessor("products", {
|
||||||
header: t("fields.products"),
|
header: t("fields.products"),
|
||||||
cell: ({ getValue }) => {
|
cell: ({ getValue }) => {
|
||||||
const count = getValue()?.length
|
const count = getValue()?.length || undefined
|
||||||
|
|
||||||
return <span>{count || "-"}</span>
|
return <TextCell text={count} />
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
columnHelper.display({
|
|
||||||
id: "actions",
|
|
||||||
cell: ({ row }) => <CollectionRowActions collection={row.original} />,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
[t]
|
[t]
|
||||||
)
|
)
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import { useMemo } from "react"
|
|
||||||
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
|
|
||||||
|
|
||||||
import type { Discount } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
import {
|
|
||||||
CodeCell,
|
|
||||||
CodeHeader,
|
|
||||||
} from "../../../components/table/table-cells/discount/code-cell"
|
|
||||||
import {
|
|
||||||
DescriptionHeader,
|
|
||||||
DescriptionCell,
|
|
||||||
} from "../../../components/table/table-cells/discount/description-cell"
|
|
||||||
import {
|
|
||||||
ValueCell,
|
|
||||||
ValueHeader,
|
|
||||||
} from "../../../components/table/table-cells/discount/value-cell"
|
|
||||||
import {
|
|
||||||
RedemptionCell,
|
|
||||||
RedemptionHeader,
|
|
||||||
} from "../../../components/table/table-cells/discount/redemption-cell"
|
|
||||||
import {
|
|
||||||
StatusHeader,
|
|
||||||
StatusCell,
|
|
||||||
} from "../../../components/table/table-cells/discount/status-cell"
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Discount>()
|
|
||||||
|
|
||||||
export const useDiscountTableColumns = () => {
|
|
||||||
return useMemo(
|
|
||||||
() => [
|
|
||||||
columnHelper.display({
|
|
||||||
id: "discount",
|
|
||||||
header: () => <CodeHeader />,
|
|
||||||
cell: ({ row }) => <CodeCell discount={row.original} />,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor("rule.description", {
|
|
||||||
header: () => <DescriptionHeader />,
|
|
||||||
cell: ({ row }) => <DescriptionCell discount={row.original} />,
|
|
||||||
}),
|
|
||||||
|
|
||||||
columnHelper.accessor("rule.value", {
|
|
||||||
header: () => <ValueHeader />,
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<ValueCell
|
|
||||||
rule={row.original.rule}
|
|
||||||
currencyCode={row.original.regions[0]?.currency_code}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.display({
|
|
||||||
id: "status",
|
|
||||||
header: () => <StatusHeader />,
|
|
||||||
cell: ({ row }) => <StatusCell discount={row.original} />,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor("usage_count", {
|
|
||||||
header: () => <RedemptionHeader />,
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<RedemptionCell redemptions={row.original.usage_count} />
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
[]
|
|
||||||
) as ColumnDef<Discount>[]
|
|
||||||
}
|
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
|
import { useMemo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { DateCell } from "../../../components/table/table-cells/common/date-cell"
|
||||||
|
import { TextCell } from "../../../components/table/table-cells/common/text-cell"
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<HttpTypes.AdminProductTag>()
|
||||||
|
|
||||||
|
export const useProductTagTableColumns = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.accessor("value", {
|
||||||
|
header: () => t("fields.value"),
|
||||||
|
cell: ({ getValue }) => <TextCell text={getValue()} />,
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("created_at", {
|
||||||
|
header: () => t("fields.createdAt"),
|
||||||
|
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
return <DateCell date={getValue()} />
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("updated_at", {
|
||||||
|
header: () => t("fields.updatedAt"),
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
return <DateCell date={getValue()} />
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[t]
|
||||||
|
)
|
||||||
|
}
|
||||||
+7
-14
@@ -2,8 +2,9 @@ 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"
|
||||||
import { ProductTypeRowActions } from "./product-table-row-actions"
|
|
||||||
import { DateCell } from "../../../../../components/table/table-cells/common/date-cell"
|
import { DateCell } from "../../../components/table/table-cells/common/date-cell"
|
||||||
|
import { TextCell } from "../../../components/table/table-cells/common/text-cell"
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<HttpTypes.AdminProductType>()
|
const columnHelper = createColumnHelper<HttpTypes.AdminProductType>()
|
||||||
|
|
||||||
@@ -13,28 +14,20 @@ export const useProductTypeTableColumns = () => {
|
|||||||
return useMemo(
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
columnHelper.accessor("value", {
|
columnHelper.accessor("value", {
|
||||||
header: () => t("productTypes.fields.value"),
|
header: () => t("fields.value"),
|
||||||
cell: ({ getValue }) => getValue(),
|
cell: ({ getValue }) => <TextCell text={getValue()} />,
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("created_at", {
|
columnHelper.accessor("created_at", {
|
||||||
header: () => t("fields.createdAt"),
|
header: () => t("fields.createdAt"),
|
||||||
|
|
||||||
cell: ({ getValue }) => {
|
cell: ({ getValue }) => {
|
||||||
const date = new Date(getValue())
|
return <DateCell date={getValue()} />
|
||||||
return <DateCell date={date} />
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("updated_at", {
|
columnHelper.accessor("updated_at", {
|
||||||
header: () => t("fields.updatedAt"),
|
header: () => t("fields.updatedAt"),
|
||||||
cell: ({ getValue }) => {
|
cell: ({ getValue }) => {
|
||||||
const date = new Date(getValue())
|
return <DateCell date={getValue()} />
|
||||||
return <DateCell date={date} />
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
columnHelper.display({
|
|
||||||
id: "actions",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return <ProductTypeRowActions productType={row.original} />
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export * from "./use-collection-table-filters"
|
||||||
|
export * from "./use-customer-group-table-filters"
|
||||||
|
export * from "./use-customer-table-filters"
|
||||||
|
export * from "./use-date-table-filters"
|
||||||
|
export * from "./use-order-table-filters"
|
||||||
|
export * from "./use-product-table-filters"
|
||||||
|
export * from "./use-product-tag-table-filters"
|
||||||
|
export * from "./use-product-type-table-filters"
|
||||||
|
export * from "./use-promotion-table-filters"
|
||||||
|
export * from "./use-region-table-filters"
|
||||||
|
export * from "./use-sales-channel-table-filters"
|
||||||
|
export * from "./use-shipping-option-table-filters"
|
||||||
|
export * from "./use-tax-rate-table-filters"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { useDateTableFilters } from "./use-date-table-filters"
|
||||||
|
|
||||||
|
export const useCollectionTableFilters = () => {
|
||||||
|
const dateFilters = useDateTableFilters()
|
||||||
|
|
||||||
|
return dateFilters
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { Filter } from "../../../components/table/data-table"
|
|
||||||
|
|
||||||
export const useDiscountTableFilters = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
let filters: Filter[] = [
|
|
||||||
{ label: t("fields.createdAt"), key: "created_at" },
|
|
||||||
{ label: t("fields.updatedAt"), key: "updated_at" },
|
|
||||||
].map((f) => ({
|
|
||||||
key: f.key,
|
|
||||||
label: f.label,
|
|
||||||
type: "date",
|
|
||||||
}))
|
|
||||||
|
|
||||||
const isDisabledFilter: Filter = {
|
|
||||||
key: "is_disabled",
|
|
||||||
label: t("fields.disabled"),
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: t("fields.true"),
|
|
||||||
value: "true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("fields.false"),
|
|
||||||
value: "false",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDynamicFilter: Filter = {
|
|
||||||
key: "is_dynamic",
|
|
||||||
label: t("fields.type"),
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: t("fields.dynamic"),
|
|
||||||
value: "true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("fields.normal"),
|
|
||||||
value: "false",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
filters = [...filters, isDisabledFilter, isDynamicFilter]
|
|
||||||
|
|
||||||
return filters
|
|
||||||
}
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import { useDateTableFilters } from "./use-date-table-filters"
|
||||||
|
|
||||||
|
export const useProductTagTableFilters = () => {
|
||||||
|
const dateFilters = useDateTableFilters()
|
||||||
|
|
||||||
|
return dateFilters
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { useDateTableFilters } from "../../../../../hooks/table/filters/use-date-table-filters"
|
import { useDateTableFilters } from "./use-date-table-filters"
|
||||||
|
|
||||||
export const useProductTypeTableFilters = () => {
|
export const useProductTypeTableFilters = () => {
|
||||||
const dateFilters = useDateTableFilters()
|
const dateFilters = useDateTableFilters()
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export * from "./use-campaign-table-query"
|
||||||
|
export * from "./use-collection-table-query"
|
||||||
|
export * from "./use-customer-group-table-query"
|
||||||
|
export * from "./use-customer-table-query"
|
||||||
|
export * from "./use-order-table-query"
|
||||||
|
export * from "./use-product-table-query"
|
||||||
|
export * from "./use-product-tag-table-query"
|
||||||
|
export * from "./use-product-type-table-query"
|
||||||
|
export * from "./use-region-table-query"
|
||||||
|
export * from "./use-sales-channel-table-query"
|
||||||
|
export * from "./use-shipping-option-table-query"
|
||||||
|
export * from "./use-tax-rate-table-query"
|
||||||
|
export * from "./use-tax-region-table-query"
|
||||||
|
export * from "./use-user-invite-table-query"
|
||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
import { AdminCollectionFilters, FindParams } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
import { useQueryParams } from "../../use-query-params"
|
||||||
|
|
||||||
type UseCollectionTableQueryProps = {
|
type UseCollectionTableQueryProps = {
|
||||||
prefix?: string
|
prefix?: string
|
||||||
@@ -17,7 +17,7 @@ export const useCollectionTableQuery = ({
|
|||||||
|
|
||||||
const { offset, created_at, updated_at, q, order } = queryObject
|
const { offset, created_at, updated_at, q, order } = queryObject
|
||||||
|
|
||||||
const searchParams: FindParams & AdminCollectionFilters = {
|
const searchParams: HttpTypes.AdminCollectionListParams = {
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
offset: offset ? Number(offset) : 0,
|
offset: offset ? Number(offset) : 0,
|
||||||
order,
|
order,
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import { AdminGetDiscountsParams } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
import { useQueryParams } from "../../use-query-params"
|
|
||||||
|
|
||||||
type UseDiscountTableQueryProps = {
|
|
||||||
prefix?: string
|
|
||||||
pageSize?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useDiscountTableQuery = ({
|
|
||||||
prefix,
|
|
||||||
pageSize = 20,
|
|
||||||
}: UseDiscountTableQueryProps) => {
|
|
||||||
const queryObject = useQueryParams(
|
|
||||||
[
|
|
||||||
"offset",
|
|
||||||
"order",
|
|
||||||
"q",
|
|
||||||
"is_dynamic",
|
|
||||||
"is_disabled",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
],
|
|
||||||
prefix
|
|
||||||
)
|
|
||||||
|
|
||||||
const { offset, order, q, is_dynamic, is_disabled, created_at, updated_at } =
|
|
||||||
queryObject
|
|
||||||
|
|
||||||
const searchParams: AdminGetDiscountsParams = {
|
|
||||||
limit: pageSize,
|
|
||||||
is_disabled: is_disabled ? is_disabled === "true" : undefined,
|
|
||||||
is_dynamic: is_dynamic ? is_dynamic === "true" : undefined,
|
|
||||||
created_at: created_at ? JSON.parse(created_at) : undefined,
|
|
||||||
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
|
|
||||||
offset: offset ? Number(offset) : 0,
|
|
||||||
order,
|
|
||||||
q,
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
searchParams,
|
|
||||||
raw: queryObject,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { useQueryParams } from "../../use-query-params"
|
||||||
|
|
||||||
|
type UseProductTagTableQueryProps = {
|
||||||
|
prefix?: string
|
||||||
|
pageSize?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useProductTagTableQuery = ({
|
||||||
|
prefix,
|
||||||
|
pageSize = 20,
|
||||||
|
}: UseProductTagTableQueryProps) => {
|
||||||
|
const queryObject = useQueryParams(
|
||||||
|
["offset", "q", "order", "created_at", "updated_at"],
|
||||||
|
prefix
|
||||||
|
)
|
||||||
|
|
||||||
|
const { offset, q, order, created_at, updated_at } = queryObject
|
||||||
|
const searchParams: HttpTypes.AdminProductTagListParams = {
|
||||||
|
limit: pageSize,
|
||||||
|
offset: offset ? Number(offset) : 0,
|
||||||
|
order,
|
||||||
|
created_at: created_at ? JSON.parse(created_at) : undefined,
|
||||||
|
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
|
||||||
|
q,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchParams,
|
||||||
|
raw: queryObject,
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -1,4 +1,5 @@
|
|||||||
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { useQueryParams } from "../../use-query-params"
|
||||||
|
|
||||||
type UseProductTypeTableQueryProps = {
|
type UseProductTypeTableQueryProps = {
|
||||||
prefix?: string
|
prefix?: string
|
||||||
@@ -15,7 +16,7 @@ export const useProductTypeTableQuery = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const { offset, q, order, created_at, updated_at } = queryObject
|
const { offset, q, order, created_at, updated_at } = queryObject
|
||||||
const searchParams = {
|
const searchParams: HttpTypes.AdminProductTypeListParams = {
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
offset: offset ? Number(offset) : 0,
|
offset: offset ? Number(offset) : 0,
|
||||||
order,
|
order,
|
||||||
File renamed without changes.
@@ -98,7 +98,8 @@
|
|||||||
"add": "Add",
|
"add": "Add",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
"browse": "Browse",
|
"browse": "Browse",
|
||||||
"logout": "Logout"
|
"logout": "Logout",
|
||||||
|
"hide": "Hide"
|
||||||
},
|
},
|
||||||
"operators": {
|
"operators": {
|
||||||
"in": "In"
|
"in": "In"
|
||||||
@@ -1049,127 +1050,239 @@
|
|||||||
"type": "Enter shipping profile type, for example: Heavy, Oversized, Freight-only, etc."
|
"type": "Enter shipping profile type, for example: Heavy, Oversized, Freight-only, etc."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"discounts": {
|
|
||||||
"domain": "Discounts",
|
|
||||||
"startDate": "Start date",
|
|
||||||
"createDiscountTitle": "Create Discount",
|
|
||||||
"validDuration": "Duration of the discount",
|
|
||||||
"redemptionsLimit": "Redemptions limit",
|
|
||||||
"endDate": "End date",
|
|
||||||
"type": "Discount type",
|
|
||||||
"percentageDiscount": "Percentage discount",
|
|
||||||
"freeShipping": "Free shipping",
|
|
||||||
"fixedDiscount": "Fixed discount",
|
|
||||||
"fixedAmount": "Fixed amount",
|
|
||||||
"validRegions": "Valid regions",
|
|
||||||
"deleteWarning": "You are about to delete the discount {{code}}. This action cannot be undone.",
|
|
||||||
"editDiscountDetails": "Edit Discount Details",
|
|
||||||
"editDiscountConfiguration": "Edit Discount Configuration",
|
|
||||||
"hasStartDate": "Discount has a start date",
|
|
||||||
"hasEndDate": "Discount has an expiry date",
|
|
||||||
"startDateHint": "Schedule the discount to activate in the future.",
|
|
||||||
"endDateHint": "Schedule the discount to deactivate in the future.",
|
|
||||||
"codeHint": "Discount code applies from when you hit the publish button and forever if left untouched.",
|
|
||||||
"hasUsageLimit": "Limit the number of redemptions?",
|
|
||||||
"usageLimitHint": "Limit applies across all customers, not per customer.",
|
|
||||||
"titleHint": "The code your customers will enter during checkout. This will appear on your customer's invoice.\nUppercase letters and numbers only.",
|
|
||||||
"hasDurationLimit": "Availability duration",
|
|
||||||
"durationHint": "Set the duration of the discount",
|
|
||||||
"chooseValidRegions": "Choose valid regions",
|
|
||||||
"conditionsHint": "Create conditions to apply on the discount",
|
|
||||||
"isTemplateDiscount": "Is this a template discount?",
|
|
||||||
"percentageDescription": "Discount applied in %",
|
|
||||||
"fixedDescription": "Amount discount",
|
|
||||||
"shippingDescription": "Override delivery amount",
|
|
||||||
"selectRegionFirst": "Select a region first",
|
|
||||||
"templateHint": "Template discounts allow you to define a set of rules that can be used across a group of discounts. This is useful in campaigns that should generate unique codes for each user, but where the rules for all unique codes should be the same.",
|
|
||||||
"conditions": {
|
|
||||||
"editHeader": "Edit Discount Conditions",
|
|
||||||
"editHint": "Specify conditions for when the discount can be applied to a cart.",
|
|
||||||
"manageTypesAction": "Manage condition types",
|
|
||||||
"including": {
|
|
||||||
"products_one": "Discount applies to <0/> product",
|
|
||||||
"products_other": "Discount applies to <0/> products",
|
|
||||||
"customer_groups_one": "Discount applies to <0/> customer group",
|
|
||||||
"customer_groups_other": "Discount applies to <0/> customer groups",
|
|
||||||
"product_tags_one": "Discount applies to <0/> tag",
|
|
||||||
"product_tags_other": "Discount applies to <0/> tags",
|
|
||||||
"product_collections_one": "Discount applies to <0/> product collection",
|
|
||||||
"product_collections_other": "Discount applies to <0/> product collections",
|
|
||||||
"product_types_one": "Discount applies to <0/> product type",
|
|
||||||
"product_types_other": "Discount applies to <0/> product types"
|
|
||||||
},
|
|
||||||
"excluding": {
|
|
||||||
"products": "Discount applies to <1>all</1> products except <0/>",
|
|
||||||
"customer_groups": "Discount applies to <1>all</1> customer groups except <0/>",
|
|
||||||
"product_tags": "Discount applies to <1>all</1> product tags except <0/>",
|
|
||||||
"product_collections": "Discount applies to <1>all</1> product collections except <0/>",
|
|
||||||
"product_types": "Discount applies to <1>all</1> product types except <0/>"
|
|
||||||
},
|
|
||||||
"edit": {
|
|
||||||
"appliesTo": "Discount applies to",
|
|
||||||
"except": {
|
|
||||||
"products_one": "product, except",
|
|
||||||
"products_other": "products, except",
|
|
||||||
"product_tags_one": "product tag, except",
|
|
||||||
"product_tags_other": "product tags, except",
|
|
||||||
"product_types_one": "product type, except",
|
|
||||||
"product_types_other": "product types, except",
|
|
||||||
"product_collections_one": "product collection, except",
|
|
||||||
"product_collections_other": "product collections, except",
|
|
||||||
"customer_groups_one": "customer group, except",
|
|
||||||
"customer_groups_other": "customer groups, except"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"discountStatus": {
|
|
||||||
"scheduled": "Scheduled",
|
|
||||||
"expired": "Expired",
|
|
||||||
"active": "Active",
|
|
||||||
"disabled": "Disabled"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"taxRates": {
|
|
||||||
"domain": "Tax Rates",
|
|
||||||
"fields": {
|
|
||||||
"isCombinable": "Is combinable?",
|
|
||||||
"appliesTo": "Tax Rate applies to",
|
|
||||||
"customer_groups": "Customer Group",
|
|
||||||
"product_collections": "Product Collection",
|
|
||||||
"product_tags": "Product Tag",
|
|
||||||
"product_types": "Product Type",
|
|
||||||
"products": "Product"
|
|
||||||
},
|
|
||||||
"edit": {
|
|
||||||
"title": "Edit Tax Rate",
|
|
||||||
"description": "Edits the tax rate of a tax region"
|
|
||||||
},
|
|
||||||
"create": {
|
|
||||||
"title": "Create Tax Rate Override",
|
|
||||||
"description": "Create a tax rate that overrides the default tax rates for selected conditions."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"taxRegions": {
|
"taxRegions": {
|
||||||
"domain": "Tax Regions",
|
"domain": "Tax Regions",
|
||||||
"subtitle": "Manage tax rates and settings for each region.",
|
"list": {
|
||||||
"description": "Manage your region's tax structure",
|
"hint": "Manage what you charge your customers when they shop from different countries and regions."
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"confirmation": "You are about to delete a tax region. This action cannot be undone.",
|
||||||
|
"successToast": "The tax region was successfully deleted."
|
||||||
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Create Tax Region",
|
"header": "Create Tax Region",
|
||||||
"description": "Creates a tax region with a default tax rate."
|
"hint": "Create a new tax region to define tax rates for a specific country.",
|
||||||
},
|
"errors": {
|
||||||
"create-child": {
|
"rateIsRequired": "Tax rate is required when creating a default tax rate.",
|
||||||
"title": "Create Default Rate for Province",
|
"nameIsRequired": "Name is required when creating a default tax rate."
|
||||||
"description": "Creates a tax region for a province with default tax rate"
|
|
||||||
},
|
|
||||||
"removeWarning": "You are about to remove {{tax_region_name}}. This action cannot be undone.",
|
|
||||||
"fields": {
|
|
||||||
"rate": {
|
|
||||||
"name": "Rate",
|
|
||||||
"hint": "Tax rate to apply for a region or province"
|
|
||||||
},
|
},
|
||||||
"is_combinable": {
|
"successToast": "The tax region was successfully created."
|
||||||
"name": "Is combinable",
|
},
|
||||||
"hint": "Whether this tax rate can be combined with the default rate from province or parent tax region."
|
"province": {
|
||||||
|
"header": "Provinces",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Province Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific province."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"header": "States",
|
||||||
|
"create": {
|
||||||
|
"header": "Create State Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific state."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stateOrTerritory": {
|
||||||
|
"header": "States or Territories",
|
||||||
|
"create": {
|
||||||
|
"header": "Create State/Territory Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific state/territory."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"county": {
|
||||||
|
"header": "Counties",
|
||||||
|
"create": {
|
||||||
|
"header": "Create County Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific county."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"header": "Regions",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Region Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific region."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"department": {
|
||||||
|
"header": "Departments",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Department Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific department."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"territory": {
|
||||||
|
"header": "Territories",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Territory Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific territory."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefecture": {
|
||||||
|
"header": "Prefectures",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Prefecture Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific prefecture."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"district": {
|
||||||
|
"header": "Districts",
|
||||||
|
"create": {
|
||||||
|
"header": "Create District Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific district."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"governorate": {
|
||||||
|
"header": "Governorates",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Governorate Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific governorate."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"canton": {
|
||||||
|
"header": "Cantons",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Canton Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific canton."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emirate": {
|
||||||
|
"header": "Emirates",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Emirate Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific emirate."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sublevel": {
|
||||||
|
"header": "Sublevels",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Sublevel Tax Region",
|
||||||
|
"hint": "Create a new tax region to define tax rates for a specific sublevel."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"taxOverrides": {
|
||||||
|
"header": "Overrides",
|
||||||
|
"create": {
|
||||||
|
"header": "Create Override",
|
||||||
|
"hint": "Create a tax rate that overrides the default tax rates for selected conditions."
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"header": "Edit Override",
|
||||||
|
"hint": "Edit the tax rate that overrides the default tax rates for selected conditions."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"taxRates": {
|
||||||
|
"create": {
|
||||||
|
"header": "Create Tax Rate",
|
||||||
|
"hint": "Create a new tax rate to define the tax rate for a region.",
|
||||||
|
"successToast": "Tax rate was successfully created."
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"header": "Edit Tax Rate",
|
||||||
|
"hint": "Edit the tax rate to define the tax rate for a region.",
|
||||||
|
"successToast": "Tax rate was successfully updated."
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"confirmation": "You are about to delete the tax rate {{name}}. This action cannot be undone.",
|
||||||
|
"successToast": "Tax rate was successfully deleted."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"isCombinable": {
|
||||||
|
"label": "Combinable",
|
||||||
|
"hint": "Whether this tax rate can be combined with the default rate from the tax region.",
|
||||||
|
"true": "Combinable",
|
||||||
|
"false": "Not combinable"
|
||||||
|
},
|
||||||
|
"defaultTaxRate": {
|
||||||
|
"label": "Default tax rate",
|
||||||
|
"tooltip": "The default tax rate for this region. An example is the standard VAT rate for a country or region.",
|
||||||
|
"action": "Create default tax rate"
|
||||||
|
},
|
||||||
|
"taxRate": "Tax rate",
|
||||||
|
"taxCode": "Tax code",
|
||||||
|
"targets": {
|
||||||
|
"label": "Targets",
|
||||||
|
"hint": "Select the targets that this tax rate will apply to.",
|
||||||
|
"options": {
|
||||||
|
"product": "Products",
|
||||||
|
"productCollection": "Product collections",
|
||||||
|
"productTag": "Product tags",
|
||||||
|
"productType": "Product types",
|
||||||
|
"customerGroup": "Customer groups"
|
||||||
|
},
|
||||||
|
"operators": {
|
||||||
|
"in": "in",
|
||||||
|
"on": "on",
|
||||||
|
"and": "and"
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"product": "Search for products",
|
||||||
|
"productCollection": "Search for product collections",
|
||||||
|
"productTag": "Search for product tags",
|
||||||
|
"productType": "Search for product types",
|
||||||
|
"customerGroup": "Search for customer groups"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"product": "Product",
|
||||||
|
"productCollection": "Product collection",
|
||||||
|
"productTag": "Product tag",
|
||||||
|
"productType": "Product type",
|
||||||
|
"customerGroup": "Customer group"
|
||||||
|
},
|
||||||
|
"modal": {
|
||||||
|
"header": "Add targets"
|
||||||
|
},
|
||||||
|
"values_one": "{{count}} value",
|
||||||
|
"values_other": "{{count}} values",
|
||||||
|
"numberOfTargets_one": "{{count}} target",
|
||||||
|
"numberOfTargets_other": "{{count}} targets",
|
||||||
|
"additionalValues_one": "and {{count}} more value",
|
||||||
|
"additionalValues_other": "and {{count}} more values",
|
||||||
|
"action": "Add target"
|
||||||
|
},
|
||||||
|
"sublevels": {
|
||||||
|
"labels": {
|
||||||
|
"province": "Province",
|
||||||
|
"state": "State",
|
||||||
|
"region": "Region",
|
||||||
|
"stateOrTerritory": "State/Territory",
|
||||||
|
"department": "Department",
|
||||||
|
"county": "County",
|
||||||
|
"territory": "Territory",
|
||||||
|
"prefecture": "Prefecture",
|
||||||
|
"district": "District",
|
||||||
|
"governorate": "Governorate",
|
||||||
|
"emirate": "Emirate",
|
||||||
|
"canton": "Canton",
|
||||||
|
"sublevel": "Sublevel code"
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"province": "Select province",
|
||||||
|
"state": "Select state",
|
||||||
|
"region": "Select region",
|
||||||
|
"stateOrTerritory": "Select state/territory",
|
||||||
|
"department": "Select department",
|
||||||
|
"county": "Select county",
|
||||||
|
"territory": "Select territory",
|
||||||
|
"prefecture": "Select prefecture",
|
||||||
|
"district": "Select district",
|
||||||
|
"governorate": "Select governorate",
|
||||||
|
"emirate": "Select emirate",
|
||||||
|
"canton": "Select canton"
|
||||||
|
},
|
||||||
|
"tooltips": {
|
||||||
|
"sublevel": "Enter the ISO 3166-2 code for the sublevel tax region.",
|
||||||
|
"notPartOfCountry": "{{province}} does not appear to be part of {{country}}. Please double-check if this is correct."
|
||||||
|
},
|
||||||
|
"alert": {
|
||||||
|
"header": "Sublevel regions are disabled for this tax region",
|
||||||
|
"description": "Sublevel regions are disabled for this region by default. You can enable them to create sublevel regions like provinces, states, or territories.",
|
||||||
|
"action": "Enable sublevel regions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"noDefaultRate": {
|
||||||
|
"label": "No default rate",
|
||||||
|
"tooltip": "This tax region does not have a default tax rate. If there is a standard rate, such as a country's VAT, please add it to this region."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1985,8 +2098,6 @@
|
|||||||
"note": "Note",
|
"note": "Note",
|
||||||
"automaticTaxes": "Automatic Taxes",
|
"automaticTaxes": "Automatic Taxes",
|
||||||
"taxInclusivePricing": "Tax inclusive pricing",
|
"taxInclusivePricing": "Tax inclusive pricing",
|
||||||
"taxRate": "Tax Rate",
|
|
||||||
"taxCode": "Tax Code",
|
|
||||||
"currency": "Currency",
|
"currency": "Currency",
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
"address2": "Apartment, suite, etc.",
|
"address2": "Apartment, suite, etc.",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { AddressDTO } from "@medusajs/types"
|
import { AddressDTO } from "@medusajs/types"
|
||||||
|
|
||||||
import { countries } from "./countries"
|
import { countries } from "./data/countries"
|
||||||
|
|
||||||
export const isSameAddress = (a: AddressDTO | null, b: AddressDTO | null) => {
|
export const isSameAddress = (a: AddressDTO | null, b: AddressDTO | null) => {
|
||||||
if (!a || !b) {
|
if (!a || !b) {
|
||||||
|
|||||||
File renamed without changes.
File diff suppressed because it is too large.
Load diff
File renamed without changes.
@@ -1,32 +0,0 @@
|
|||||||
import { Discount } from "@medusajs/medusa"
|
|
||||||
import { end, parse } from "iso8601-duration"
|
|
||||||
|
|
||||||
export enum PromotionStatus {
|
|
||||||
SCHEDULED = "SCHEDULED",
|
|
||||||
EXPIRED = "EXPIRED",
|
|
||||||
ACTIVE = "ACTIVE",
|
|
||||||
DISABLED = "DISABLED",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getDiscountStatus = (discount: Discount) => {
|
|
||||||
if (discount.is_disabled) {
|
|
||||||
return PromotionStatus.DISABLED
|
|
||||||
}
|
|
||||||
|
|
||||||
const date = new Date()
|
|
||||||
if (new Date(discount.starts_at) > date) {
|
|
||||||
return PromotionStatus.SCHEDULED
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(discount.ends_at && new Date(discount.ends_at) < date) ||
|
|
||||||
(discount.valid_duration &&
|
|
||||||
date >
|
|
||||||
end(parse(discount.valid_duration), new Date(discount.starts_at))) ||
|
|
||||||
discount.usage_count === discount.usage_limit
|
|
||||||
) {
|
|
||||||
return PromotionStatus.EXPIRED
|
|
||||||
}
|
|
||||||
|
|
||||||
return PromotionStatus.ACTIVE
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { currencies } from "./currencies"
|
import { currencies } from "./data/currencies"
|
||||||
|
|
||||||
export const getDecimalDigits = (currency: string) => {
|
export const getDecimalDigits = (currency: string) => {
|
||||||
return currencies[currency.toUpperCase()]?.decimal_digits ?? 0
|
return currencies[currency.toUpperCase()]?.decimal_digits ?? 0
|
||||||
@@ -15,7 +15,7 @@ export const getDecimalDigits = (currency: string) => {
|
|||||||
* getFormattedAmount(10, "usd") // '10,00 $' if the browser's locale is fr-FR
|
* getFormattedAmount(10, "usd") // '10,00 $' if the browser's locale is fr-FR
|
||||||
*/
|
*/
|
||||||
export const getLocaleAmount = (amount: number, currencyCode: string) => {
|
export const getLocaleAmount = (amount: number, currencyCode: string) => {
|
||||||
const formatter = new Intl.NumberFormat(undefined, {
|
const formatter = new Intl.NumberFormat([], {
|
||||||
style: "currency",
|
style: "currency",
|
||||||
currencyDisplay: "narrowSymbol",
|
currencyDisplay: "narrowSymbol",
|
||||||
currency: currencyCode,
|
currency: currencyCode,
|
||||||
@@ -25,7 +25,7 @@ export const getLocaleAmount = (amount: number, currencyCode: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getNativeSymbol = (currencyCode: string) => {
|
export const getNativeSymbol = (currencyCode: string) => {
|
||||||
const formatted = new Intl.NumberFormat(undefined, {
|
const formatted = new Intl.NumberFormat([], {
|
||||||
style: "currency",
|
style: "currency",
|
||||||
currency: currencyCode,
|
currency: currencyCode,
|
||||||
currencyDisplay: "narrowSymbol",
|
currencyDisplay: "narrowSymbol",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const formatter = new Intl.NumberFormat(undefined, {
|
const formatter = new Intl.NumberFormat([], {
|
||||||
style: "percent",
|
style: "percent",
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ export const useGlobalShortcuts = () => {
|
|||||||
},
|
},
|
||||||
label: t("app.keyboardShortcuts.goToTaxRegions"),
|
label: t("app.keyboardShortcuts.goToTaxRegions"),
|
||||||
type: "settingShortcut",
|
type: "settingShortcut",
|
||||||
callback: () => navigate("/settings/taxes"),
|
callback: () => navigate("/settings/tax-regions"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keys: {
|
keys: {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
AdminApiKeyResponse,
|
AdminApiKeyResponse,
|
||||||
AdminProductCategoryResponse,
|
AdminProductCategoryResponse,
|
||||||
AdminTaxRateResponse,
|
|
||||||
AdminTaxRegionResponse,
|
AdminTaxRegionResponse,
|
||||||
HttpTypes,
|
HttpTypes,
|
||||||
SalesChannelDTO,
|
SalesChannelDTO,
|
||||||
UserDTO,
|
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { Outlet, RouteObject } from "react-router-dom"
|
import { Outlet, RouteObject } from "react-router-dom"
|
||||||
|
|
||||||
@@ -15,6 +13,12 @@ import { SettingsLayout } from "../../components/layout/settings-layout"
|
|||||||
import { ErrorBoundary } from "../../components/utilities/error-boundary"
|
import { ErrorBoundary } from "../../components/utilities/error-boundary"
|
||||||
import { PriceListRes } from "../../types/api-responses"
|
import { PriceListRes } from "../../types/api-responses"
|
||||||
|
|
||||||
|
import { getCountryByIso2 } from "../../lib/data/countries"
|
||||||
|
import {
|
||||||
|
getProvinceByIso2,
|
||||||
|
isProvinceInCountry,
|
||||||
|
} from "../../lib/data/country-states"
|
||||||
|
import { taxRegionLoader } from "../../routes/tax-regions/tax-region-detail/loader"
|
||||||
import { RouteExtensions } from "./route-extensions"
|
import { RouteExtensions } from "./route-extensions"
|
||||||
import { SettingsExtensions } from "./settings-extensions"
|
import { SettingsExtensions } from "./settings-extensions"
|
||||||
|
|
||||||
@@ -1074,60 +1078,116 @@ export const RouteMap: RouteObject[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "taxes",
|
path: "tax-regions",
|
||||||
element: <Outlet />,
|
element: <Outlet />,
|
||||||
handle: {
|
handle: {
|
||||||
crumb: () => "Taxes",
|
crumb: () => "Tax Regions",
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
lazy: () => import("../../routes/taxes/tax-region-list"),
|
lazy: () => import("../../routes/tax-regions/tax-region-list"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "create",
|
path: "create",
|
||||||
lazy: () => import("../../routes/taxes/tax-region-create"),
|
lazy: () =>
|
||||||
children: [],
|
import("../../routes/tax-regions/tax-region-create"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ":id",
|
path: ":id",
|
||||||
lazy: () => import("../../routes/taxes/tax-region-detail"),
|
Component: Outlet,
|
||||||
|
loader: taxRegionLoader,
|
||||||
handle: {
|
handle: {
|
||||||
crumb: (data: AdminTaxRegionResponse) => {
|
crumb: (data: AdminTaxRegionResponse) => {
|
||||||
return data.tax_region.country_code
|
return (
|
||||||
|
getCountryByIso2(data.tax_region.country_code)
|
||||||
|
?.display_name ||
|
||||||
|
data.tax_region.country_code?.toUpperCase()
|
||||||
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "create-default",
|
path: "",
|
||||||
lazy: () =>
|
lazy: () =>
|
||||||
import("../../routes/taxes/tax-province-create"),
|
import("../../routes/tax-regions/tax-region-detail"),
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "create-override",
|
|
||||||
lazy: () => import("../../routes/taxes/tax-rate-create"),
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "tax-rates",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: ":taxRateId",
|
path: "provinces/create",
|
||||||
children: [
|
lazy: () =>
|
||||||
{
|
import(
|
||||||
path: "edit",
|
"../../routes/tax-regions/tax-region-province-create"
|
||||||
lazy: () =>
|
),
|
||||||
import("../../routes/taxes/tax-rate-edit"),
|
},
|
||||||
handle: {
|
{
|
||||||
crumb: (data: AdminTaxRateResponse) => {
|
path: "overrides/create",
|
||||||
return data.tax_rate.code
|
lazy: () =>
|
||||||
},
|
import(
|
||||||
},
|
"../../routes/tax-regions/tax-region-tax-override-create"
|
||||||
},
|
),
|
||||||
],
|
},
|
||||||
|
{
|
||||||
|
path: "overrides/:tax_rate_id/edit",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/tax-regions/tax-region-tax-override-edit"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "tax-rates/create",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/tax-regions/tax-region-tax-rate-create"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "tax-rates/:tax_rate_id/edit",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/tax-regions/tax-region-tax-rate-edit"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "provinces/:province_id",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/tax-regions/tax-region-province-detail"
|
||||||
|
),
|
||||||
|
handle: {
|
||||||
|
crumb: (data: AdminTaxRegionResponse) => {
|
||||||
|
const countryCode =
|
||||||
|
data.tax_region.country_code?.toUpperCase()
|
||||||
|
const provinceCode =
|
||||||
|
data.tax_region.province_code?.toUpperCase()
|
||||||
|
|
||||||
|
const isValid = isProvinceInCountry(
|
||||||
|
countryCode,
|
||||||
|
provinceCode
|
||||||
|
)
|
||||||
|
|
||||||
|
return isValid
|
||||||
|
? getProvinceByIso2(provinceCode)
|
||||||
|
: provinceCode
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "tax-rates/create",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/tax-regions/tax-region-tax-rate-create"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "tax-rates/:tax_rate_id/edit",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../routes/tax-regions/tax-region-tax-rate-edit"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import * as zod from "zod"
|
|||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
||||||
import { useUpdateCampaign } from "../../../../../hooks/api/campaigns"
|
import { useUpdateCampaign } from "../../../../../hooks/api/campaigns"
|
||||||
import { getCurrencySymbol } from "../../../../../lib/currencies"
|
import { getCurrencySymbol } from "../../../../../lib/data/currencies"
|
||||||
|
|
||||||
type EditCampaignBudgetFormProps = {
|
type EditCampaignBudgetFormProps = {
|
||||||
campaign: CampaignResponse
|
campaign: CampaignResponse
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ import { useNavigate } from "react-router-dom"
|
|||||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
import { formatDate } from "../../../../../components/common/date"
|
import { formatDate } from "../../../../../components/common/date"
|
||||||
import { useDeleteCampaign } from "../../../../../hooks/api/campaigns"
|
import { useDeleteCampaign } from "../../../../../hooks/api/campaigns"
|
||||||
import { currencies } from "../../../../../lib/currencies"
|
import { currencies } from "../../../../../lib/data/currencies"
|
||||||
import {
|
import {
|
||||||
campaignStatus,
|
campaignStatus,
|
||||||
statusColor,
|
statusColor,
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ import { useTranslation } from "react-i18next"
|
|||||||
|
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import { useStore } from "../../../../../hooks/api/store"
|
import { useStore } from "../../../../../hooks/api/store"
|
||||||
import { currencies, getCurrencySymbol } from "../../../../../lib/currencies"
|
import { currencies, getCurrencySymbol } from "../../../../../lib/data/currencies"
|
||||||
|
|
||||||
export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => {
|
export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|||||||
+25
-4
@@ -2,13 +2,17 @@ import { Button, Container, Heading, Text } from "@medusajs/ui"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
|
import { useMemo } from "react"
|
||||||
import { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
import { useCollections } from "../../../../../hooks/api/collections"
|
import { useCollections } from "../../../../../hooks/api/collections"
|
||||||
|
import { useCollectionTableColumns } from "../../../../../hooks/table/columns/use-collection-table-columns"
|
||||||
|
import { useCollectionTableFilters } from "../../../../../hooks/table/filters"
|
||||||
|
import { useCollectionTableQuery } from "../../../../../hooks/table/query"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
import { useCollectionTableColumns } from "./use-collection-table-columns"
|
import { CollectionRowActions } from "./collection-row-actions"
|
||||||
import { useCollectionTableFilters } from "./use-collection-table-filters"
|
|
||||||
import { useCollectionTableQuery } from "./use-collection-table-query"
|
|
||||||
|
|
||||||
const PAGE_SIZE = 20
|
const PAGE_SIZE = 20
|
||||||
|
|
||||||
@@ -26,7 +30,7 @@ export const CollectionListTable = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const filters = useCollectionTableFilters()
|
const filters = useCollectionTableFilters()
|
||||||
const columns = useCollectionTableColumns()
|
const columns = useColumns()
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
data: collections ?? [],
|
data: collections ?? [],
|
||||||
@@ -71,3 +75,20 @@ export const CollectionListTable = () => {
|
|||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<HttpTypes.AdminCollection>()
|
||||||
|
|
||||||
|
const useColumns = () => {
|
||||||
|
const base = useCollectionTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
...base,
|
||||||
|
columnHelper.display({
|
||||||
|
id: "actions",
|
||||||
|
cell: ({ row }) => <CollectionRowActions collection={row.original} />,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[base]
|
||||||
|
)
|
||||||
|
}
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { Filter } from "../../../../../components/table/data-table"
|
|
||||||
|
|
||||||
export const useCollectionTableFilters = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
let filters: Filter[] = []
|
|
||||||
|
|
||||||
const dateFilters: Filter[] = [
|
|
||||||
{ label: t("fields.createdAt"), key: "created_at" },
|
|
||||||
{ label: t("fields.updatedAt"), key: "updated_at" },
|
|
||||||
].map((f) => ({
|
|
||||||
key: f.key,
|
|
||||||
label: f.label,
|
|
||||||
type: "date",
|
|
||||||
}))
|
|
||||||
|
|
||||||
filters = [...filters, ...dateFilters]
|
|
||||||
|
|
||||||
return filters
|
|
||||||
}
|
|
||||||
+1
-1
@@ -17,7 +17,7 @@ import { useDataTable } from "../../../../../hooks/use-data-table"
|
|||||||
import {
|
import {
|
||||||
StaticCountry,
|
StaticCountry,
|
||||||
countries as staticCountries,
|
countries as staticCountries,
|
||||||
} from "../../../../../lib/countries"
|
} from "../../../../../lib/data/countries"
|
||||||
import { useCountries } from "../../../../regions/common/hooks/use-countries"
|
import { useCountries } from "../../../../regions/common/hooks/use-countries"
|
||||||
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
|
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
|
||||||
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
|
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ import { getFormattedAddress } from "../../../../../lib/addresses"
|
|||||||
import {
|
import {
|
||||||
StaticCountry,
|
StaticCountry,
|
||||||
countries as staticCountries,
|
countries as staticCountries,
|
||||||
} from "../../../../../lib/countries"
|
} from "../../../../../lib/data/countries"
|
||||||
import { formatProvider } from "../../../../../lib/format-provider"
|
import { formatProvider } from "../../../../../lib/format-provider"
|
||||||
import {
|
import {
|
||||||
isOptionEnabledInStore,
|
isOptionEnabledInStore,
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ import {
|
|||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/modals"
|
} from "../../../../../components/modals"
|
||||||
import { useUpdateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets"
|
import { useUpdateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets"
|
||||||
import { countries } from "../../../../../lib/countries"
|
import { countries } from "../../../../../lib/data/countries"
|
||||||
import { GeoZoneForm } from "../../../common/components/geo-zone-form"
|
import { GeoZoneForm } from "../../../common/components/geo-zone-form"
|
||||||
|
|
||||||
const EditeServiceZoneSchema = z.object({
|
const EditeServiceZoneSchema = z.object({
|
||||||
|
|||||||
+2
-2
@@ -42,7 +42,7 @@ export const PriceListEditForm = ({ priceList }: PriceListEditFormProps) => {
|
|||||||
resolver: zodResolver(PriceListEditSchema),
|
resolver: zodResolver(PriceListEditSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync } = useUpdatePriceList(priceList.id)
|
const { mutateAsync, isPending } = useUpdatePriceList(priceList.id)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
await mutateAsync(values, {
|
await mutateAsync(values, {
|
||||||
@@ -172,7 +172,7 @@ export const PriceListEditForm = ({ priceList }: PriceListEditFormProps) => {
|
|||||||
{t("actions.cancel")}
|
{t("actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</RouteDrawer.Close>
|
</RouteDrawer.Close>
|
||||||
<Button size="small" type="submit">
|
<Button size="small" type="submit" isLoading={isPending}>
|
||||||
{t("actions.save")}
|
{t("actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+28
-4
@@ -1,13 +1,18 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { Button, Container, Heading, Text } from "@medusajs/ui"
|
import { Button, Container, Heading, Text } from "@medusajs/ui"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
|
import { useMemo } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
import { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
import { useProductTypes } from "../../../../../hooks/api/product-types"
|
import { useProductTypes } from "../../../../../hooks/api/product-types"
|
||||||
|
import { useProductTypeTableColumns } from "../../../../../hooks/table/columns/use-product-type-table-columns"
|
||||||
|
import { useProductTypeTableFilters } from "../../../../../hooks/table/filters/use-product-type-table-filters"
|
||||||
|
import { useProductTypeTableQuery } from "../../../../../hooks/table/query/use-product-type-table-query"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
import { useProductTypeTableColumns } from "./use-product-type-table-columns"
|
import { ProductTypeRowActions } from "./product-table-row-actions"
|
||||||
import { useProductTypeTableFilters } from "./use-product-type-table-filters"
|
|
||||||
import { useProductTypeTableQuery } from "./use-product-type-table-query"
|
|
||||||
|
|
||||||
const PAGE_SIZE = 20
|
const PAGE_SIZE = 20
|
||||||
|
|
||||||
@@ -25,7 +30,7 @@ export const ProductTypeListTable = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const filters = useProductTypeTableFilters()
|
const filters = useProductTypeTableFilters()
|
||||||
const columns = useProductTypeTableColumns()
|
const columns = useColumns()
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
columns,
|
columns,
|
||||||
@@ -68,3 +73,22 @@ export const ProductTypeListTable = () => {
|
|||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<HttpTypes.AdminProductType>()
|
||||||
|
|
||||||
|
const useColumns = () => {
|
||||||
|
const base = useProductTypeTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
...base,
|
||||||
|
columnHelper.display({
|
||||||
|
id: "actions",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return <ProductTypeRowActions productType={row.original} />
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[base]
|
||||||
|
)
|
||||||
|
}
|
||||||
+3
-3
@@ -26,14 +26,14 @@ import {
|
|||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { Divider } from "../../../../../components/common/divider"
|
import { Divider } from "../../../../../components/common/divider"
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import { PercentageInput } from "../../../../../components/inputs/percentage-input"
|
import { DeprecatedPercentageInput } from "../../../../../components/inputs/percentage-input"
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/modals"
|
} from "../../../../../components/modals"
|
||||||
import { useCampaigns } from "../../../../../hooks/api/campaigns"
|
import { useCampaigns } from "../../../../../hooks/api/campaigns"
|
||||||
import { useCreatePromotion } from "../../../../../hooks/api/promotions"
|
import { useCreatePromotion } from "../../../../../hooks/api/promotions"
|
||||||
import { getCurrencySymbol } from "../../../../../lib/currencies"
|
import { getCurrencySymbol } from "../../../../../lib/data/currencies"
|
||||||
import { defaultCampaignValues } from "../../../../campaigns/campaign-create/components/create-campaign-form"
|
import { defaultCampaignValues } from "../../../../campaigns/campaign-create/components/create-campaign-form"
|
||||||
import { RulesFormField } from "../../../common/edit-rules/components/rules-form-field"
|
import { RulesFormField } from "../../../common/edit-rules/components/rules-form-field"
|
||||||
import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form"
|
import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form"
|
||||||
@@ -661,7 +661,7 @@ export const CreatePromotionForm = () => {
|
|||||||
disabled={!currencyCode}
|
disabled={!currencyCode}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PercentageInput
|
<DeprecatedPercentageInput
|
||||||
key="amount"
|
key="amount"
|
||||||
className="text-right"
|
className="text-right"
|
||||||
min={0}
|
min={0}
|
||||||
|
|||||||
+9
-9
@@ -13,13 +13,13 @@ import { Trans, useTranslation } from "react-i18next"
|
|||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import { PercentageInput } from "../../../../../components/inputs/percentage-input"
|
import { DeprecatedPercentageInput } from "../../../../../components/inputs/percentage-input"
|
||||||
import {
|
import {
|
||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/modals"
|
} from "../../../../../components/modals"
|
||||||
import { useUpdatePromotion } from "../../../../../hooks/api/promotions"
|
import { useUpdatePromotion } from "../../../../../hooks/api/promotions"
|
||||||
import { getCurrencySymbol } from "../../../../../lib/currencies"
|
import { getCurrencySymbol } from "../../../../../lib/data/currencies"
|
||||||
|
|
||||||
type EditPromotionFormProps = {
|
type EditPromotionFormProps = {
|
||||||
promotion: PromotionDTO
|
promotion: PromotionDTO
|
||||||
@@ -99,7 +99,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
>
|
>
|
||||||
<RadioGroup.ChoiceBox
|
<RadioGroup.ChoiceBox
|
||||||
className={clx("basis-1/2", {
|
className={clx("basis-1/2", {
|
||||||
"border-2 border-ui-border-interactive":
|
"border-ui-border-interactive border-2":
|
||||||
"false" === field.value,
|
"false" === field.value,
|
||||||
})}
|
})}
|
||||||
value={"false"}
|
value={"false"}
|
||||||
@@ -110,7 +110,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
/>
|
/>
|
||||||
<RadioGroup.ChoiceBox
|
<RadioGroup.ChoiceBox
|
||||||
className={clx("basis-1/2", {
|
className={clx("basis-1/2", {
|
||||||
"border-2 border-ui-border-interactive":
|
"border-ui-border-interactive border-2":
|
||||||
"true" === field.value,
|
"true" === field.value,
|
||||||
})}
|
})}
|
||||||
value={"true"}
|
value={"true"}
|
||||||
@@ -172,7 +172,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
>
|
>
|
||||||
<RadioGroup.ChoiceBox
|
<RadioGroup.ChoiceBox
|
||||||
className={clx("basis-1/2", {
|
className={clx("basis-1/2", {
|
||||||
"border-2 border-ui-border-interactive":
|
"border-ui-border-interactive border-2":
|
||||||
"fixed" === field.value,
|
"fixed" === field.value,
|
||||||
})}
|
})}
|
||||||
value={"fixed"}
|
value={"fixed"}
|
||||||
@@ -184,7 +184,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
|
|
||||||
<RadioGroup.ChoiceBox
|
<RadioGroup.ChoiceBox
|
||||||
className={clx("basis-1/2", {
|
className={clx("basis-1/2", {
|
||||||
"border-2 border-ui-border-interactive":
|
"border-ui-border-interactive border-2":
|
||||||
"percentage" === field.value,
|
"percentage" === field.value,
|
||||||
})}
|
})}
|
||||||
value={"percentage"}
|
value={"percentage"}
|
||||||
@@ -227,7 +227,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
value={field.value}
|
value={field.value}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PercentageInput
|
<DeprecatedPercentageInput
|
||||||
key="amount"
|
key="amount"
|
||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
@@ -264,7 +264,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
>
|
>
|
||||||
<RadioGroup.ChoiceBox
|
<RadioGroup.ChoiceBox
|
||||||
className={clx("basis-1/2", {
|
className={clx("basis-1/2", {
|
||||||
"border-2 border-ui-border-interactive":
|
"border-ui-border-interactive border-2":
|
||||||
"each" === field.value,
|
"each" === field.value,
|
||||||
})}
|
})}
|
||||||
value={"each"}
|
value={"each"}
|
||||||
@@ -276,7 +276,7 @@ export const EditPromotionDetailsForm = ({
|
|||||||
|
|
||||||
<RadioGroup.ChoiceBox
|
<RadioGroup.ChoiceBox
|
||||||
className={clx("basis-1/2", {
|
className={clx("basis-1/2", {
|
||||||
"border-2 border-ui-border-interactive":
|
"border-ui-border-interactive border-2":
|
||||||
"across" === field.value,
|
"across" === field.value,
|
||||||
})}
|
})}
|
||||||
value={"across"}
|
value={"across"}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RegionCountryDTO } from "@medusajs/types"
|
import { RegionCountryDTO } from "@medusajs/types"
|
||||||
import { json } from "react-router-dom"
|
import { json } from "react-router-dom"
|
||||||
import { StaticCountry } from "../../../../lib/countries"
|
import { StaticCountry } from "../../../../lib/data/countries"
|
||||||
|
|
||||||
const acceptedOrderKeys = ["name", "code"]
|
const acceptedOrderKeys = ["name", "code"]
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ import { createColumnHelper } from "@tanstack/react-table"
|
|||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { StaticCountry } from "../../../../lib/countries"
|
import { StaticCountry } from "../../../../lib/data/countries"
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<StaticCountry>()
|
const columnHelper = createColumnHelper<StaticCountry>()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import {
|
|||||||
import { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
import { useUpdateRegion } from "../../../../../hooks/api/regions"
|
import { useUpdateRegion } from "../../../../../hooks/api/regions"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
import { countries as staticCountries } from "../../../../../lib/countries"
|
import { countries as staticCountries } from "../../../../../lib/data/countries"
|
||||||
import { useCountries } from "../../../common/hooks/use-countries"
|
import { useCountries } from "../../../common/hooks/use-countries"
|
||||||
import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns"
|
import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns"
|
||||||
import { useCountryTableQuery } from "../../../common/hooks/use-country-table-query"
|
import { useCountryTableQuery } from "../../../common/hooks/use-country-table-query"
|
||||||
|
|||||||
+2
-2
@@ -29,8 +29,8 @@ import {
|
|||||||
import { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
import { useCreateRegion } from "../../../../../hooks/api/regions"
|
import { useCreateRegion } from "../../../../../hooks/api/regions"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
import { countries as staticCountries } from "../../../../../lib/countries"
|
import { countries as staticCountries } from "../../../../../lib/data/countries"
|
||||||
import { CurrencyInfo } from "../../../../../lib/currencies"
|
import { CurrencyInfo } from "../../../../../lib/data/currencies"
|
||||||
import { formatProvider } from "../../../../../lib/format-provider"
|
import { formatProvider } from "../../../../../lib/format-provider"
|
||||||
import { useCountries } from "../../../common/hooks/use-countries"
|
import { useCountries } from "../../../common/hooks/use-countries"
|
||||||
import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns"
|
import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { RouteFocusModal } from "../../../components/modals/route-focus-modal"
|
import { RouteFocusModal } from "../../../components/modals/route-focus-modal"
|
||||||
import { usePaymentProviders } from "../../../hooks/api/payments"
|
import { usePaymentProviders } from "../../../hooks/api/payments"
|
||||||
import { useStore } from "../../../hooks/api/store"
|
import { useStore } from "../../../hooks/api/store"
|
||||||
import { currencies } from "../../../lib/currencies"
|
import { currencies } from "../../../lib/data/currencies"
|
||||||
import { CreateRegionForm } from "./components/create-region-form"
|
import { CreateRegionForm } from "./components/create-region-form"
|
||||||
|
|
||||||
export const RegionCreate = () => {
|
export const RegionCreate = () => {
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import { useNavigate } from "react-router-dom"
|
|||||||
import { ActionMenu } from "../../../../../components/common/action-menu/index.ts"
|
import { ActionMenu } from "../../../../../components/common/action-menu/index.ts"
|
||||||
import { ListSummary } from "../../../../../components/common/list-summary/index.ts"
|
import { ListSummary } from "../../../../../components/common/list-summary/index.ts"
|
||||||
import { useDeleteRegion } from "../../../../../hooks/api/regions.tsx"
|
import { useDeleteRegion } from "../../../../../hooks/api/regions.tsx"
|
||||||
import { currencies } from "../../../../../lib/currencies.ts"
|
import { currencies } from "../../../../../lib/data/currencies.ts"
|
||||||
import { formatProvider } from "../../../../../lib/format-provider.ts"
|
import { formatProvider } from "../../../../../lib/format-provider.ts"
|
||||||
import { SectionRow } from "../../../../../components/common/section/section-row.tsx"
|
import { SectionRow } from "../../../../../components/common/section/section-row.tsx"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ import {
|
|||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/modals/index.ts"
|
} from "../../../../../components/modals/index.ts"
|
||||||
import { useUpdateRegion } from "../../../../../hooks/api/regions.tsx"
|
import { useUpdateRegion } from "../../../../../hooks/api/regions.tsx"
|
||||||
import { CurrencyInfo } from "../../../../../lib/currencies.ts"
|
import { CurrencyInfo } from "../../../../../lib/data/currencies.ts"
|
||||||
import { formatProvider } from "../../../../../lib/format-provider.ts"
|
import { formatProvider } from "../../../../../lib/format-provider.ts"
|
||||||
|
|
||||||
type EditRegionFormProps = {
|
type EditRegionFormProps = {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { RouteDrawer } from "../../../components/modals"
|
|||||||
import { usePaymentProviders } from "../../../hooks/api/payments"
|
import { usePaymentProviders } from "../../../hooks/api/payments"
|
||||||
import { useRegion } from "../../../hooks/api/regions"
|
import { useRegion } from "../../../hooks/api/regions"
|
||||||
import { useStore } from "../../../hooks/api/store"
|
import { useStore } from "../../../hooks/api/store"
|
||||||
import { currencies } from "../../../lib/currencies"
|
import { currencies } from "../../../lib/data/currencies"
|
||||||
import { EditRegionForm } from "./components/edit-region-form"
|
import { EditRegionForm } from "./components/edit-region-form"
|
||||||
import { usePricePreferences } from "../../../hooks/api/price-preferences"
|
import { usePricePreferences } from "../../../hooks/api/price-preferences"
|
||||||
|
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./target-form"
|
||||||
+789
@@ -0,0 +1,789 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { Button, Checkbox } from "@medusajs/ui"
|
||||||
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
|
import {
|
||||||
|
OnChangeFn,
|
||||||
|
RowSelectionState,
|
||||||
|
createColumnHelper,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
import { useEffect, useMemo, useState } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
|
import { useSearchParams } from "react-router-dom"
|
||||||
|
import {
|
||||||
|
StackedDrawer,
|
||||||
|
StackedFocusModal,
|
||||||
|
} from "../../../../../components/modals"
|
||||||
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
|
import {
|
||||||
|
useCollections,
|
||||||
|
useCustomerGroups,
|
||||||
|
useProductTypes,
|
||||||
|
useProducts,
|
||||||
|
useTags,
|
||||||
|
} from "../../../../../hooks/api"
|
||||||
|
import {
|
||||||
|
useCollectionTableColumns,
|
||||||
|
useCustomerGroupTableColumns,
|
||||||
|
useProductTableColumns,
|
||||||
|
useProductTagTableColumns,
|
||||||
|
useProductTypeTableColumns,
|
||||||
|
} from "../../../../../hooks/table/columns"
|
||||||
|
import {
|
||||||
|
useCollectionTableFilters,
|
||||||
|
useCustomerGroupTableFilters,
|
||||||
|
useProductTableFilters,
|
||||||
|
useProductTagTableFilters,
|
||||||
|
useProductTypeTableFilters,
|
||||||
|
} from "../../../../../hooks/table/filters"
|
||||||
|
import {
|
||||||
|
useCollectionTableQuery,
|
||||||
|
useCustomerGroupTableQuery,
|
||||||
|
useProductTableQuery,
|
||||||
|
useProductTagTableQuery,
|
||||||
|
useProductTypeTableQuery,
|
||||||
|
} from "../../../../../hooks/table/query"
|
||||||
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
|
import { TaxRateRuleReferenceType } from "../../constants"
|
||||||
|
import { TaxRateRuleReference } from "../../schemas"
|
||||||
|
|
||||||
|
type TargetFormProps = {
|
||||||
|
referenceType: TaxRateRuleReferenceType
|
||||||
|
type: "focus" | "drawer"
|
||||||
|
state: TaxRateRuleReference[]
|
||||||
|
setState: (state: TaxRateRuleReference[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRowSelection(state: TaxRateRuleReference[]) {
|
||||||
|
return state.reduce((acc, reference) => {
|
||||||
|
acc[reference.value] = true
|
||||||
|
return acc
|
||||||
|
}, {} as RowSelectionState)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TargetForm = ({
|
||||||
|
referenceType,
|
||||||
|
type,
|
||||||
|
setState,
|
||||||
|
state,
|
||||||
|
}: TargetFormProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const Component = type === "focus" ? StackedFocusModal : StackedDrawer
|
||||||
|
|
||||||
|
const [intermediate, setIntermediate] =
|
||||||
|
useState<TaxRateRuleReference[]>(state)
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
setState(intermediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex size-full flex-col overflow-hidden">
|
||||||
|
<Component.Body className="min-h-0 p-0">
|
||||||
|
<Table
|
||||||
|
referenceType={referenceType}
|
||||||
|
initialRowState={initRowSelection(state)}
|
||||||
|
intermediate={intermediate}
|
||||||
|
setIntermediate={setIntermediate}
|
||||||
|
/>
|
||||||
|
</Component.Body>
|
||||||
|
<Component.Footer>
|
||||||
|
<Component.Close asChild>
|
||||||
|
<Button variant="secondary" size="small" type="button">
|
||||||
|
{t("actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Component.Close>
|
||||||
|
<Button type="button" size="small" onClick={handleSave}>
|
||||||
|
{t("actions.save")}
|
||||||
|
</Button>
|
||||||
|
</Component.Footer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableProps = {
|
||||||
|
referenceType: TaxRateRuleReferenceType
|
||||||
|
initialRowState: RowSelectionState
|
||||||
|
intermediate: TaxRateRuleReference[]
|
||||||
|
setIntermediate: (state: TaxRateRuleReference[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Table = ({ referenceType, ...props }: TableProps) => {
|
||||||
|
switch (referenceType) {
|
||||||
|
case TaxRateRuleReferenceType.CUSTOMER_GROUP:
|
||||||
|
return <CustomerGroupTable {...props} />
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT:
|
||||||
|
return <ProductTable {...props} />
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
|
||||||
|
return <ProductCollectionTable {...props} />
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_TYPE:
|
||||||
|
return <ProductTypeTable {...props} />
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_TAG:
|
||||||
|
return <ProductTagTable {...props} />
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableImplementationProps = {
|
||||||
|
initialRowState: RowSelectionState
|
||||||
|
intermediate: TaxRateRuleReference[]
|
||||||
|
setIntermediate: (state: TaxRateRuleReference[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE = 50
|
||||||
|
|
||||||
|
const PREFIX_CUSTOMER_GROUP = "cg"
|
||||||
|
|
||||||
|
const CustomerGroupTable = ({
|
||||||
|
initialRowState,
|
||||||
|
intermediate,
|
||||||
|
setIntermediate,
|
||||||
|
}: TableImplementationProps) => {
|
||||||
|
const [rowSelection, setRowSelection] =
|
||||||
|
useState<RowSelectionState>(initialRowState)
|
||||||
|
|
||||||
|
useCleanupSearchParams()
|
||||||
|
|
||||||
|
const { searchParams, raw } = useCustomerGroupTableQuery({
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_CUSTOMER_GROUP,
|
||||||
|
})
|
||||||
|
const { customer_groups, count, isLoading, isError, error } =
|
||||||
|
useCustomerGroups(searchParams, {
|
||||||
|
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 newCustomerGroups =
|
||||||
|
customer_groups
|
||||||
|
?.filter((cg) => newIds.includes(cg.id))
|
||||||
|
.map((cg) => ({ value: cg.id, label: cg.name! })) || []
|
||||||
|
|
||||||
|
const filteredIntermediate = intermediate.filter(
|
||||||
|
(cg) => !removedIds.includes(cg.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
setIntermediate([...filteredIntermediate, ...newCustomerGroups])
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = useCustomerGroupTableFilters()
|
||||||
|
const columns = useGroupColumns()
|
||||||
|
|
||||||
|
const { table } = useDataTable({
|
||||||
|
data: customer_groups || [],
|
||||||
|
columns,
|
||||||
|
count,
|
||||||
|
enablePagination: true,
|
||||||
|
enableRowSelection: true,
|
||||||
|
getRowId: (row) => row.id,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
|
},
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_CUSTOMER_GROUP,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
isLoading={isLoading}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={["name", "created_at", "updated_at"]}
|
||||||
|
layout="fill"
|
||||||
|
pagination
|
||||||
|
search
|
||||||
|
prefix={PREFIX_CUSTOMER_GROUP}
|
||||||
|
queryObject={raw}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cgColumnHelper = createColumnHelper<HttpTypes.AdminCustomerGroup>()
|
||||||
|
|
||||||
|
const useGroupColumns = () => {
|
||||||
|
const base = useCustomerGroupTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
cgColumnHelper.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 = "p"
|
||||||
|
|
||||||
|
const ProductTable = ({
|
||||||
|
initialRowState,
|
||||||
|
intermediate,
|
||||||
|
setIntermediate,
|
||||||
|
}: TableImplementationProps) => {
|
||||||
|
const [rowSelection, setRowSelection] =
|
||||||
|
useState<RowSelectionState>(initialRowState)
|
||||||
|
|
||||||
|
useCleanupSearchParams()
|
||||||
|
|
||||||
|
const { searchParams, raw } = useProductTableQuery({
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { products, count, isLoading, isError, error } = useProducts(
|
||||||
|
searchParams,
|
||||||
|
{
|
||||||
|
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 newProducts =
|
||||||
|
products
|
||||||
|
?.filter((p) => newIds.includes(p.id))
|
||||||
|
.map((p) => ({
|
||||||
|
value: p.id,
|
||||||
|
label: p.title!,
|
||||||
|
})) || []
|
||||||
|
|
||||||
|
const filteredIntermediate = intermediate.filter(
|
||||||
|
(p) => !removedIds.includes(p.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
setIntermediate([...filteredIntermediate, ...newProducts])
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = useProductTableFilters()
|
||||||
|
const columns = useProductColumns()
|
||||||
|
|
||||||
|
const { table } = useDataTable({
|
||||||
|
data: products || [],
|
||||||
|
columns,
|
||||||
|
count,
|
||||||
|
enablePagination: true,
|
||||||
|
enableRowSelection: true,
|
||||||
|
getRowId: (row) => row.id,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
|
},
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
isLoading={isLoading}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={["title", "created_at", "updated_at"]}
|
||||||
|
layout="fill"
|
||||||
|
pagination
|
||||||
|
search
|
||||||
|
prefix={PREFIX_PRODUCT}
|
||||||
|
queryObject={raw}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pColumnHelper = createColumnHelper<HttpTypes.AdminProduct>()
|
||||||
|
|
||||||
|
const useProductColumns = () => {
|
||||||
|
const base = useProductTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
pColumnHelper.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_COLLECTION = "pc"
|
||||||
|
|
||||||
|
const ProductCollectionTable = ({
|
||||||
|
initialRowState,
|
||||||
|
intermediate,
|
||||||
|
setIntermediate,
|
||||||
|
}: TableImplementationProps) => {
|
||||||
|
const [rowSelection, setRowSelection] =
|
||||||
|
useState<RowSelectionState>(initialRowState)
|
||||||
|
|
||||||
|
useCleanupSearchParams()
|
||||||
|
|
||||||
|
const { searchParams, raw } = useCollectionTableQuery({
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT_COLLECTION,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { collections, count, isLoading, isError, error } = useCollections(
|
||||||
|
searchParams,
|
||||||
|
{
|
||||||
|
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 newCollections =
|
||||||
|
collections
|
||||||
|
?.filter((p) => newIds.includes(p.id))
|
||||||
|
.map((p) => ({
|
||||||
|
value: p.id,
|
||||||
|
label: p.title,
|
||||||
|
})) || []
|
||||||
|
|
||||||
|
const filteredIntermediate = intermediate.filter(
|
||||||
|
(p) => !removedIds.includes(p.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
setIntermediate([...filteredIntermediate, ...newCollections])
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = useCollectionTableFilters()
|
||||||
|
const columns = useCollectionColumns()
|
||||||
|
|
||||||
|
const { table } = useDataTable({
|
||||||
|
data: collections || [],
|
||||||
|
columns,
|
||||||
|
count,
|
||||||
|
enablePagination: true,
|
||||||
|
enableRowSelection: true,
|
||||||
|
getRowId: (row) => row.id,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
|
},
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT_COLLECTION,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
isLoading={isLoading}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={["title", "created_at", "updated_at"]}
|
||||||
|
layout="fill"
|
||||||
|
pagination
|
||||||
|
search
|
||||||
|
prefix={PREFIX_PRODUCT_COLLECTION}
|
||||||
|
queryObject={raw}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pcColumnHelper = createColumnHelper<HttpTypes.AdminCollection>()
|
||||||
|
|
||||||
|
const useCollectionColumns = () => {
|
||||||
|
const base = useCollectionTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
pcColumnHelper.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_TYPE = "pt"
|
||||||
|
|
||||||
|
const ProductTypeTable = ({
|
||||||
|
initialRowState,
|
||||||
|
intermediate,
|
||||||
|
setIntermediate,
|
||||||
|
}: TableImplementationProps) => {
|
||||||
|
const [rowSelection, setRowSelection] =
|
||||||
|
useState<RowSelectionState>(initialRowState)
|
||||||
|
|
||||||
|
useCleanupSearchParams()
|
||||||
|
|
||||||
|
const { searchParams, raw } = useProductTypeTableQuery({
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT_TYPE,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { product_types, count, isLoading, isError, error } = useProductTypes(
|
||||||
|
searchParams,
|
||||||
|
{
|
||||||
|
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 newTypes =
|
||||||
|
product_types
|
||||||
|
?.filter((p) => newIds.includes(p.id))
|
||||||
|
.map((p) => ({
|
||||||
|
value: p.id,
|
||||||
|
label: p.value,
|
||||||
|
})) || []
|
||||||
|
|
||||||
|
const filteredIntermediate = intermediate.filter(
|
||||||
|
(p) => !removedIds.includes(p.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
setIntermediate([...filteredIntermediate, ...newTypes])
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = useProductTypeTableFilters()
|
||||||
|
const columns = useProductTypeColumns()
|
||||||
|
|
||||||
|
const { table } = useDataTable({
|
||||||
|
data: product_types || [],
|
||||||
|
columns,
|
||||||
|
count,
|
||||||
|
enablePagination: true,
|
||||||
|
enableRowSelection: true,
|
||||||
|
getRowId: (row) => row.id,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
|
},
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT_TYPE,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
isLoading={isLoading}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={["value", "created_at", "updated_at"]}
|
||||||
|
layout="fill"
|
||||||
|
pagination
|
||||||
|
search
|
||||||
|
prefix={PREFIX_PRODUCT_TYPE}
|
||||||
|
queryObject={raw}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ptColumnHelper = createColumnHelper<HttpTypes.AdminProductType>()
|
||||||
|
|
||||||
|
const useProductTypeColumns = () => {
|
||||||
|
const base = useProductTypeTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
ptColumnHelper.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 = ({
|
||||||
|
initialRowState,
|
||||||
|
intermediate,
|
||||||
|
setIntermediate,
|
||||||
|
}: TableImplementationProps) => {
|
||||||
|
const [rowSelection, setRowSelection] =
|
||||||
|
useState<RowSelectionState>(initialRowState)
|
||||||
|
|
||||||
|
useCleanupSearchParams()
|
||||||
|
|
||||||
|
const { searchParams, raw } = useProductTagTableQuery({
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT_TAG,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { product_tags, count, isLoading, isError, error } = useTags(
|
||||||
|
searchParams,
|
||||||
|
{
|
||||||
|
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 newTags =
|
||||||
|
product_tags
|
||||||
|
?.filter((p) => newIds.includes(p.id))
|
||||||
|
.map((p) => ({
|
||||||
|
value: p.id,
|
||||||
|
label: p.value,
|
||||||
|
})) || []
|
||||||
|
|
||||||
|
const filteredIntermediate = intermediate.filter(
|
||||||
|
(p) => !removedIds.includes(p.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
setIntermediate([...filteredIntermediate, ...newTags])
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = useProductTagTableFilters()
|
||||||
|
const columns = useProductTagColumns()
|
||||||
|
|
||||||
|
const { table } = useDataTable({
|
||||||
|
data: product_tags || [],
|
||||||
|
columns,
|
||||||
|
count,
|
||||||
|
enablePagination: true,
|
||||||
|
enableRowSelection: true,
|
||||||
|
getRowId: (row) => row.id,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
|
},
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
prefix: PREFIX_PRODUCT_TAG,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
isLoading={isLoading}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={["value", "created_at", "updated_at"]}
|
||||||
|
layout="fill"
|
||||||
|
pagination
|
||||||
|
search
|
||||||
|
prefix={PREFIX_PRODUCT_TAG}
|
||||||
|
queryObject={raw}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ptagColumnHelper = createColumnHelper<HttpTypes.AdminProductTag>()
|
||||||
|
|
||||||
|
const useProductTagColumns = () => {
|
||||||
|
const base = useProductTagTableColumns()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
ptagColumnHelper.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 useCleanupSearchParams = () => {
|
||||||
|
const [_, setSearchParams] = useSearchParams()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
setSearchParams({})
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./target-item"
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
import { XMarkMini } from "@medusajs/icons"
|
||||||
|
import { IconButton, Text } from "@medusajs/ui"
|
||||||
|
|
||||||
|
type TargetItemProps = {
|
||||||
|
index: number
|
||||||
|
onRemove: (index: number) => void
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TargetItem = ({ index, label, onRemove }: TargetItemProps) => {
|
||||||
|
return (
|
||||||
|
<div className="bg-ui-bg-field-component shadow-borders-base flex items-center justify-between gap-2 rounded-md px-2 py-0.5">
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
variant="transparent"
|
||||||
|
type="button"
|
||||||
|
onClick={() => onRemove(index)}
|
||||||
|
>
|
||||||
|
<XMarkMini />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./tax-override-card"
|
||||||
+378
@@ -0,0 +1,378 @@
|
|||||||
|
import {
|
||||||
|
ArrowDownRightMini,
|
||||||
|
PencilSquare,
|
||||||
|
Trash,
|
||||||
|
TriangleRightMini,
|
||||||
|
} from "@medusajs/icons"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { Badge, IconButton, StatusBadge, Text, Tooltip } from "@medusajs/ui"
|
||||||
|
import * as Collapsible from "@radix-ui/react-collapsible"
|
||||||
|
import { ComponentPropsWithoutRef } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
|
import { Divider } from "../../../../../components/common/divider"
|
||||||
|
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 { useTags } from "../../../../../hooks/api/tags"
|
||||||
|
import { formatPercentage } from "../../../../../lib/percentage-helpers"
|
||||||
|
import { TaxRateRuleReferenceType } from "../../constants"
|
||||||
|
import { useDeleteTaxRateAction } from "../../hooks"
|
||||||
|
|
||||||
|
interface TaxOverrideCardProps extends ComponentPropsWithoutRef<"div"> {
|
||||||
|
taxRate: HttpTypes.AdminTaxRate
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TaxOverrideCard = ({ taxRate }: TaxOverrideCardProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const handleDelete = useDeleteTaxRateAction(taxRate)
|
||||||
|
|
||||||
|
if (taxRate.is_default) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupedRules = taxRate.rules.reduce((acc, rule) => {
|
||||||
|
if (!acc[rule.reference]) {
|
||||||
|
acc[rule.reference] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[rule.reference].push(rule.reference_id)
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {} as Record<string, string[]>)
|
||||||
|
|
||||||
|
const validKeys = Object.values(TaxRateRuleReferenceType)
|
||||||
|
const numberOfTargets = Object.keys(groupedRules).map((key) =>
|
||||||
|
validKeys.includes(key as TaxRateRuleReferenceType)
|
||||||
|
).length
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Collapsible.Root>
|
||||||
|
<div className="flex items-center justify-between px-6 py-3">
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<Collapsible.Trigger asChild>
|
||||||
|
<IconButton size="2xsmall" variant="transparent" className="group">
|
||||||
|
<TriangleRightMini className="text-ui-fg-muted transition-transform group-data-[state='open']:rotate-90" />
|
||||||
|
</IconButton>
|
||||||
|
</Collapsible.Trigger>
|
||||||
|
<div className="flex items-center gap-x-1.5">
|
||||||
|
<Text size="small" weight="plus" leading="compact">
|
||||||
|
{taxRate.name}
|
||||||
|
</Text>
|
||||||
|
{taxRate.code && (
|
||||||
|
<div className="text-ui-fg-subtle flex items-center gap-x-1.5">
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
·
|
||||||
|
</Text>
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
{taxRate.code}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-3">
|
||||||
|
<Text size="small" leading="compact" className="text-ui-fg-subtle">
|
||||||
|
{t("taxRegions.fields.targets.numberOfTargets", {
|
||||||
|
count: numberOfTargets,
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
<div className="bg-ui-border-base h-3 w-px" />
|
||||||
|
<StatusBadge color={taxRate.is_combinable ? "green" : "grey"}>
|
||||||
|
{taxRate.is_combinable
|
||||||
|
? t("taxRegions.fields.isCombinable.true")
|
||||||
|
: t("taxRegions.fields.isCombinable.false")}
|
||||||
|
</StatusBadge>
|
||||||
|
<ActionMenu
|
||||||
|
groups={[
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: t("actions.edit"),
|
||||||
|
icon: <PencilSquare />,
|
||||||
|
to: `overrides/${taxRate.id}/edit`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: t("actions.delete"),
|
||||||
|
icon: <Trash />,
|
||||||
|
onClick: handleDelete,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Collapsible.Content>
|
||||||
|
<div className="bg-ui-bg-subtle">
|
||||||
|
<Divider variant="dashed" />
|
||||||
|
<div className="px-6 py-3">
|
||||||
|
<div className="flex items-center gap-x-3">
|
||||||
|
<div className="text-ui-fg-muted flex size-5 items-center justify-center">
|
||||||
|
<ArrowDownRightMini />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap items-center gap-x-1.5 gap-y-2">
|
||||||
|
<Badge size="2xsmall">{formatPercentage(taxRate.rate)}</Badge>
|
||||||
|
<Text
|
||||||
|
size="small"
|
||||||
|
leading="compact"
|
||||||
|
className="text-ui-fg-subtle"
|
||||||
|
>
|
||||||
|
{t("taxRegions.fields.targets.operators.on")}
|
||||||
|
</Text>
|
||||||
|
{Object.entries(groupedRules).map(([reference, ids], index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={reference}
|
||||||
|
className="flex items-center gap-x-1.5"
|
||||||
|
>
|
||||||
|
<Reference
|
||||||
|
key={reference}
|
||||||
|
reference={reference as TaxRateRuleReferenceType}
|
||||||
|
ids={ids}
|
||||||
|
/>
|
||||||
|
{index < Object.keys(groupedRules).length - 1 && (
|
||||||
|
<Text
|
||||||
|
size="small"
|
||||||
|
leading="compact"
|
||||||
|
className="text-ui-fg-subtle"
|
||||||
|
>
|
||||||
|
{t("taxRegions.fields.targets.operators.and")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Collapsible.Content>
|
||||||
|
</Collapsible.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Reference = ({
|
||||||
|
reference,
|
||||||
|
ids,
|
||||||
|
}: {
|
||||||
|
reference: TaxRateRuleReferenceType
|
||||||
|
ids: string[]
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-x-1.5">
|
||||||
|
<ReferenceBadge reference={reference} />
|
||||||
|
<ReferenceValues type={reference} ids={ids} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReferenceBadge = ({
|
||||||
|
reference,
|
||||||
|
}: {
|
||||||
|
reference: TaxRateRuleReferenceType
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
let label: string | null = null
|
||||||
|
|
||||||
|
switch (reference) {
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT:
|
||||||
|
label = t("taxRegions.fields.targets.tags.product")
|
||||||
|
break
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
|
||||||
|
label = t("taxRegions.fields.targets.tags.productCollection")
|
||||||
|
break
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_TAG:
|
||||||
|
label = t("taxRegions.fields.targets.tags.productTag")
|
||||||
|
break
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_TYPE:
|
||||||
|
label = t("taxRegions.fields.targets.tags.productType")
|
||||||
|
break
|
||||||
|
case TaxRateRuleReferenceType.CUSTOMER_GROUP:
|
||||||
|
label = t("taxRegions.fields.targets.tags.customerGroup")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!label) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Badge size="2xsmall">{label}</Badge>
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReferenceValues = ({
|
||||||
|
type,
|
||||||
|
ids,
|
||||||
|
}: {
|
||||||
|
type: TaxRateRuleReferenceType
|
||||||
|
ids: string[]
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const { isPending, additional, labels, isError, error } = useReferenceValues(
|
||||||
|
type,
|
||||||
|
ids
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPending) {
|
||||||
|
return (
|
||||||
|
<div className="bg-ui-tag-neutral-bg border-ui-tag-neutral-border h-5 w-14 animate-pulse rounded-md" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<ul>
|
||||||
|
{labels?.map((label: string, index) => (
|
||||||
|
<li key={index}>{label}</li>
|
||||||
|
))}
|
||||||
|
{additional > 0 && (
|
||||||
|
<li>
|
||||||
|
{t("taxRegions.fields.targets.additionalValues", {
|
||||||
|
count: additional,
|
||||||
|
})}
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Badge size="2xsmall">
|
||||||
|
{t("taxRegions.fields.targets.values", {
|
||||||
|
count: ids.length,
|
||||||
|
})}
|
||||||
|
</Badge>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useReferenceValues = (
|
||||||
|
type: TaxRateRuleReferenceType,
|
||||||
|
ids: string[]
|
||||||
|
): {
|
||||||
|
labels: string[] | undefined
|
||||||
|
isPending: boolean
|
||||||
|
additional: number
|
||||||
|
isError: boolean
|
||||||
|
error: FetchError | null
|
||||||
|
} => {
|
||||||
|
const products = useProducts(
|
||||||
|
{
|
||||||
|
id: ids,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!ids.length && type === TaxRateRuleReferenceType.PRODUCT,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const tags = useTags(
|
||||||
|
{
|
||||||
|
id: ids,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!ids.length && type === TaxRateRuleReferenceType.PRODUCT_TAG,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const productTypes = useProductTypes(
|
||||||
|
{
|
||||||
|
id: ids,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!ids.length && type === TaxRateRuleReferenceType.PRODUCT_TYPE,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const collections = useCollections(
|
||||||
|
{
|
||||||
|
id: ids,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled:
|
||||||
|
!!ids.length && type === TaxRateRuleReferenceType.PRODUCT_COLLECTION,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const customerGroups = useCustomerGroups(
|
||||||
|
{
|
||||||
|
id: ids,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!ids.length && type === TaxRateRuleReferenceType.CUSTOMER_GROUP,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT:
|
||||||
|
return {
|
||||||
|
labels: products.products?.map((product) => product.title),
|
||||||
|
isPending: products.isPending,
|
||||||
|
additional:
|
||||||
|
products.products && products.count
|
||||||
|
? products.count - products.products.length
|
||||||
|
: 0,
|
||||||
|
isError: products.isError,
|
||||||
|
error: products.error,
|
||||||
|
}
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_TAG:
|
||||||
|
return {
|
||||||
|
labels: tags.product_tags?.map((tag: any) => tag.value),
|
||||||
|
isPending: tags.isPending,
|
||||||
|
additional:
|
||||||
|
tags.product_tags && tags.count
|
||||||
|
? tags.count - tags.product_tags.length
|
||||||
|
: 0,
|
||||||
|
isError: tags.isError,
|
||||||
|
error: tags.error,
|
||||||
|
}
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_TYPE:
|
||||||
|
return {
|
||||||
|
labels: productTypes.product_types?.map((type) => type.value),
|
||||||
|
isPending: productTypes.isPending,
|
||||||
|
additional:
|
||||||
|
productTypes.product_types && productTypes.count
|
||||||
|
? productTypes.count - productTypes.product_types.length
|
||||||
|
: 0,
|
||||||
|
isError: productTypes.isError,
|
||||||
|
error: productTypes.error,
|
||||||
|
}
|
||||||
|
case TaxRateRuleReferenceType.PRODUCT_COLLECTION:
|
||||||
|
return {
|
||||||
|
labels: collections.collections?.map((collection) => collection.title!),
|
||||||
|
isPending: collections.isPending,
|
||||||
|
additional:
|
||||||
|
collections.collections && collections.count
|
||||||
|
? collections.count - collections.collections.length
|
||||||
|
: 0,
|
||||||
|
isError: collections.isError,
|
||||||
|
error: collections.error,
|
||||||
|
}
|
||||||
|
case TaxRateRuleReferenceType.CUSTOMER_GROUP:
|
||||||
|
return {
|
||||||
|
labels: customerGroups.customer_groups?.map((group) => group.name!),
|
||||||
|
isPending: customerGroups.isPending,
|
||||||
|
additional:
|
||||||
|
customerGroups.customer_groups && customerGroups.count
|
||||||
|
? customerGroups.count - customerGroups.customer_groups.length
|
||||||
|
: 0,
|
||||||
|
isError: customerGroups.isError,
|
||||||
|
error: customerGroups.error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./tax-override-table"
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { Button } from "@medusajs/ui"
|
||||||
|
import { Table } from "@tanstack/react-table"
|
||||||
|
import { ReactNode } from "react"
|
||||||
|
import { Link } from "react-router-dom"
|
||||||
|
import {
|
||||||
|
NoRecords,
|
||||||
|
NoResults,
|
||||||
|
} from "../../../../../components/common/empty-table-content"
|
||||||
|
import { TableFooterSkeleton } from "../../../../../components/common/skeleton"
|
||||||
|
import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination"
|
||||||
|
import { DataTableOrderBy } from "../../../../../components/table/data-table/data-table-order-by"
|
||||||
|
import { DataTableSearch } from "../../../../../components/table/data-table/data-table-search"
|
||||||
|
import { TaxOverrideCard } from "../tax-override-card"
|
||||||
|
|
||||||
|
type TaxOverrideTableProps = {
|
||||||
|
isPending: boolean
|
||||||
|
queryObject: Record<string, any>
|
||||||
|
count?: number
|
||||||
|
table: Table<HttpTypes.AdminTaxRate>
|
||||||
|
action: { label: string; to: string }
|
||||||
|
prefix?: string
|
||||||
|
children?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TaxOverrideTable = ({
|
||||||
|
isPending,
|
||||||
|
action,
|
||||||
|
count = 0,
|
||||||
|
table,
|
||||||
|
queryObject,
|
||||||
|
prefix,
|
||||||
|
children,
|
||||||
|
}: TaxOverrideTableProps) => {
|
||||||
|
if (isPending) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col divide-y">
|
||||||
|
{Array.from({ length: 3 }).map((_, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-ui-bg-field-component h-[52px] w-full animate-pulse"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<TableFooterSkeleton layout="fit" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const noQuery =
|
||||||
|
Object.values(queryObject).filter((v) => Boolean(v)).length === 0
|
||||||
|
const noResults = !isPending && count === 0 && !noQuery
|
||||||
|
const noRecords = !isPending && count === 0 && noQuery
|
||||||
|
|
||||||
|
const { pageIndex, pageSize } = table.getState().pagination
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col divide-y">
|
||||||
|
<div className="flex flex-col justify-between gap-x-4 gap-y-3 px-6 py-4 md:flex-row md:items-center">
|
||||||
|
<div>{children}</div>
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
{!noRecords && (
|
||||||
|
<div className="flex w-full items-center gap-x-2 md:w-fit">
|
||||||
|
<div className="w-full md:w-fit">
|
||||||
|
<DataTableSearch prefix={prefix} />
|
||||||
|
</div>
|
||||||
|
<DataTableOrderBy
|
||||||
|
keys={["name", "rate", "code", "updated_at", "created_at"]}
|
||||||
|
prefix={prefix}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Link to={action.to}>
|
||||||
|
<Button size="small" variant="secondary">
|
||||||
|
{action.label}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{noResults && <NoResults />}
|
||||||
|
{noRecords && <NoRecords />}
|
||||||
|
{!noRecords && !noResults
|
||||||
|
? !isPending
|
||||||
|
? table.getRowModel().rows.map((row) => {
|
||||||
|
return (
|
||||||
|
<TaxOverrideCard
|
||||||
|
key={row.id}
|
||||||
|
taxRate={row.original}
|
||||||
|
role="row"
|
||||||
|
aria-rowindex={row.index}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
: Array.from({ length: 3 }).map((_, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-ui-bg-field-component h-[60px] w-full animate-pulse"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
{!noRecords && (
|
||||||
|
<LocalizedTablePagination
|
||||||
|
prefix={prefix}
|
||||||
|
canNextPage={table.getCanNextPage()}
|
||||||
|
canPreviousPage={table.getCanPreviousPage()}
|
||||||
|
count={count}
|
||||||
|
nextPage={table.nextPage}
|
||||||
|
previousPage={table.previousPage}
|
||||||
|
pageCount={table.getPageCount()}
|
||||||
|
pageIndex={pageIndex}
|
||||||
|
pageSize={pageSize}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./tax-rate-line"
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { StatusBadge, Text } from "@medusajs/ui"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
|
import { formatPercentage } from "../../../../../lib/percentage-helpers"
|
||||||
|
import { useDeleteTaxRateAction } from "../../hooks"
|
||||||
|
|
||||||
|
type TaxRateLineProps = {
|
||||||
|
taxRate: HttpTypes.AdminTaxRate
|
||||||
|
isSublevelTaxRate?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TaxRateLine = ({
|
||||||
|
taxRate,
|
||||||
|
isSublevelTaxRate,
|
||||||
|
}: TaxRateLineProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-ui-fg-subtle grid grid-cols-[1fr_1fr_auto] items-center gap-4 px-6 py-4">
|
||||||
|
<div className="flex items-center gap-x-1.5">
|
||||||
|
<Text size="small" weight="plus" leading="compact">
|
||||||
|
{taxRate.name}
|
||||||
|
</Text>
|
||||||
|
{taxRate.code && (
|
||||||
|
<div className="flex items-center gap-x-1.5">
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
·
|
||||||
|
</Text>
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
{taxRate.code}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Text size="small" leading="compact">
|
||||||
|
{formatPercentage(taxRate.rate)}
|
||||||
|
</Text>
|
||||||
|
<div className="flex items-center justify-end gap-x-2">
|
||||||
|
{isSublevelTaxRate && (
|
||||||
|
<StatusBadge color={taxRate.is_combinable ? "green" : "grey"}>
|
||||||
|
{taxRate.is_combinable
|
||||||
|
? t("taxRegions.fields.isCombinable.true")
|
||||||
|
: t("taxRegions.fields.isCombinable.false")}
|
||||||
|
</StatusBadge>
|
||||||
|
)}
|
||||||
|
<TaxRateActions taxRate={taxRate} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaxRateActions = ({ taxRate }: { taxRate: HttpTypes.AdminTaxRate }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const handleDelete = useDeleteTaxRateAction(taxRate)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionMenu
|
||||||
|
groups={[
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: t("actions.edit"),
|
||||||
|
icon: <PencilSquare />,
|
||||||
|
to: `tax-rates/${taxRate.id}/edit`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: t("actions.delete"),
|
||||||
|
icon: <Trash />,
|
||||||
|
onClick: handleDelete,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./tax-region-card"
|
||||||
+200
@@ -0,0 +1,200 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { Heading, Text, Tooltip, clx } from "@medusajs/ui"
|
||||||
|
import ReactCountryFlag from "react-country-flag"
|
||||||
|
|
||||||
|
import { ExclamationCircle, MapPin, Plus, Trash } from "@medusajs/icons"
|
||||||
|
import { ComponentPropsWithoutRef, ReactNode } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { Link } from "react-router-dom"
|
||||||
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
|
import { IconAvatar } from "../../../../../components/common/icon-avatar"
|
||||||
|
import { getCountryByIso2 } from "../../../../../lib/data/countries"
|
||||||
|
import {
|
||||||
|
getProvinceByIso2,
|
||||||
|
isProvinceInCountry,
|
||||||
|
} from "../../../../../lib/data/country-states"
|
||||||
|
import { useDeleteTaxRegionAction } from "../../hooks"
|
||||||
|
|
||||||
|
interface TaxRegionCardProps extends ComponentPropsWithoutRef<"div"> {
|
||||||
|
taxRegion: HttpTypes.AdminTaxRegion
|
||||||
|
type?: "header" | "list"
|
||||||
|
variant?: "country" | "province"
|
||||||
|
asLink?: boolean
|
||||||
|
badge?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TaxRegionCard = ({
|
||||||
|
taxRegion,
|
||||||
|
type = "list",
|
||||||
|
variant = "country",
|
||||||
|
asLink = true,
|
||||||
|
badge,
|
||||||
|
}: TaxRegionCardProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { id, country_code, province_code } = taxRegion
|
||||||
|
|
||||||
|
const country = getCountryByIso2(country_code)
|
||||||
|
const province = getProvinceByIso2(province_code)
|
||||||
|
|
||||||
|
let name = "N/A"
|
||||||
|
let misconfiguredSublevelTooltip: string | null = null
|
||||||
|
|
||||||
|
if (province || province_code) {
|
||||||
|
name = province ? province : province_code!.toUpperCase()
|
||||||
|
} else if (country || country_code) {
|
||||||
|
name = country ? country.display_name : country_code!.toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
country_code &&
|
||||||
|
province_code &&
|
||||||
|
!isProvinceInCountry(country_code, province_code)
|
||||||
|
) {
|
||||||
|
name = province_code.toUpperCase()
|
||||||
|
misconfiguredSublevelTooltip = t(
|
||||||
|
"taxRegions.fields.sublevels.tooltips.notPartOfCountry",
|
||||||
|
{
|
||||||
|
country: country?.display_name,
|
||||||
|
province: province_code.toUpperCase(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const showCreateDefaultTaxRate =
|
||||||
|
!taxRegion.tax_rates.filter((tr) => tr.is_default).length &&
|
||||||
|
type === "header"
|
||||||
|
|
||||||
|
const Component = (
|
||||||
|
<div
|
||||||
|
className={clx(
|
||||||
|
"group-data-[link=true]:hover:bg-ui-bg-base-hover transition-fg flex flex-col justify-between gap-y-4 px-6 md:flex-row md:items-center md:gap-y-0",
|
||||||
|
{
|
||||||
|
"py-4": type === "header",
|
||||||
|
"py-3": type === "list",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-x-4">
|
||||||
|
<IconAvatar size={type === "list" ? "small" : "large"}>
|
||||||
|
{country_code && !province_code ? (
|
||||||
|
<div
|
||||||
|
className={clx(
|
||||||
|
"flex size-fit items-center justify-center overflow-hidden rounded-[1px]",
|
||||||
|
{
|
||||||
|
"rounded-sm": type === "header",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ReactCountryFlag
|
||||||
|
countryCode={country_code}
|
||||||
|
svg
|
||||||
|
style={
|
||||||
|
type === "list"
|
||||||
|
? { width: "12px", height: "9px" }
|
||||||
|
: { width: "16px", height: "12px" }
|
||||||
|
}
|
||||||
|
aria-label={country?.display_name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<MapPin className="text-ui-fg-subtle" />
|
||||||
|
)}
|
||||||
|
</IconAvatar>
|
||||||
|
<div>
|
||||||
|
{type === "list" ? (
|
||||||
|
<Text size="small" weight="plus" leading="compact">
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Heading>{name}</Heading>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex size-fit items-center gap-x-2 md:hidden">
|
||||||
|
{misconfiguredSublevelTooltip && (
|
||||||
|
<Tooltip content={misconfiguredSublevelTooltip}>
|
||||||
|
<ExclamationCircle className="text-ui-tag-orange-icon" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{badge}
|
||||||
|
<TaxRegionCardActions
|
||||||
|
taxRegion={taxRegion}
|
||||||
|
showCreateDefaultTaxRate={showCreateDefaultTaxRate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden size-fit items-center gap-x-2 md:flex">
|
||||||
|
{misconfiguredSublevelTooltip && (
|
||||||
|
<Tooltip content={misconfiguredSublevelTooltip}>
|
||||||
|
<ExclamationCircle className="text-ui-tag-orange-icon" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{badge}
|
||||||
|
<TaxRegionCardActions
|
||||||
|
taxRegion={taxRegion}
|
||||||
|
showCreateDefaultTaxRate={showCreateDefaultTaxRate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (asLink) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={variant === "country" ? `${id}` : `provinces/${id}`}
|
||||||
|
data-link="true"
|
||||||
|
className="group block"
|
||||||
|
>
|
||||||
|
{Component}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Component
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaxRegionCardActions = ({
|
||||||
|
taxRegion,
|
||||||
|
showCreateDefaultTaxRate,
|
||||||
|
}: {
|
||||||
|
taxRegion: HttpTypes.AdminTaxRegion
|
||||||
|
showCreateDefaultTaxRate?: boolean
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const to = taxRegion.parent_id
|
||||||
|
? `/settings/tax-regions/${taxRegion.parent_id}`
|
||||||
|
: undefined
|
||||||
|
const handleDelete = useDeleteTaxRegionAction({ taxRegion, to })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionMenu
|
||||||
|
groups={[
|
||||||
|
...(showCreateDefaultTaxRate
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
icon: <Plus />,
|
||||||
|
label: t("taxRegions.fields.defaultTaxRate.action"),
|
||||||
|
to: `tax-rates/create`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
icon: <Trash />,
|
||||||
|
label: t("actions.delete"),
|
||||||
|
onClick: handleDelete,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./tax-region-table"
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { Button } from "@medusajs/ui"
|
||||||
|
import { Table } from "@tanstack/react-table"
|
||||||
|
import { ReactNode } from "react"
|
||||||
|
import { Link } from "react-router-dom"
|
||||||
|
import {
|
||||||
|
NoRecords,
|
||||||
|
NoResults,
|
||||||
|
} from "../../../../../components/common/empty-table-content"
|
||||||
|
import { TableFooterSkeleton } from "../../../../../components/common/skeleton"
|
||||||
|
import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination"
|
||||||
|
import { DataTableOrderBy } from "../../../../../components/table/data-table/data-table-order-by"
|
||||||
|
import { TaxRegionCard } from "../tax-region-card"
|
||||||
|
|
||||||
|
type TaxRegionTableProps = {
|
||||||
|
variant?: "country" | "province"
|
||||||
|
isPending: boolean
|
||||||
|
queryObject: Record<string, any>
|
||||||
|
count?: number
|
||||||
|
table: Table<HttpTypes.AdminTaxRegion>
|
||||||
|
action: { label: string; to: string }
|
||||||
|
prefix?: string
|
||||||
|
children?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TaxRegionTable = ({
|
||||||
|
variant = "country",
|
||||||
|
isPending,
|
||||||
|
action,
|
||||||
|
count = 0,
|
||||||
|
table,
|
||||||
|
queryObject,
|
||||||
|
prefix,
|
||||||
|
children,
|
||||||
|
}: TaxRegionTableProps) => {
|
||||||
|
if (isPending) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col divide-y">
|
||||||
|
{Array.from({ length: 3 }).map((_, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-ui-bg-field-component h-[52px] w-full animate-pulse"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<TableFooterSkeleton layout="fit" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const noQuery =
|
||||||
|
Object.values(queryObject).filter((v) => Boolean(v)).length === 0
|
||||||
|
const noResults = !isPending && count === 0 && !noQuery
|
||||||
|
const noRecords = !isPending && count === 0 && noQuery
|
||||||
|
|
||||||
|
const { pageIndex, pageSize } = table.getState().pagination
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col divide-y">
|
||||||
|
<div className="flex flex-col justify-between gap-x-4 gap-y-3 px-6 py-4 md:flex-row md:items-center">
|
||||||
|
<div>{children}</div>
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
{!noRecords && (
|
||||||
|
<div className="flex w-full items-center gap-x-2 md:w-fit">
|
||||||
|
{/* Re-enable when we allow searching tax regions by country name rather than country_code */}
|
||||||
|
{/* <div className="w-full md:w-fit">
|
||||||
|
<DataTableSearch prefix={prefix} />
|
||||||
|
</div> */}
|
||||||
|
<DataTableOrderBy
|
||||||
|
keys={["updated_at", "created_at"]}
|
||||||
|
prefix={prefix}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Link to={action.to}>
|
||||||
|
<Button size="small" variant="secondary">
|
||||||
|
{action.label}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{noResults && <NoResults />}
|
||||||
|
{noRecords && <NoRecords />}
|
||||||
|
{!noRecords && !noResults
|
||||||
|
? !isPending
|
||||||
|
? table.getRowModel().rows.map((row) => {
|
||||||
|
return (
|
||||||
|
<TaxRegionCard
|
||||||
|
variant={variant}
|
||||||
|
key={row.id}
|
||||||
|
taxRegion={row.original}
|
||||||
|
role="row"
|
||||||
|
aria-rowindex={row.index}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
: Array.from({ length: 3 }).map((_, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-ui-bg-field-component h-[60px] w-full animate-pulse"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
{!noRecords && (
|
||||||
|
<LocalizedTablePagination
|
||||||
|
prefix={prefix}
|
||||||
|
canNextPage={table.getCanNextPage()}
|
||||||
|
canPreviousPage={table.getCanPreviousPage()}
|
||||||
|
count={count}
|
||||||
|
nextPage={table.nextPage}
|
||||||
|
previousPage={table.previousPage}
|
||||||
|
pageCount={table.getPageCount()}
|
||||||
|
pageIndex={pageIndex}
|
||||||
|
pageSize={pageSize}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
+2
-6
@@ -1,11 +1,7 @@
|
|||||||
export enum ConditionEntities {
|
export enum TaxRateRuleReferenceType {
|
||||||
PRODUCT = "products",
|
PRODUCT = "products",
|
||||||
PRODUCT_TYPE = "product_types",
|
|
||||||
PRODUCT_COLLECTION = "product_collections",
|
PRODUCT_COLLECTION = "product_collections",
|
||||||
PRODUCT_TAG = "product_tags",
|
PRODUCT_TAG = "product_tags",
|
||||||
|
PRODUCT_TYPE = "product_types",
|
||||||
CUSTOMER_GROUP = "customer_groups",
|
CUSTOMER_GROUP = "customer_groups",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Operators {
|
|
||||||
IN = "in",
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { toast, usePrompt } from "@medusajs/ui"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { useNavigate } from "react-router-dom"
|
||||||
|
import { useDeleteTaxRate } from "../../../hooks/api/tax-rates"
|
||||||
|
import { useDeleteTaxRegion } from "../../../hooks/api/tax-regions"
|
||||||
|
|
||||||
|
export const useDeleteTaxRegionAction = ({
|
||||||
|
taxRegion,
|
||||||
|
to = "/settings/tax-regions",
|
||||||
|
}: {
|
||||||
|
taxRegion: HttpTypes.AdminTaxRegion
|
||||||
|
to?: string
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const prompt = usePrompt()
|
||||||
|
|
||||||
|
const { mutateAsync } = useDeleteTaxRegion(taxRegion.id)
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
const res = await prompt({
|
||||||
|
title: t("general.areYouSure"),
|
||||||
|
description: t("taxRegions.delete.confirmation"),
|
||||||
|
confirmText: t("actions.delete"),
|
||||||
|
cancelText: t("actions.cancel"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutateAsync(undefined, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(t("general.success"), {
|
||||||
|
description: t("taxRegions.delete.successToast"),
|
||||||
|
dismissable: true,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
|
||||||
|
navigate(to, { replace: true })
|
||||||
|
},
|
||||||
|
onError: (e) => {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissable: true,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDeleteTaxRateAction = (taxRate: HttpTypes.AdminTaxRate) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const prompt = usePrompt()
|
||||||
|
|
||||||
|
const { mutateAsync } = useDeleteTaxRate(taxRate.id)
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
const res = await prompt({
|
||||||
|
title: t("general.areYouSure"),
|
||||||
|
description: t("taxRegions.taxRates.delete.confirmation", {
|
||||||
|
name: taxRate.name,
|
||||||
|
}),
|
||||||
|
confirmText: t("actions.delete"),
|
||||||
|
cancelText: t("actions.cancel"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutateAsync(undefined, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(t("taxRegions.taxRates.delete.successToast"))
|
||||||
|
},
|
||||||
|
onError: (e) => {
|
||||||
|
toast.error(e.message)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleDelete
|
||||||
|
}
|
||||||
Loaded 100 of 232 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user