feat(ui,dashboard): Add DataTable block (#10024)

**What**
- Adds opinionated DataTable block to `@medusajs/ui` 
- Adds new DataTable to `@medusajs/dashboard` that uses the above mentioned block as the primitive.

The PR also replaces the table on /customer-groups and the variants table on /products/:id with the new DataTable, to provide an example of it's usage. The previous DataTable component has been renamed to `_DataTable` and has been deprecated.

**Note**
This PR has a lot of LOC. 5,346 of these changes are the fr.json file, which wasn't formatted correctly before. When adding the new translations needed for this PR the file was formatted which caused each line to change to have the proper indentation.

Resolves CMRC-333
This commit is contained in:
Kasper Fabricius Kristensen
2025-01-20 13:26:12 +00:00
committed by GitHub
parent c3976a312b
commit 147c0e5a35
130 changed files with 9238 additions and 3884 deletions
@@ -30,11 +30,16 @@ export type ActionGroup = {
type ActionMenuProps = PropsWithChildren<{
groups: ActionGroup[]
variant?: "transparent" | "primary"
}>
export const ActionMenu = ({ groups, children }: ActionMenuProps) => {
export const ActionMenu = ({
groups,
variant = "transparent",
children,
}: ActionMenuProps) => {
const inner = children ?? (
<IconButton size="small" variant="transparent">
<IconButton size="small" variant={variant}>
<EllipsisHorizontal />
</IconButton>
)
@@ -0,0 +1,404 @@
import {
Button,
DataTableColumnDef,
DataTableCommand,
DataTableEmptyStateProps,
DataTableFilter,
DataTableFilteringState,
DataTablePaginationState,
DataTableRowSelectionState,
DataTableSortingState,
Heading,
DataTable as Primitive,
useDataTable,
} from "@medusajs/ui"
import React, { ReactNode, useCallback, useState } from "react"
import { useTranslation } from "react-i18next"
import { Link, useNavigate, useSearchParams } from "react-router-dom"
import { useQueryParams } from "../../hooks/use-query-params"
import { ActionMenu } from "../common/action-menu"
type DataTableActionProps = {
label: string
disabled?: boolean
} & (
| {
to: string
}
| {
onClick: () => void
}
)
type DataTableActionMenuActionProps = {
label: string
icon: ReactNode
disabled?: boolean
} & (
| {
to: string
}
| {
onClick: () => void
}
)
type DataTableActionMenuGroupProps = {
actions: DataTableActionMenuActionProps[]
}
type DataTableActionMenuProps = {
groups: DataTableActionMenuGroupProps[]
}
interface DataTableProps<TData> {
data?: TData[]
columns: DataTableColumnDef<TData, any>[]
filters?: DataTableFilter[]
commands?: DataTableCommand[]
action?: DataTableActionProps
actionMenu?: DataTableActionMenuProps
rowCount?: number
getRowId: (row: TData) => string
enablePagination?: boolean
enableSearch?: boolean
autoFocusSearch?: boolean
rowHref?: (row: TData) => string
emptyState?: DataTableEmptyStateProps
heading: string
prefix?: string
pageSize?: number
isLoading?: boolean
rowSelection?: {
state: DataTableRowSelectionState
onRowSelectionChange: (value: DataTableRowSelectionState) => void
}
}
export const DataTable = <TData,>({
data = [],
columns,
filters,
commands,
action,
actionMenu,
getRowId,
rowCount = 0,
enablePagination = true,
enableSearch = true,
autoFocusSearch = false,
rowHref,
heading,
prefix,
pageSize = 10,
emptyState,
rowSelection,
isLoading = false,
}: DataTableProps<TData>) => {
const { t } = useTranslation()
const enableFiltering = filters && filters.length > 0
const enableCommands = commands && commands.length > 0
const enableSorting = columns.some((column) => column.enableSorting)
const filterIds = filters?.map((f) => f.id) ?? []
const prefixedFilterIds = filterIds.map((id) => getQueryParamKey(id, prefix))
const { offset, order, q, ...filterParams } = useQueryParams(
[
...filterIds,
...(enableSorting ? ["order"] : []),
...(enableSearch ? ["q"] : []),
...(enablePagination ? ["offset"] : []),
],
prefix
)
const [_, setSearchParams] = useSearchParams()
const [search, setSearch] = useState<string>(q ?? "")
const handleSearchChange = (value: string) => {
setSearch(value)
setSearchParams((prev) => {
if (value) {
prev.set(getQueryParamKey("q", prefix), value)
} else {
prev.delete(getQueryParamKey("q", prefix))
}
return prev
})
}
const [pagination, setPagination] = useState<DataTablePaginationState>(
offset ? parsePaginationState(offset, pageSize) : { pageIndex: 0, pageSize }
)
const handlePaginationChange = (value: DataTablePaginationState) => {
setPagination(value)
setSearchParams((prev) => {
if (value.pageIndex === 0) {
prev.delete(getQueryParamKey("offset", prefix))
} else {
prev.set(
getQueryParamKey("offset", prefix),
transformPaginationState(value).toString()
)
}
return prev
})
}
const [filtering, setFiltering] = useState<DataTableFilteringState>(
parseFilterState(filterIds, filterParams)
)
const handleFilteringChange = (value: DataTableFilteringState) => {
setFiltering(value)
setSearchParams((prev) => {
Array.from(prev.keys()).forEach((key) => {
if (prefixedFilterIds.includes(key) && !(key in value)) {
prev.delete(key)
}
})
Object.entries(value).forEach(([key, filter]) => {
if (
prefixedFilterIds.includes(getQueryParamKey(key, prefix)) &&
filter
) {
prev.set(getQueryParamKey(key, prefix), JSON.stringify(filter))
}
})
return prev
})
}
const [sorting, setSorting] = useState<DataTableSortingState | null>(
order ? parseSortingState(order) : null
)
const handleSortingChange = (value: DataTableSortingState) => {
setSorting(value)
setSearchParams((prev) => {
if (value) {
const valueToStore = transformSortingState(value)
prev.set(getQueryParamKey("order", prefix), valueToStore)
} else {
prev.delete(getQueryParamKey("order", prefix))
}
return prev
})
}
const { pagination: paginationTranslations, toolbar: toolbarTranslations } =
useDataTableTranslations()
const navigate = useNavigate()
const onRowClick = useCallback(
(event: React.MouseEvent<HTMLTableRowElement, MouseEvent>, row: TData) => {
if (!rowHref) {
return
}
const href = rowHref(row)
if (event.metaKey || event.ctrlKey || event.button === 1) {
window.open(href, "_blank", "noreferrer")
return
}
if (event.shiftKey) {
window.open(href, undefined, "noreferrer")
return
}
navigate(href)
},
[navigate, rowHref]
)
const instance = useDataTable({
data,
columns,
filters,
commands,
rowCount,
getRowId,
onRowClick: rowHref ? onRowClick : undefined,
pagination: enablePagination
? {
state: pagination,
onPaginationChange: handlePaginationChange,
}
: undefined,
filtering: enableFiltering
? {
state: filtering,
onFilteringChange: handleFilteringChange,
}
: undefined,
sorting: enableSorting
? {
state: sorting,
onSortingChange: handleSortingChange,
}
: undefined,
search: enableSearch
? {
state: search,
onSearchChange: handleSearchChange,
}
: undefined,
rowSelection,
isLoading,
})
return (
<Primitive instance={instance}>
<Primitive.Toolbar
className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center"
translations={toolbarTranslations}
>
<div className="flex w-full items-center justify-between">
<Heading>{heading}</Heading>
<div className="flex items-center justify-end gap-x-2 md:hidden">
{enableFiltering && (
<Primitive.FilterMenu tooltip={t("filters.filterLabel")} />
)}
<Primitive.SortingMenu tooltip={t("filters.sortLabel")} />
{actionMenu && <ActionMenu variant="primary" {...actionMenu} />}
{action && <DataTableAction {...action} />}
</div>
</div>
<div className="flex w-full items-center gap-2 md:justify-end">
{enableSearch && (
<div className="w-full md:w-auto">
<Primitive.Search
placeholder={t("filters.searchLabel")}
autoFocus={autoFocusSearch}
/>
</div>
)}
<div className="hidden items-center gap-x-2 md:flex">
{enableFiltering && (
<Primitive.FilterMenu tooltip={t("filters.filterLabel")} />
)}
<Primitive.SortingMenu tooltip={t("filters.sortLabel")} />
{actionMenu && <ActionMenu variant="primary" {...actionMenu} />}
{action && <DataTableAction {...action} />}
</div>
</div>
</Primitive.Toolbar>
<Primitive.Table emptyState={emptyState} />
{enablePagination && (
<Primitive.Pagination translations={paginationTranslations} />
)}
{enableCommands && (
<Primitive.CommandBar selectedLabel={(count) => `${count} selected`} />
)}
</Primitive>
)
}
function transformSortingState(value: DataTableSortingState) {
return value.desc ? `-${value.id}` : value.id
}
function parseSortingState(value: string) {
return value.startsWith("-")
? { id: value.slice(1), desc: true }
: { id: value, desc: false }
}
function transformPaginationState(value: DataTablePaginationState) {
return value.pageIndex * value.pageSize
}
function parsePaginationState(value: string, pageSize: number) {
const offset = parseInt(value)
return {
pageIndex: Math.floor(offset / pageSize),
pageSize,
}
}
function parseFilterState(
filterIds: string[],
value: Record<string, string | undefined>
) {
if (!value) {
return {}
}
const filters: DataTableFilteringState = {}
for (const id of filterIds) {
const filterValue = value[id]
if (filterValue) {
filters[id] = {
id,
value: JSON.parse(filterValue),
}
}
}
return filters
}
function getQueryParamKey(key: string, prefix?: string) {
return prefix ? `${prefix}_${key}` : key
}
const useDataTableTranslations = () => {
const { t } = useTranslation()
const paginationTranslations = {
of: t("general.of"),
results: t("general.results"),
pages: t("general.pages"),
prev: t("general.prev"),
next: t("general.next"),
}
const toolbarTranslations = {
clearAll: t("actions.clearAll"),
}
return {
pagination: paginationTranslations,
toolbar: toolbarTranslations,
}
}
const DataTableAction = ({
label,
disabled,
...props
}: DataTableActionProps) => {
const buttonProps = {
size: "small" as const,
disabled: disabled ?? false,
type: "button" as const,
variant: "secondary" as const,
}
if ("to" in props) {
return (
<Button {...buttonProps} asChild>
<Link to={props.to}>{label}</Link>
</Button>
)
}
return (
<Button {...buttonProps} onClick={props.onClick}>
{label}
</Button>
)
}
@@ -0,0 +1 @@
export * from "./data-table"
@@ -7,8 +7,8 @@ import { useTranslation } from "react-i18next"
import { useSelectedParams } from "../hooks"
import { useDataTableFilterContext } from "./context"
import { IFilter } from "./types"
import FilterChip from "./filter-chip"
import { IFilter } from "./types"
interface SelectFilterProps extends IFilter {
options: { label: string; value: unknown }[]
@@ -41,7 +41,9 @@ export const SelectFilter = ({
.map((v) => options.find((o) => o.value === v)?.label)
.filter(Boolean) as string[]
const [previousValue, setPreviousValue] = useState<string | string[] | undefined>(labelValues)
const [previousValue, setPreviousValue] = useState<
string | string[] | undefined
>(labelValues)
const handleRemove = () => {
selectedParams.delete()
@@ -84,8 +86,16 @@ export const SelectFilter = ({
}
}
const normalizedValues = labelValues ? (Array.isArray(labelValues) ? labelValues : [labelValues]) : null
const normalizedPrev = previousValue ? (Array.isArray(previousValue) ? previousValue : [previousValue]) : null
const normalizedValues = labelValues
? Array.isArray(labelValues)
? labelValues
: [labelValues]
: null
const normalizedPrev = previousValue
? Array.isArray(previousValue)
? previousValue
: [previousValue]
: null
return (
<Popover.Root modal open={open} onOpenChange={handleOpenChange}>
@@ -176,7 +176,7 @@ export const DataTableRoot = <TData,>({
: undefined,
}}
className={clx({
"bg-ui-bg-base sticky left-0 after:absolute after:inset-y-0 after:right-0 after:h-full after:w-px after:bg-transparent after:content-['']":
"bg-ui-bg-subtle sticky left-0 after:absolute after:inset-y-0 after:right-0 after:h-full after:w-px after:bg-transparent after:content-['']":
isStickyHeader,
"left-[68px]":
isStickyHeader && hasSelect && !isSelectHeader,
@@ -18,7 +18,10 @@ interface DataTableProps<TData>
// const MemoizedDataTableRoot = memo(DataTableRoot) as typeof DataTableRoot
const MemoizedDataTableQuery = memo(DataTableQuery) as typeof DataTableQuery
export const DataTable = <TData,>({
/**
* @deprecated Use the DataTable component from "/components/data-table" instead
*/
export const _DataTable = <TData,>({
table,
columns,
pagination,
@@ -40,7 +40,7 @@ export const useCustomerGroup = (
}
export const useCustomerGroups = (
query?: Record<string, any>,
query?: HttpTypes.AdminGetCustomerGroupsParams,
options?: Omit<
UseQueryOptions<
HttpTypes.AdminGetCustomerGroupsParams,
@@ -127,6 +127,29 @@ export const useDeleteCustomerGroup = (
})
}
export const useDeleteCustomerGroupLazy = (
options?: UseMutationOptions<
HttpTypes.AdminCustomerGroupDeleteResponse,
FetchError,
{ id: string }
>
) => {
return useMutation({
mutationFn: ({ id }) => sdk.admin.customerGroup.delete(id),
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: customerGroupsQueryKeys.lists(),
})
queryClient.invalidateQueries({
queryKey: customerGroupsQueryKeys.detail(variables.id),
})
options?.onSuccess?.(data, variables, context)
},
...options,
})
}
export const useAddCustomersToGroup = (
id: string,
options?: UseMutationOptions<
@@ -241,6 +241,32 @@ export const useDeleteVariant = (
})
}
export const useDeleteVariantLazy = (
productId: string,
options?: UseMutationOptions<
HttpTypes.AdminProductVariantDeleteResponse,
FetchError,
{ variantId: string }
>
) => {
return useMutation({
mutationFn: ({ variantId }) =>
sdk.admin.product.deleteVariant(productId, variantId),
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({ queryKey: variantsQueryKeys.lists() })
queryClient.invalidateQueries({
queryKey: variantsQueryKeys.detail(variables.variantId),
})
queryClient.invalidateQueries({
queryKey: productsQueryKeys.detail(productId),
})
options?.onSuccess?.(data, variables, context)
},
...options,
})
}
export const useProduct = (
id: string,
query?: Record<string, any>,
@@ -0,0 +1,55 @@
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
export const useDateFilterOptions = () => {
const { t } = useTranslation()
const today = useMemo(() => {
const date = new Date()
date.setHours(0, 0, 0, 0)
return date
}, [])
return useMemo(() => {
return [
{
label: t("filters.date.today"),
value: {
$gte: today.toISOString(),
},
},
{
label: t("filters.date.lastSevenDays"),
value: {
$gte: new Date(
today.getTime() - 7 * 24 * 60 * 60 * 1000
).toISOString(), // 7 days ago
},
},
{
label: t("filters.date.lastThirtyDays"),
value: {
$gte: new Date(
today.getTime() - 30 * 24 * 60 * 60 * 1000
).toISOString(), // 30 days ago
},
},
{
label: t("filters.date.lastNinetyDays"),
value: {
$gte: new Date(
today.getTime() - 90 * 24 * 60 * 60 * 1000
).toISOString(), // 90 days ago
},
},
{
label: t("filters.date.lastTwelveMonths"),
value: {
$gte: new Date(
today.getTime() - 365 * 24 * 60 * 60 * 1000
).toISOString(), // 365 days ago
},
},
]
}, [today, t])
}
File diff suppressed because it is too large Load Diff
@@ -316,6 +316,12 @@
"greaterThanLabel": "größer als {{value}}",
"andLabel": "Und"
},
"radio": {
"yes": "Ja",
"no": "Nein",
"true": "Wahr",
"false": "Falsch"
},
"addFilter": "Filter hinzufügen"
},
"errorBoundary": {
@@ -467,7 +473,17 @@
}
},
"deleteWarning": "Sie sind dabei, das Produkt {{title}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.",
"variants": "Varianten",
"variants": {
"header": "Varianten",
"empty": {
"heading": "Keine Varianten",
"description": "Es gibt keine Varianten, um angezeigt zu werden."
},
"filtered": {
"heading": "Keine Ergebnisse",
"description": "Keine Varianten stimmen mit den aktuellen Filterkriterien überein."
}
},
"attributes": "Attribute",
"editAttributes": "Attribute bearbeiten",
"editOptions": "Optionen bearbeiten",
@@ -298,6 +298,9 @@
}
},
"filters": {
"sortLabel": "Sort",
"filterLabel": "Filter",
"searchLabel": "Search",
"date": {
"today": "Today",
"lastSevenDays": "Last 7 days",
@@ -306,7 +309,9 @@
"lastTwelveMonths": "Last 12 months",
"custom": "Custom",
"from": "From",
"to": "To"
"to": "To",
"starting": "Starting",
"ending": "Ending"
},
"compare": {
"lessThan": "Less than",
@@ -317,6 +322,18 @@
"greaterThanLabel": "greater than {{value}}",
"andLabel": "and"
},
"sorting": {
"alphabeticallyAsc": "A to Z",
"alphabeticallyDesc": "Z to A",
"dateAsc": "Newest first",
"dateDesc": "Oldest first"
},
"radio": {
"yes": "Yes",
"no": "No",
"true": "True",
"false": "False"
},
"addFilter": "Add filter"
},
"errorBoundary": {
@@ -468,7 +485,17 @@
}
},
"deleteWarning": "You are about to delete the product {{title}}. This action cannot be undone.",
"variants": "Variants",
"variants": {
"header": "Variants",
"empty": {
"heading": "No variants",
"description": "There are no variants to display."
},
"filtered": {
"heading": "No results",
"description": "No variants match the current filter criteria."
}
},
"attributes": "Attributes",
"editAttributes": "Edit Attributes",
"editOptions": "Edit Options",
@@ -891,6 +918,16 @@
"customerGroups": {
"domain": "Customer Groups",
"subtitle": "Organize customers into groups. Groups can have different promotions and prices.",
"list": {
"empty": {
"heading": "No customer groups",
"description": "There are no customer groups to display."
},
"filtered": {
"heading": "No results",
"description": "No customer groups match the current filter criteria."
}
},
"create": {
"header": "Create Customer Group",
"hint": "Create a new customer group to segment your customers.",
@@ -316,6 +316,12 @@
"greaterThanLabel": "mayor que {{value}}",
"andLabel": "y"
},
"radio": {
"yes": "Sí",
"no": "No",
"true": "Verdadero",
"false": "Falso"
},
"addFilter": "Agregar filtro"
},
"errorBoundary": {
@@ -467,7 +473,17 @@
}
},
"deleteWarning": "Estás a punto de eliminar el producto {{title}}. Esta acción no puede deshacerse.",
"variants": "Variantes",
"variants": {
"header": "Variantes",
"empty": {
"heading": "No hay variantes",
"description": "No hay variantes para mostrar."
},
"filtered": {
"heading": "No hay resultados",
"description": "No hay variantes que coincidan con los criterios de filtro actuales."
}
},
"attributes": "Atributos",
"editAttributes": "Editar Atributos",
"editOptions": "Editar Opciones",
File diff suppressed because it is too large Load Diff
@@ -317,6 +317,12 @@
"greaterThanLabel": "maggiore di {{value}}",
"andLabel": "e"
},
"radio": {
"yes": "Sì",
"no": "No",
"true": "Vero",
"false": "Falso"
},
"addFilter": "Aggiungi filtro"
},
"errorBoundary": {
@@ -468,7 +474,17 @@
}
},
"deleteWarning": "Stai per eliminare il prodotto {{title}}. Questa azione non può essere annullata.",
"variants": "Varianti",
"variants": {
"header": "Varianti",
"empty": {
"heading": "Nessuna variante",
"description": "Non ci sono varianti da visualizzare."
},
"filtered": {
"heading": "Nessun risultato",
"description": "Nessuna variante corrisponde ai criteri di filtro correnti."
}
},
"attributes": "Attributi",
"editAttributes": "Modifica Attributi",
"editOptions": "Modifica Opzioni",
@@ -2092,14 +2108,14 @@
"label": "La lista prezzi ha una data di scadenza?",
"hint": "Pianifica la lista prezzi per disattivarsi in futuro."
},
"customerAvailability": {
"header": "Scegli gruppi di clienti",
"label": "Disponibilità cliente",
"hint": "Scegli quali gruppi di clienti la lista prezzi dovrebbe essere applicata.",
"placeholder": "Cerca gruppi di clienti",
"attribute": "Gruppi di clienti"
}
"customerAvailability": {
"header": "Scegli gruppi di clienti",
"label": "Disponibilità cliente",
"hint": "Scegli quali gruppi di clienti la lista prezzi dovrebbe essere applicata.",
"placeholder": "Cerca gruppi di clienti",
"attribute": "Gruppi di clienti"
}
}
},
"profile": {
"domain": "Profilo",
@@ -2771,4 +2787,4 @@
"seconds_one": "Secondo",
"seconds_other": "Secondi"
}
}
}
@@ -317,6 +317,12 @@
"greaterThanLabel": "{{value}}以上",
"andLabel": "かつ"
},
"radio": {
"yes": "はい",
"no": "いいえ",
"true": "真",
"false": "偽"
},
"addFilter": "フィルター追加"
},
"errorBoundary": {
@@ -468,7 +474,17 @@
}
},
"deleteWarning": "商品「{{title}}」を削除しようとしています。この操作は元に戻せません。",
"variants": "バリエーション",
"variants": {
"header": "バリエーション",
"empty": {
"heading": "バリエーションはありません",
"description": "表示するバリエーションはありません。"
},
"filtered": {
"heading": "結果はありません",
"description": "現在のフィルター条件に一致するバリエーションはありません。"
}
},
"attributes": "属性",
"editAttributes": "属性を編集",
"editOptions": "オプションを編集",
@@ -316,6 +316,12 @@
"greaterThanLabel": "więcej niż {{value}}",
"andLabel": "i"
},
"radio": {
"yes": "Tak",
"no": "Nie",
"true": "Prawda",
"false": "Fałsz"
},
"addFilter": "Dodaj filtr"
},
"errorBoundary": {
@@ -467,7 +473,17 @@
}
},
"deleteWarning": "Zamierzasz usunąć produkt {{title}}. Ta akcja nie może zostać cofnięta.",
"variants": "Warianty",
"variants": {
"header": "Warianty",
"empty": {
"heading": "Brak wariantów",
"description": "Nie ma wariantów do wyświetlenia."
},
"filtered": {
"heading": "Brak wariantów",
"description": "Nie ma wariantów, które pasują do aktualnych kryteriów filtrów."
}
},
"attributes": "Atrybuty",
"editAttributes": "Edytuj atrybuty",
"editOptions": "Edytuj opcje",
@@ -316,6 +316,12 @@
"greaterThanLabel": "maior que {{value}}",
"andLabel": "e"
},
"radio": {
"yes": "Sim",
"no": "Não",
"true": "Verdadeiro",
"false": "Falso"
},
"addFilter": "Adicionar filtro"
},
"errorBoundary": {
@@ -467,7 +473,17 @@
}
},
"deleteWarning": "Você está prestes a excluir o produto {{title}}. Esta ação não pode ser desfeita.",
"variants": "Variantes",
"variants": {
"header": "Variantes",
"empty": {
"heading": "Nenhuma variante",
"description": "Não há variantes para exibir."
},
"filtered": {
"heading": "Nenhuma variante",
"description": "Nenhuma variante corresponde aos critérios de filtro atuais."
}
},
"attributes": "Atributos",
"editAttributes": "Editar atributos",
"editOptions": "Editar opções",
@@ -316,6 +316,12 @@
"greaterThanLabel": "มากกว่า {{value}}",
"andLabel": "และ"
},
"radio": {
"yes": "ใช่",
"no": "ไม่",
"true": "จริง",
"false": "เท็จ"
},
"addFilter": "เพิ่มตัวกรอง"
},
"errorBoundary": {
@@ -467,7 +473,17 @@
}
},
"deleteWarning": "คุณกำลังจะลบสินค้า {{title}} การดำเนินการนี้ไม่สามารถยกเลิกได้",
"variants": "ตัวเลือก",
"variants": {
"header": "ตัวเลือก",
"empty": {
"heading": "ไม่มีตัวเลือก",
"description": "ไม่มีตัวเลือกที่จะแสดง"
},
"filtered": {
"heading": "ไม่มีผลลัพธ์",
"description": "ไม่มีตัวเลือกที่ตรงกับเกณฑ์การกรองปัจจุบัน"
}
},
"attributes": "แอตทริบิวต์",
"editAttributes": "แก้ไขแอตทริบิวต์",
"editOptions": "แก้ไขตัวเลือก",
@@ -316,6 +316,12 @@
"greaterThanLabel": "{{value}}'den büyük",
"andLabel": "ve"
},
"radio": {
"yes": "Evet",
"no": "Hayır",
"true": "Doğru",
"false": "Yanlış"
},
"addFilter": "Filtre Ekle"
},
"errorBoundary": {
@@ -467,7 +473,17 @@
}
},
"deleteWarning": "Ürün {{title}}'i silmek üzeresiniz. Bu işlem geri alınamaz.",
"variants": "Varyantlar",
"variants": {
"header": "Varyantlar",
"empty": {
"heading": "Varyant yok",
"description": "Görüntülenecek varyant yok."
},
"filtered": {
"heading": "Sonuç yok",
"description": "Varyantlar mevcut filtrelerle eşleşmiyor."
}
},
"attributes": "Öznitelikler",
"editAttributes": "Öznitelikleri Düzenle",
"editOptions": "Seçenekleri Düzenle",
@@ -317,6 +317,12 @@
"greaterThanLabel": "більше ніж {{value}}",
"andLabel": "і"
},
"radio": {
"yes": "Так",
"no": "немає",
"true": "правда",
"false": "помилковий"
},
"addFilter": "Додати фільтр"
},
"errorBoundary": {
@@ -468,7 +474,17 @@
}
},
"deleteWarning": "Ви збираєтеся видалити продукт {{title}}. Цю дію не можна скасувати.",
"variants": "Варіанти",
"variants": {
"header": "Варіанти",
"empty": {
"heading": "Немає варіантів",
"description": "Немає варіантів для відображення."
},
"filtered": {
"heading": "Немає результатів",
"description": "Немає варіантів, які відповідають поточному критерію фільтрації."
}
},
"attributes": "Атрибути",
"editAttributes": "Редагувати атрибути",
"editOptions": "Редагувати опції",
@@ -6,7 +6,7 @@ import { RowSelectionState, createColumnHelper } from "@tanstack/react-table"
import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useBatchRemoveSalesChannelsFromApiKey } from "../../../../../hooks/api/api-keys"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns"
@@ -109,7 +109,7 @@ export const ApiKeySalesChannelSection = ({
]}
/>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
filters={filters}
@@ -2,7 +2,7 @@ import { Button, Container, Heading, Text } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useApiKeys } from "../../../../../hooks/api/api-keys"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useApiKeyManagementTableColumns } from "./use-api-key-management-table-columns"
@@ -70,7 +70,7 @@ export const ApiKeyManagementListTable = ({
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
columns={columns}
@@ -15,7 +15,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { VisuallyHidden } from "../../../../../components/utilities/visually-hidden"
import { useBatchAddSalesChannelsToApiKey } from "../../../../../hooks/api/api-keys"
@@ -139,7 +139,7 @@ export const ApiKeySalesChannelsForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-1 flex-col overflow-auto">
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -12,7 +12,7 @@ import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import * as zod from "zod"
import { RouteFocusModal, useRouteModal } from "../../../../components/modals"
import { DataTable } from "../../../../components/table/data-table"
import { _DataTable } from "../../../../components/table/data-table"
import { KeyboundForm } from "../../../../components/utilities/keybound-form"
import { useAddOrRemoveCampaignPromotions } from "../../../../hooks/api/campaigns"
import { usePromotions } from "../../../../hooks/api/promotions"
@@ -124,7 +124,7 @@ export const AddCampaignPromotionsForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex size-full flex-col overflow-y-auto">
<DataTable
<_DataTable
table={table}
count={count}
columns={columns}
@@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useAddOrRemoveCampaignPromotions } from "../../../../../hooks/api/campaigns"
import { usePromotions } from "../../../../../hooks/api/promotions"
import { usePromotionTableColumns } from "../../../../../hooks/table/columns/use-promotion-table-columns"
@@ -89,7 +89,7 @@ export const CampaignPromotionSection = ({
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -7,7 +7,7 @@ import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../components/common/action-menu"
import { DataTable } from "../../../../components/table/data-table"
import { _DataTable } from "../../../../components/table/data-table"
import {
useCampaigns,
useDeleteCampaign,
@@ -58,7 +58,7 @@ export const CampaignListTable = () => {
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -14,7 +14,7 @@ import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useUpdateProductCategoryProducts } from "../../../../../hooks/api/categories"
import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
@@ -125,7 +125,7 @@ export const CategoryProductSection = ({
]}
/>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
columns={columns}
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProductCategories } from "../../../../../hooks/api/categories"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useDeleteProductCategoryAction } from "../../../common/hooks/use-delete-product-category-action"
@@ -84,7 +84,7 @@ export const CategoryListTable = () => {
</Button>
</div>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -15,7 +15,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProductCategoryProducts } from "../../../../../hooks/api/categories"
import { useProducts } from "../../../../../hooks/api/products"
@@ -159,7 +159,7 @@ export const EditCategoryProductsForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -15,7 +15,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals/index.ts"
import { DataTable } from "../../../../../components/table/data-table/data-table.tsx"
import { _DataTable } from "../../../../../components/table/data-table/data-table.tsx"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx"
import { useUpdateCollectionProducts } from "../../../../../hooks/api/collections.tsx"
import { useProducts } from "../../../../../hooks/api/products.tsx"
@@ -168,7 +168,7 @@ export const AddProductsToCollectionForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -6,7 +6,7 @@ import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useUpdateCollectionProducts } from "../../../../../hooks/api/collections"
import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
@@ -114,7 +114,7 @@ export const CollectionProductSection = ({
]}
/>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
search
@@ -6,7 +6,7 @@ import { HttpTypes } from "@medusajs/types"
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 { useCollectionTableColumns } from "../../../../../hooks/table/columns/use-collection-table-columns"
import { useCollectionTableFilters } from "../../../../../hooks/table/filters"
@@ -60,7 +60,7 @@ export const CollectionListTable = () => {
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -15,7 +15,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useAddCustomersToGroup } from "../../../../../hooks/api/customer-groups"
import { useCustomers } from "../../../../../hooks/api/customers"
@@ -153,7 +153,7 @@ export const AddCustomersForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useRemoveCustomersFromGroup } from "../../../../../hooks/api/customer-groups"
import { useCustomers } from "../../../../../hooks/api/customers"
import { useCustomerTableColumns } from "../../../../../hooks/table/columns/use-customer-table-columns"
@@ -95,7 +95,7 @@ export const CustomerGroupCustomerSection = ({
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -1,172 +1,249 @@
import { PencilSquare, Trash } from "@medusajs/icons"
import { HttpTypes } from "@medusajs/types"
import {
Button,
Container,
Heading,
Text,
createDataTableColumnHelper,
createDataTableFilterHelper,
toast,
usePrompt,
} from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { keepPreviousData } from "@tanstack/react-query"
import { useCallback, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { useNavigate } from "react-router-dom"
import { HttpTypes } from "@medusajs/types"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { DataTable } from "../../../../../components/data-table"
import { SingleColumnPage } from "../../../../../components/layout/pages"
import { useDashboardExtension } from "../../../../../extensions"
import {
useCustomerGroups,
useDeleteCustomerGroup,
} from "../../../../../hooks/api/customer-groups"
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
useDeleteCustomerGroupLazy,
} from "../../../../../hooks/api"
import { useDateFilterOptions } from "../../../../../hooks/filters/use-date-filter-options"
import { useDate } from "../../../../../hooks/use-date"
import { useQueryParams } from "../../../../../hooks/use-query-params"
const PAGE_SIZE = 20
const PAGE_SIZE = 10
export const CustomerGroupListTable = () => {
const { t } = useTranslation()
const { getWidgets } = useDashboardExtension()
const { searchParams, raw } = useCustomerGroupTableQuery({
pageSize: PAGE_SIZE,
})
const { customer_groups, count, isLoading, isError, error } =
useCustomerGroups({
...searchParams,
fields: "id,name,customers.id",
})
const { q, order, offset, created_at, updated_at } = useQueryParams([
"q",
"order",
"offset",
"created_at",
"updated_at",
])
const filters = useCustomerGroupTableFilters()
const columns = useColumns()
const filters = useFilters()
const { table } = useDataTable({
data: customer_groups ?? [],
columns,
enablePagination: true,
count,
getRowId: (row) => row.id,
pageSize: PAGE_SIZE,
})
const { customer_groups, count, isPending, isError, error } =
useCustomerGroups(
{
q,
order,
offset: offset ? parseInt(offset) : undefined,
limit: PAGE_SIZE,
created_at: created_at ? JSON.parse(created_at) : undefined,
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
fields: "id,name,created_at,updated_at,customers.id",
},
{
placeholderData: keepPreviousData,
}
)
if (isError) {
throw error
}
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<div>
<Heading level="h2">{t("customerGroups.domain")}</Heading>
<Text className="text-ui-fg-subtle" size="small">
{t("customerGroups.subtitle")}
</Text>
</div>
<Link to="/customer-groups/create">
<Button size="small" variant="secondary">
{t("actions.create")}
</Button>
</Link>
</div>
<DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
count={count}
filters={filters}
search
pagination
navigateTo={(row) => `/customer-groups/${row.original.id}`}
orderBy={[
{ key: "name", label: t("fields.name") },
{ key: "created_at", label: t("fields.createdAt") },
{ key: "updated_at", label: t("fields.updatedAt") },
]}
queryObject={raw}
isLoading={isLoading}
/>
</Container>
<SingleColumnPage
widgets={{
before: getWidgets("customer_group.list.before"),
after: getWidgets("customer_group.list.after"),
}}
>
<Container className="overflow-hidden p-0">
<DataTable
data={customer_groups}
columns={columns}
filters={filters}
heading={t("customerGroups.domain")}
rowCount={count}
getRowId={(row) => row.id}
rowHref={(row) => `/customer-groups/${row.id}`}
action={{
label: t("actions.create"),
to: "/customer-groups/create",
}}
emptyState={{
empty: {
heading: t("customerGroups.list.empty.heading"),
description: t("customerGroups.list.empty.description"),
},
filtered: {
heading: t("customerGroups.list.filtered.heading"),
description: t("customerGroups.list.filtered.description"),
},
}}
pageSize={PAGE_SIZE}
isLoading={isPending}
/>
</Container>
</SingleColumnPage>
)
}
const CustomerGroupRowActions = ({
group,
}: {
group: HttpTypes.AdminCustomerGroup
}) => {
const { t } = useTranslation()
const prompt = usePrompt()
const { mutateAsync } = useDeleteCustomerGroup(group.id)
const handleDelete = async () => {
const res = await prompt({
title: t("customerGroups.delete.title"),
description: t("customerGroups.delete.description", {
name: group.name,
}),
confirmText: t("actions.delete"),
cancelText: t("actions.cancel"),
})
if (!res) {
return
}
await mutateAsync(undefined, {
onSuccess: () => {
toast.success(
t("customerGroups.delete.successToast", {
name: group.name,
})
)
},
onError: (error) => {
toast.error(error.message)
},
})
}
return (
<ActionMenu
groups={[
{
actions: [
{
label: t("actions.edit"),
to: `/customer-groups/${group.id}/edit`,
icon: <PencilSquare />,
},
],
},
{
actions: [
{
label: t("actions.delete"),
onClick: handleDelete,
icon: <Trash />,
},
],
},
]}
/>
)
}
const columnHelper = createColumnHelper<HttpTypes.AdminCustomerGroup>()
const columnHelper = createDataTableColumnHelper<HttpTypes.AdminCustomerGroup>()
const useColumns = () => {
const columns = useCustomerGroupTableColumns()
const { t } = useTranslation()
const { getFullDate } = useDate()
const navigate = useNavigate()
const prompt = usePrompt()
return useMemo(
() => [
...columns,
columnHelper.display({
id: "actions",
cell: ({ row }) => <CustomerGroupRowActions group={row.original} />,
}),
],
[columns]
const { mutateAsync: deleteCustomerGroup } = useDeleteCustomerGroupLazy()
const handleDeleteCustomerGroup = useCallback(
async ({ id, name }: { id: string; name: string }) => {
const res = await prompt({
title: t("customerGroups.delete.title"),
description: t("customerGroups.delete.description", {
name,
}),
verificationText: name,
verificationInstruction: t("general.typeToConfirm"),
confirmText: t("actions.delete"),
cancelText: t("actions.cancel"),
})
if (!res) {
return
}
await deleteCustomerGroup(
{ id },
{
onSuccess: () => {
toast.success(t("customerGroups.delete.successToast", { name }))
},
onError: (e) => {
toast.error(e.message)
},
}
)
},
[t, prompt, deleteCustomerGroup]
)
return useMemo(() => {
return [
columnHelper.accessor("name", {
header: t("fields.name"),
enableSorting: true,
sortAscLabel: t("filters.sorting.alphabeticallyAsc"),
sortDescLabel: t("filters.sorting.alphabeticallyDesc"),
}),
columnHelper.accessor("customers", {
header: t("customers.domain"),
cell: ({ row }) => {
return <span>{row.original.customers?.length ?? 0}</span>
},
}),
columnHelper.accessor("created_at", {
header: t("fields.createdAt"),
cell: ({ row }) => {
return (
<span>
{getFullDate({
date: row.original.created_at,
includeTime: true,
})}
</span>
)
},
enableSorting: true,
sortAscLabel: t("filters.sorting.dateAsc"),
sortDescLabel: t("filters.sorting.dateDesc"),
}),
columnHelper.accessor("updated_at", {
header: t("fields.updatedAt"),
cell: ({ row }) => {
return (
<span>
{getFullDate({
date: row.original.updated_at,
includeTime: true,
})}
</span>
)
},
enableSorting: true,
sortAscLabel: t("filters.sorting.dateAsc"),
sortDescLabel: t("filters.sorting.dateDesc"),
}),
columnHelper.action({
actions: [
[
{
icon: <PencilSquare />,
label: t("actions.edit"),
onClick: (row) => {
navigate(`/customer-groups/${row.row.original.id}/edit`)
},
},
],
[
{
icon: <Trash />,
label: t("actions.delete"),
onClick: (row) => {
handleDeleteCustomerGroup({
id: row.row.original.id,
name: row.row.original.name ?? "",
})
},
},
],
],
}),
]
}, [t, navigate, getFullDate, handleDeleteCustomerGroup])
}
const filterHelper = createDataTableFilterHelper<HttpTypes.AdminCustomerGroup>()
const useFilters = () => {
const { t } = useTranslation()
const { getFullDate } = useDate()
const dateFilterOptions = useDateFilterOptions()
return useMemo(() => {
return [
filterHelper.accessor("created_at", {
type: "date",
label: t("fields.createdAt"),
format: "date",
formatDateValue: (date) => getFullDate({ date }),
rangeOptionStartLabel: t("filters.date.starting"),
rangeOptionEndLabel: t("filters.date.ending"),
rangeOptionLabel: t("filters.date.custom"),
options: dateFilterOptions,
}),
filterHelper.accessor("updated_at", {
type: "date",
label: t("fields.updatedAt"),
format: "date",
rangeOptionStartLabel: t("filters.date.starting"),
rangeOptionEndLabel: t("filters.date.ending"),
rangeOptionLabel: t("filters.date.custom"),
formatDateValue: (date) => getFullDate({ date }),
options: dateFilterOptions,
}),
]
}, [t, dateFilterOptions, getFullDate])
}
@@ -15,17 +15,18 @@ import { PencilSquare, Trash } from "@medusajs/icons"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu/index.ts"
import { DataTable } from "../../../../../components/table/data-table/index.ts"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { _DataTable } from "../../../../../components/table/data-table"
import { useBatchCustomerCustomerGroups } from "../../../../../hooks/api"
import {
useCustomerGroups,
useRemoveCustomersFromGroup,
} from "../../../../../hooks/api/customer-groups.tsx"
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns.tsx"
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters.tsx"
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query.tsx"
import { useDataTable } from "../../../../../hooks/use-data-table.tsx"
import { useBatchCustomerCustomerGroups } from "../../../../../hooks/api"
} from "../../../../../hooks/api/customer-groups"
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
type CustomerGroupSectionProps = {
customer: HttpTypes.AdminCustomer
@@ -97,19 +98,23 @@ export const CustomerGroupSection = ({
return
}
try {
await batchCustomerCustomerGroups({ remove: customerGroupIds })
toast.success(
t("customers.groups.removed.success", {
groups: customer_groups!
.filter((cg) => customerGroupIds.includes(cg.id))
.map((cg) => cg?.name),
})
)
} catch (e) {
toast.error(e.message)
}
await batchCustomerCustomerGroups(
{ remove: customerGroupIds },
{
onSuccess: () => {
toast.success(
t("customers.groups.removed.success", {
groups: customer_groups!
.filter((cg) => customerGroupIds.includes(cg.id))
.map((cg) => cg?.name),
})
)
},
onError: (error) => {
toast.error(error.message)
},
}
)
}
if (isError) {
@@ -126,7 +131,7 @@ export const CustomerGroupSection = ({
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -259,6 +264,6 @@ const useColumns = (customerId: string) => {
),
}),
],
[columns]
[columns, customerId]
)
}
@@ -6,7 +6,7 @@ import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useOrders } from "../../../../../hooks/api/orders"
import { useOrderTableColumns } from "../../../../../hooks/table/columns/use-order-table-columns"
import { useOrderTableFilters } from "../../../../../hooks/table/filters/use-order-table-filters"
@@ -70,7 +70,7 @@ export const CustomerOrderSection = ({
{/* </Button>*/}
{/*</div>*/}
</div>
<DataTable
<_DataTable
columns={columns}
table={table}
pagination
@@ -8,7 +8,7 @@ import { Link } from "react-router-dom"
import { HttpTypes } from "@medusajs/types"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useCustomers } from "../../../../../hooks/api/customers"
import { useCustomerTableColumns } from "../../../../../hooks/table/columns/use-customer-table-columns"
import { useCustomerTableFilters } from "../../../../../hooks/table/filters/use-customer-table-filters"
@@ -56,7 +56,7 @@ export const CustomerListTable = () => {
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -15,14 +15,14 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useBatchCustomerCustomerGroups } from "../../../../../hooks/api"
import { useCustomerGroups } from "../../../../../hooks/api/customer-groups"
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useBatchCustomerCustomerGroups } from "../../../../../hooks/api"
type AddCustomerGroupsFormProps = {
customerId: string
@@ -155,7 +155,7 @@ export const AddCustomerGroupsForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -1,8 +1,8 @@
import { DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useInventoryItemLevels } from "../../../../../hooks/api/inventory"
import { useLocationLevelTableQuery } from "./use-location-list-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useLocationListTableColumns } from "./use-location-list-table-columns"
import { useLocationLevelTableQuery } from "./use-location-list-table-query"
const PAGE_SIZE = 20
@@ -42,7 +42,7 @@ export const ItemLocationListTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -1,15 +1,15 @@
import { useMemo } from "react"
import { HttpTypes } from "@medusajs/types"
import { useMemo } from "react"
import { DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useStockLocations } from "../../../../../hooks/api"
import { useReservationItems } from "../../../../../hooks/api/reservations"
import { useDataTable } from "../../../../../hooks/use-data-table"
import {
ExtendedReservationItem,
useReservationTableColumn,
} from "./use-reservation-list-table-columns"
import { useReservationsTableQuery } from "./use-reservation-list-table-query"
import { useStockLocations } from "../../../../../hooks/api"
const PAGE_SIZE = 20
@@ -57,7 +57,7 @@ export const ReservationItemTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -5,7 +5,7 @@ import { RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
import { useTranslation } from "react-i18next"
import { Link, useNavigate } from "react-router-dom"
import { DataTable } from "../../../../components/table/data-table"
import { _DataTable } from "../../../../components/table/data-table"
import { useInventoryItems } from "../../../../hooks/api/inventory"
import { useDataTable } from "../../../../hooks/use-data-table"
import { INVENTORY_ITEM_IDS_KEY } from "../../common/constants"
@@ -69,7 +69,7 @@ export const InventoryListTable = () => {
<Link to="create">{t("actions.create")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -15,7 +15,7 @@ import {
StackedFocusModal,
useStackedModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table"
import {
StaticCountry,
@@ -238,7 +238,7 @@ const AreaStackedModal = <TForm extends UseFormReturn<any>>({
</StackedFocusModal.Description>
</StackedFocusModal.Header>
<StackedFocusModal.Body className="flex-1 overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -16,7 +16,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers"
import { useUpdateStockLocationFulfillmentProviders } from "../../../../../hooks/api/stock-locations"
@@ -132,7 +132,7 @@ export const LocationEditFulfillmentProvidersForm = ({
<KeyboundForm onSubmit={handleSubmit} className="flex size-full flex-col">
<RouteFocusModal.Header />
<RouteFocusModal.Body className="flex flex-1 flex-col overflow-auto">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -12,7 +12,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { useUpdateStockLocationSalesChannels } from "../../../../../hooks/api/stock-locations"
import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns"
@@ -137,7 +137,7 @@ export const LocationEditSalesChannelsForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -7,7 +7,7 @@ import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
import { getReturnableQuantity } from "../../../../../lib/rma"
@@ -134,7 +134,7 @@ export const AddClaimItemsTable = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -2,7 +2,7 @@ import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useVariants } from "../../../../../hooks/api"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useClaimOutboundItemTableColumns } from "./use-claim-outbound-item-table-columns"
@@ -72,7 +72,7 @@ export const AddClaimOutboundItemsTable = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -2,7 +2,7 @@ import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useVariants } from "../../../../../hooks/api"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useOrderEditItemsTableColumns } from "./use-order-edit-item-table-columns"
@@ -65,7 +65,7 @@ export const AddOrderEditItemsTable = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -3,7 +3,7 @@ import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { getReturnableQuantity } from "../../../../../lib/rma"
import { useExchangeItemTableColumns } from "./use-exchange-item-table-columns"
@@ -102,7 +102,7 @@ export const AddExchangeInboundItemsTable = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -2,7 +2,7 @@ import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useVariants } from "../../../../../hooks/api"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useExchangeOutboundItemTableColumns } from "./use-exchange-outbound-item-table-columns"
@@ -72,7 +72,7 @@ export const AddExchangeOutboundItemsTable = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -8,7 +8,7 @@ import {
} from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
import { getReturnableQuantity } from "../../../../../lib/rma"
@@ -135,7 +135,7 @@ export const AddReturnItemsTable = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -2,7 +2,7 @@ import { Container, Heading } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table/data-table"
import { _DataTable } from "../../../../../components/table/data-table/data-table"
import { useOrders } from "../../../../../hooks/api/orders"
import { useOrderTableColumns } from "../../../../../hooks/table/columns/use-order-table-columns"
import { useOrderTableFilters } from "../../../../../hooks/table/filters/use-order-table-filters"
@@ -49,7 +49,7 @@ export const OrderListTable = () => {
<div className="flex items-center justify-between px-6 py-4">
<Heading>{t("orders.domain")}</Heading>
</div>
<DataTable
<_DataTable
columns={columns}
table={table}
pagination
@@ -10,7 +10,7 @@ import { useEffect, useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { StackedDrawer } from "../../../../../components/modals/stacked-drawer"
import { StackedFocusModal } from "../../../../../components/modals/stacked-focus-modal"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useCustomerGroups } from "../../../../../hooks/api/customer-groups"
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
@@ -119,7 +119,7 @@ export const PriceListCustomerGroupRuleForm = ({
return (
<div className="flex size-full flex-col overflow-hidden">
<Component.Body className="min-h-0 p-0">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -10,7 +10,7 @@ import { useMemo, useState } from "react"
import { UseFormReturn, useWatch } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
@@ -117,7 +117,7 @@ export const PriceListProductsForm = ({ form }: PriceListProductsFormProps) => {
return (
<div className="flex size-full flex-col">
<DataTable
<_DataTable
table={table}
columns={columns}
filters={filters}
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { usePriceListLinkProducts } from "../../../../../hooks/api/price-lists"
import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
@@ -133,7 +133,7 @@ export const PriceListProductSection = ({
]}
/>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
columns={columns}
@@ -2,7 +2,7 @@ import { Button, Container, Heading, Text } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { usePriceLists } from "../../../../../hooks/api/price-lists"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { usePricingTableColumns } from "./use-pricing-table-columns"
@@ -53,7 +53,7 @@ export const PriceListListTable = () => {
<Link to="create">{t("actions.create")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -9,7 +9,7 @@ import {
import { useMemo, useState } from "react"
import { UseFormReturn, useWatch } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
@@ -136,7 +136,7 @@ export const PriceListPricesAddProductIdsForm = ({
return (
<div className="flex size-full flex-col">
<DataTable
<_DataTable
table={table}
columns={columns}
filters={filters}
@@ -1,7 +1,7 @@
import { HttpTypes } from "@medusajs/types"
import { Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProducts } from "../../../../../hooks/api"
import { useProductTableColumns } from "../../../../../hooks/table/columns"
import { useProductTableFilters } from "../../../../../hooks/table/filters"
@@ -51,7 +51,7 @@ export const ProductTagProductSection = ({
<div className="px-6 py-4">
<Heading level="h2">{t("products.domain")}</Heading>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
queryObject={raw}
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"
import { Link, useLoaderData } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProductTags } from "../../../../../hooks/api"
import { useProductTagTableColumns } from "../../../../../hooks/table/columns"
import { useProductTagTableFilters } from "../../../../../hooks/table/filters"
@@ -60,7 +60,7 @@ export const ProductTagListTable = () => {
<Link to="create">{t("actions.create")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
queryObject={raw}
@@ -2,7 +2,7 @@ import { HttpTypes } from "@medusajs/types"
import { Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
@@ -48,7 +48,7 @@ export const ProductTypeProductSection = ({
<div className="px-6 py-4">
<Heading level="h2">{t("products.domain")}</Heading>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
isLoading={isPending}
@@ -6,7 +6,7 @@ import { useMemo } from "react"
import { useTranslation } from "react-i18next"
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 { useProductTypeTableColumns } from "../../../../../hooks/table/columns/use-product-type-table-columns"
import { useProductTypeTableFilters } from "../../../../../hooks/table/filters/use-product-type-table-filters"
@@ -57,7 +57,7 @@ export const ProductTypeListTable = () => {
<Link to="create">{t("actions.create")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
filters={filters}
isLoading={isLoading}
@@ -1,15 +1,15 @@
import { useTranslation } from "react-i18next"
import { Buildings, Component } from "@medusajs/icons"
import { Container, Heading } from "@medusajs/ui"
import { HttpTypes } from "@medusajs/types"
import { Container, Heading } from "@medusajs/ui"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { LinkButton } from "../../../../../components/common/link-button"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useInventoryTableColumns } from "./use-inventory-table-columns"
import { LinkButton } from "../../../../../components/common/link-button"
const PAGE_SIZE = 20
@@ -62,7 +62,7 @@ export function VariantInventorySection({
</div>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -13,7 +13,7 @@ import {
StackedFocusModal,
useStackedModal,
} from "../../../../../../../components/modals"
import { DataTable } from "../../../../../../../components/table/data-table"
import { _DataTable } from "../../../../../../../components/table/data-table"
import { useSalesChannels } from "../../../../../../../hooks/api/sales-channels"
import { useSalesChannelTableColumns } from "../../../../../../../hooks/table/columns/use-sales-channel-table-columns"
import { useSalesChannelTableFilters } from "../../../../../../../hooks/table/filters/use-sales-channel-table-filters"
@@ -131,7 +131,7 @@ export const ProductCreateSalesChannelStackedModal = ({
<StackedFocusModal.Content className="flex flex-col overflow-hidden">
<StackedFocusModal.Header />
<StackedFocusModal.Body className="flex-1 overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -1,20 +1,31 @@
import { Buildings, PencilSquare, Plus } from "@medusajs/icons"
import { Buildings, Component, PencilSquare, Trash } from "@medusajs/icons"
import { HttpTypes } from "@medusajs/types"
import { Container, Heading } from "@medusajs/ui"
import {
Badge,
clx,
Container,
createDataTableColumnHelper,
createDataTableCommandHelper,
createDataTableFilterHelper,
DataTableAction,
Tooltip,
usePrompt,
} from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useCallback, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
import { CellContext } from "@tanstack/react-table"
import { useNavigate } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { useProductVariants } from "../../../../../hooks/api/products"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { DataTable } from "../../../../../components/data-table"
import {
useDeleteVariantLazy,
useProductVariants,
} from "../../../../../hooks/api/products"
import { useDateFilterOptions } from "../../../../../hooks/filters/use-date-filter-options"
import { useDate } from "../../../../../hooks/use-date"
import { useQueryParams } from "../../../../../hooks/use-query-params"
import { PRODUCT_VARIANT_IDS_KEY } from "../../../common/constants"
import { useProductVariantTableColumns } from "./use-variant-table-columns"
import { useProductVariantTableFilters } from "./use-variant-table-filters"
import { useProductVariantTableQuery } from "./use-variant-table-query"
type ProductVariantSectionProps = {
product: HttpTypes.AdminProduct
@@ -26,15 +37,44 @@ export const ProductVariantSection = ({
product,
}: ProductVariantSectionProps) => {
const { t } = useTranslation()
const navigate = useNavigate()
const { searchParams, raw } = useProductVariantTableQuery({
pageSize: PAGE_SIZE,
})
const { variants, count, isLoading, isError, error } = useProductVariants(
const {
q,
order,
offset,
allow_backorder,
manage_inventory,
created_at,
updated_at,
} = useQueryParams([
"q",
"order",
"offset",
"manage_inventory",
"allow_backorder",
"created_at",
"updated_at",
])
const columns = useColumns(product)
const filters = useFilters()
const commands = useCommands()
const { variants, count, isPending, isError, error } = useProductVariants(
product.id,
{
...searchParams,
q,
order,
offset: offset ? parseInt(offset) : undefined,
limit: PAGE_SIZE,
allow_backorder: allow_backorder
? JSON.parse(allow_backorder)
: undefined,
manage_inventory: manage_inventory
? JSON.parse(manage_inventory)
: undefined,
created_at: created_at ? JSON.parse(created_at) : undefined,
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
fields: "*inventory_items.inventory.location_levels,+inventory_quantity",
},
{
@@ -42,45 +82,40 @@ export const ProductVariantSection = ({
}
)
const [selection, setSelection] = useState<RowSelectionState>({})
const filters = useProductVariantTableFilters()
const columns = useProductVariantTableColumns(product)
const { table } = useDataTable({
data: variants ?? [],
columns,
count,
enablePagination: true,
getRowId: (row) => row.id,
pageSize: PAGE_SIZE,
enableRowSelection: true,
rowSelection: {
state: selection,
updater: setSelection,
},
meta: {
product,
},
})
if (isError) {
throw error
}
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">{t("products.variants")}</Heading>
<ActionMenu
groups={[
<DataTable
data={variants}
columns={columns}
filters={filters}
rowCount={count}
getRowId={(row) => row.id}
rowHref={(row) => `/products/${product.id}/variants/${row.id}`}
pageSize={PAGE_SIZE}
isLoading={isPending}
heading={t("products.variants.header")}
emptyState={{
empty: {
heading: t("products.variants.empty.heading"),
description: t("products.variants.empty.description"),
},
filtered: {
heading: t("products.variants.filtered.heading"),
description: t("products.variants.filtered.description"),
},
}}
action={{
label: t("actions.create"),
to: `variants/create`,
}}
actionMenu={{
groups: [
{
actions: [
{
label: t("actions.create"),
to: `variants/create`,
icon: <Plus />,
},
{
label: t("products.editPrices"),
to: `prices`,
@@ -93,41 +128,295 @@ export const ProductVariantSection = ({
},
],
},
]}
/>
</div>
<DataTable
table={table}
columns={columns}
filters={filters}
count={count}
pageSize={PAGE_SIZE}
isLoading={isLoading}
orderBy={[
{ key: "title", label: t("fields.title") },
{ key: "created_at", label: t("fields.createdAt") },
{ key: "updated_at", label: t("fields.updatedAt") },
]}
navigateTo={(row) =>
`/products/${row.original.product_id}/variants/${row.id}`
}
pagination
search
queryObject={raw}
commands={[
{
action: async (selection) => {
navigate(
`stock?${PRODUCT_VARIANT_IDS_KEY}=${Object.keys(selection).join(
","
)}`
)
},
label: t("inventory.stock.action"),
shortcut: "i",
},
]}
],
}}
commands={commands}
/>
</Container>
)
}
const columnHelper =
createDataTableColumnHelper<HttpTypes.AdminProductVariant>()
const useColumns = (product: HttpTypes.AdminProduct) => {
const { t } = useTranslation()
const navigate = useNavigate()
const { mutateAsync } = useDeleteVariantLazy(product.id)
const prompt = usePrompt()
const handleDelete = useCallback(
async (id: string, title: string) => {
const res = await prompt({
title: t("general.areYouSure"),
description: t("products.deleteVariantWarning", {
title,
}),
confirmText: t("actions.delete"),
cancelText: t("actions.cancel"),
})
if (!res) {
return
}
await mutateAsync({ variantId: id })
},
[mutateAsync, prompt, t]
)
const optionColumns = useMemo(() => {
if (!product?.options) {
return []
}
return product.options.map((option) => {
return columnHelper.display({
id: option.id,
header: option.title,
cell: ({ row }) => {
const variantOpt = row.original.options?.find(
(opt) => opt.option_id === option.id
)
if (!variantOpt) {
return <span className="text-ui-fg-muted">-</span>
}
return (
<div className="flex items-center">
<Tooltip content={variantOpt.value}>
<Badge
size="2xsmall"
title={variantOpt.value}
className="inline-flex min-w-[20px] max-w-[140px] items-center justify-center overflow-hidden truncate"
>
{variantOpt.value}
</Badge>
</Tooltip>
</div>
)
},
})
})
}, [product])
const getActions = useCallback(
(ctx: CellContext<HttpTypes.AdminProductVariant, unknown>) => {
const variant = ctx.row.original as HttpTypes.AdminProductVariant & {
inventory_items: { inventory: HttpTypes.AdminInventoryItem }[]
}
const mainActions: DataTableAction<HttpTypes.AdminProductVariant>[] = [
{
icon: <PencilSquare />,
label: t("actions.edit"),
onClick: (row) => {
navigate(`edit-variant?variant_id=${row.row.original.id}`)
},
},
]
const secondaryActions: DataTableAction<HttpTypes.AdminProductVariant>[] =
[
{
icon: <Trash />,
label: t("actions.delete"),
onClick: () => handleDelete(variant.id, variant.title!),
},
]
const inventoryItemsCount = variant.inventory_items?.length || 0
switch (inventoryItemsCount) {
case 0:
break
case 1: {
const inventoryItemLink = `/inventory/${
variant.inventory_items![0].inventory.id
}`
mainActions.push({
label: t("products.variant.inventory.actions.inventoryItems"),
onClick: () => {
navigate(inventoryItemLink)
},
icon: <Buildings />,
})
break
}
default: {
const ids = variant.inventory_items?.map((i) => i.inventory?.id)
if (!ids || ids.length === 0) {
break
}
const inventoryKitLink = `/inventory?${new URLSearchParams({
id: ids.join(","),
}).toString()}`
mainActions.push({
label: t("products.variant.inventory.actions.inventoryKit"),
onClick: () => {
navigate(inventoryKitLink)
},
icon: <Component />,
})
}
}
return [mainActions, secondaryActions]
},
[handleDelete, navigate, t]
)
const getInventory = useCallback(
(variant: HttpTypes.AdminProductVariant) => {
const castVariant = variant as HttpTypes.AdminProductVariant & {
inventory_items: { inventory: HttpTypes.AdminInventoryItem }[]
}
const quantity = variant.inventory_quantity
const inventoryItems = castVariant.inventory_items
?.map((i) => i.inventory)
.filter(Boolean) as HttpTypes.AdminInventoryItem[]
const hasInventoryKit = inventoryItems.length > 1
const locations: Record<string, boolean> = {}
inventoryItems.forEach((i) => {
i.location_levels?.forEach((l) => {
locations[l.id] = true
})
})
const locationCount = Object.keys(locations).length
const text = hasInventoryKit
? t("products.variant.tableItemAvailable", {
availableCount: quantity,
})
: t("products.variant.tableItem", {
availableCount: quantity,
locationCount,
count: locationCount,
})
return { text, hasInventoryKit, quantity }
},
[t]
)
return useMemo(() => {
return [
columnHelper.accessor("title", {
header: t("fields.title"),
enableSorting: true,
sortAscLabel: t("filters.sorting.alphabeticallyAsc"),
sortDescLabel: t("filters.sorting.alphabeticallyDesc"),
}),
columnHelper.accessor("sku", {
header: t("fields.sku"),
enableSorting: true,
sortAscLabel: t("filters.sorting.alphabeticallyAsc"),
sortDescLabel: t("filters.sorting.alphabeticallyDesc"),
}),
...optionColumns,
columnHelper.display({
id: "inventory",
header: t("fields.inventory"),
cell: ({ row }) => {
const { text, hasInventoryKit, quantity } = getInventory(row.original)
return (
<div className="flex h-full w-full items-center gap-2 overflow-hidden">
{hasInventoryKit && <Component />}
<span
className={clx("truncate", {
"text-ui-fg-error": !quantity,
})}
title={text}
>
{text}
</span>
</div>
)
},
}),
columnHelper.action({
actions: getActions,
}),
]
}, [t, optionColumns, getActions, getInventory])
}
const filterHelper =
createDataTableFilterHelper<HttpTypes.AdminProductVariant>()
const useFilters = () => {
const { t } = useTranslation()
const { getFullDate } = useDate()
const dateFilterOptions = useDateFilterOptions()
return useMemo(() => {
return [
filterHelper.accessor("allow_backorder", {
type: "radio",
label: t("fields.allowBackorder"),
options: [
{ label: t("filters.radio.yes"), value: "true" },
{ label: t("filters.radio.no"), value: "false" },
],
}),
filterHelper.accessor("manage_inventory", {
type: "radio",
label: t("fields.manageInventory"),
options: [
{ label: t("filters.radio.yes"), value: "true" },
{ label: t("filters.radio.no"), value: "false" },
],
}),
filterHelper.accessor("created_at", {
type: "date",
label: t("fields.createdAt"),
format: "date",
formatDateValue: (date) => getFullDate({ date }),
rangeOptionStartLabel: t("filters.date.starting"),
rangeOptionEndLabel: t("filters.date.ending"),
rangeOptionLabel: t("filters.date.custom"),
options: dateFilterOptions,
}),
filterHelper.accessor("updated_at", {
type: "date",
label: t("fields.updatedAt"),
format: "date",
rangeOptionStartLabel: t("filters.date.starting"),
rangeOptionEndLabel: t("filters.date.ending"),
rangeOptionLabel: t("filters.date.custom"),
formatDateValue: (date) => getFullDate({ date }),
options: dateFilterOptions,
}),
]
}, [t, dateFilterOptions, getFullDate])
}
const commandHelper = createDataTableCommandHelper()
const useCommands = () => {
const { t } = useTranslation()
const navigate = useNavigate()
return [
commandHelper.command({
label: t("inventory.stock.action"),
shortcut: "i",
action: async (selection) => {
navigate(
`stock?${PRODUCT_VARIANT_IDS_KEY}=${Object.keys(selection).join(",")}`
)
},
}),
]
}
@@ -1,280 +0,0 @@
import { Buildings, Component, PencilSquare, Trash } from "@medusajs/icons"
import { HttpTypes, InventoryItemDTO } from "@medusajs/types"
import { Badge, Checkbox, clx, usePrompt } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import {
Action,
ActionMenu,
} from "../../../../../components/common/action-menu"
import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell"
import { useDeleteVariant } from "../../../../../hooks/api/products"
const VariantActions = ({
variant,
product,
}: {
variant: HttpTypes.AdminProductVariant & {
inventory_items: { inventory: InventoryItemDTO }[]
}
product: HttpTypes.AdminProduct
}) => {
const { mutateAsync } = useDeleteVariant(product.id, variant.id)
const { t } = useTranslation()
const prompt = usePrompt()
const inventoryItemsCount = variant.inventory_items?.length || 0
const hasInventoryItem = inventoryItemsCount === 1
const hasInventoryKit = inventoryItemsCount > 1
const handleDelete = async () => {
const res = await prompt({
title: t("general.areYouSure"),
description: t("products.deleteVariantWarning", {
title: variant.title,
}),
confirmText: t("actions.delete"),
cancelText: t("actions.cancel"),
})
if (!res) {
return
}
await mutateAsync()
}
const [inventoryItemLink, inventoryKitLink] = useMemo(() => {
if (!variant.inventory_items?.length) {
return ["", ""]
}
const itemId = variant.inventory_items![0].inventory.id
const itemLink = `/inventory/${itemId}`
const itemIds = variant.inventory_items!.map((i) => i.inventory.id)
const params = { id: itemIds }
const query = new URLSearchParams(params).toString()
const kitLink = `/inventory?${query}`
return [itemLink, kitLink]
}, [variant.inventory_items])
return (
<ActionMenu
groups={[
{
actions: [
{
label: t("actions.edit"),
to: `edit-variant?variant_id=${variant.id}`,
icon: <PencilSquare />,
},
hasInventoryItem
? {
label: t("products.variant.inventory.actions.inventoryItems"),
to: inventoryItemLink,
icon: <Buildings />,
}
: false,
hasInventoryKit
? {
label: t("products.variant.inventory.actions.inventoryKit"),
to: inventoryKitLink,
icon: <Component />,
}
: false,
].filter(Boolean) as Action[],
},
{
actions: [
{
label: t("actions.delete"),
onClick: handleDelete,
icon: <Trash />,
},
],
},
]}
/>
)
}
const columnHelper = createColumnHelper<HttpTypes.AdminProductVariant>()
export const useProductVariantTableColumns = (
product?: HttpTypes.AdminProduct
) => {
const { t } = useTranslation()
const optionColumns = useMemo(() => {
if (!product?.options) {
return []
}
return product.options.map((option) => {
return columnHelper.display({
id: option.id,
header: () => (
<div className="flex h-full w-full items-center">
<span className="truncate">{option.title}</span>
</div>
),
cell: ({ row }) => {
const variantOpt = row.original.options?.find(
(opt) => opt.option_id === option.id
)
if (!variantOpt) {
return <PlaceholderCell />
}
return (
<div className="flex items-center">
<Badge
size="2xsmall"
title={variantOpt.value}
className="inline-flex min-w-[20px] max-w-[140px] items-center justify-center overflow-hidden truncate"
>
{variantOpt.value}
</Badge>
</div>
)
},
})
})
}, [product])
return useMemo(
() => [
columnHelper.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()
}}
/>
)
},
}),
columnHelper.accessor("title", {
header: () => (
<div className="flex h-full w-full items-center">
<span className="truncate">{t("fields.title")}</span>
</div>
),
cell: ({ getValue }) => (
<div className="flex h-full w-full items-center overflow-hidden">
<span className="truncate">{getValue()}</span>
</div>
),
}),
columnHelper.accessor("sku", {
header: () => (
<div className="flex h-full w-full items-center">
<span className="truncate">{t("fields.sku")}</span>
</div>
),
cell: ({ getValue }) => {
const value = getValue()
if (!value) {
return <PlaceholderCell />
}
return (
<div className="flex h-full w-full items-center overflow-hidden">
<span className="truncate">{value}</span>
</div>
)
},
}),
...optionColumns,
columnHelper.accessor("inventory_items", {
header: () => (
<div className="flex h-full w-full items-center">
<span className="truncate">{t("fields.inventory")}</span>
</div>
),
cell: ({ getValue, row }) => {
const variant = row.original
if (!variant.manage_inventory) {
return t("products.variant.inventory.notManaged")
}
const inventory: InventoryItemDTO[] = getValue().map(
(i) => i.inventory
)
const hasInventoryKit = inventory.length > 1
const locations = {}
inventory.forEach((i) => {
i.location_levels.forEach((l) => {
locations[l.id] = true
})
})
const locationCount = Object.keys(locations).length
const text = hasInventoryKit
? t("products.variant.tableItemAvailable", {
availableCount: variant.inventory_quantity,
})
: t("products.variant.tableItem", {
availableCount: variant.inventory_quantity,
locationCount,
count: locationCount,
})
return (
<div className="flex h-full w-full items-center gap-2 overflow-hidden">
{hasInventoryKit && <Component style={{ marginTop: 1 }} />}
<span
className={clx("truncate", {
"text-ui-fg-error": !variant.inventory_quantity,
})}
title={text}
>
{text}
</span>
</div>
)
},
}),
columnHelper.display({
id: "actions",
cell: ({ row, table }) => {
const { product } = table.options.meta as {
product: HttpTypes.AdminProduct
}
return <VariantActions variant={row.original} product={product} />
},
}),
],
[t, optionColumns]
)
}
@@ -1,58 +0,0 @@
import { useTranslation } from "react-i18next"
import { Filter } from "../../../../../components/table/data-table"
export const useProductVariantTableFilters = () => {
const { t } = useTranslation()
let filters: Filter[] = []
const manageInventoryFilter: Filter = {
key: "manage_inventory",
label: t("fields.managedInventory"),
type: "select",
options: [
{
label: t("fields.true"),
value: "true",
},
{
label: t("fields.false"),
value: "false",
},
],
}
const allowBackorderFilter: Filter = {
key: "allow_backorder",
label: t("fields.allowBackorder"),
type: "select",
options: [
{
label: t("fields.true"),
value: "true",
},
{
label: t("fields.false"),
value: "false",
},
],
}
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,
manageInventoryFilter,
allowBackorderFilter,
...dateFilters,
]
return filters
}
@@ -1,51 +0,0 @@
import { HttpTypes } from "@medusajs/types"
import { useQueryParams } from "../../../../../hooks/use-query-params"
export const useProductVariantTableQuery = ({
pageSize,
prefix,
}: {
pageSize: number
prefix?: string
}) => {
const queryObject = useQueryParams(
[
"offset",
"q",
"manage_inventory",
"allow_backorder",
"order",
"created_at",
"updated_at",
],
prefix
)
const {
offset,
manage_inventory,
allow_backorder,
created_at,
updated_at,
q,
order,
} = queryObject
const searchParams: HttpTypes.AdminProductVariantParams = {
limit: pageSize,
offset: offset ? Number(offset) : 0,
manage_inventory: manage_inventory
? manage_inventory === "true"
: undefined,
allow_backorder: allow_backorder ? allow_backorder === "true" : undefined,
order,
created_at: created_at ? JSON.parse(created_at) : undefined,
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
q,
}
return {
searchParams,
raw: queryObject,
}
}
@@ -8,7 +8,7 @@ import { Link, Outlet, useLoaderData, useLocation } from "react-router-dom"
import { HttpTypes } from "@medusajs/types"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import {
useDeleteProduct,
useProducts,
@@ -72,7 +72,7 @@ export const ProductListTable = () => {
</Button>
</div>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -12,7 +12,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useUpdateProduct } from "../../../../../hooks/api/products"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns"
@@ -123,7 +123,7 @@ export const EditSalesChannelsForm = ({
<div className="flex h-full flex-col overflow-hidden">
<RouteFocusModal.Header />
<RouteFocusModal.Body className="flex-1 overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -8,7 +8,7 @@ import { Link, Outlet, useLoaderData, useNavigate } from "react-router-dom"
import { keepPreviousData } from "@tanstack/react-query"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import {
useDeletePromotion,
usePromotions,
@@ -62,7 +62,7 @@ export const PromotionListTable = () => {
</Button>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -15,7 +15,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateRegion } from "../../../../../hooks/api/regions"
import { useDataTable } from "../../../../../hooks/use-data-table"
@@ -142,7 +142,7 @@ export const AddCountriesForm = ({ region }: AddCountriesFormProps) => {
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -27,7 +27,7 @@ import {
useRouteModal,
useStackedModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateRegion } from "../../../../../hooks/api/regions"
import { useDataTable } from "../../../../../hooks/use-data-table"
@@ -359,7 +359,7 @@ export const CreateRegionForm = ({
</StackedFocusModal.Title>
</StackedFocusModal.Header>
<StackedFocusModal.Body className="overflow-hidden">
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -9,7 +9,7 @@ import {
import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useUpdateRegion } from "../../../../../hooks/api/regions"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useCountries } from "../../../common/hooks/use-countries"
@@ -115,7 +115,7 @@ export const RegionCountrySection = ({ region }: RegionCountrySectionProps) => {
]}
/>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -15,7 +15,7 @@ import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useDeleteRegion, useRegions } from "../../../../../hooks/api/regions"
import { useRegionTableColumns } from "../../../../../hooks/table/columns/use-region-table-columns"
import { useRegionTableFilters } from "../../../../../hooks/table/filters/use-region-table-filters"
@@ -76,7 +76,7 @@ export const RegionListTable = () => {
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -1,13 +1,13 @@
import { Button, Container, Heading, Text } from "@medusajs/ui"
import { DataTable } from "../../../../../components/table/data-table"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useReservationItems } from "../../../../../hooks/api/reservations"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useReservationTableColumns } from "./use-reservation-table-columns"
import { useReservationTableFilters } from "./use-reservation-table-filters"
import { useReservationTableQuery } from "./use-reservation-table-query"
import { useTranslation } from "react-i18next"
const PAGE_SIZE = 20
@@ -51,7 +51,7 @@ export const ReservationListTable = () => {
<Link to="create">{t("actions.create")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useReturnReasons } from "../../../../../hooks/api/return-reasons"
import { useReturnReasonTableColumns } from "../../../../../hooks/table/columns"
import { useReturnReasonTableQuery } from "../../../../../hooks/table/query"
@@ -57,7 +57,7 @@ export const ReturnReasonListTable = () => {
<Link to="create">{t("actions.create")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
queryObject={raw}
count={count}
@@ -12,7 +12,7 @@ import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import * as zod from "zod"
import { RouteFocusModal, useRouteModal } from "../../../../components/modals"
import { DataTable } from "../../../../components/table/data-table"
import { _DataTable } from "../../../../components/table/data-table"
import { KeyboundForm } from "../../../../components/utilities/keybound-form"
import { useProducts } from "../../../../hooks/api/products"
import { useSalesChannelAddProducts } from "../../../../hooks/api/sales-channels"
@@ -134,7 +134,7 @@ export const AddProductsToSalesChannelForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex size-full flex-col overflow-y-auto">
<DataTable
<_DataTable
table={table}
count={count}
columns={columns}
@@ -15,7 +15,7 @@ import { Link } from "react-router-dom"
import { HttpTypes, SalesChannelDTO } from "@medusajs/types"
import { keepPreviousData } from "@tanstack/react-query"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useProducts } from "../../../../../hooks/api/products"
import { useSalesChannelRemoveProducts } from "../../../../../hooks/api/sales-channels"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
@@ -118,7 +118,7 @@ export const SalesChannelProductSection = ({
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -17,7 +17,7 @@ import {
ActionGroup,
ActionMenu,
} from "../../../../components/common/action-menu"
import { DataTable } from "../../../../components/table/data-table"
import { _DataTable } from "../../../../components/table/data-table"
import { useStore } from "../../../../hooks/api"
import {
useDeleteSalesChannel,
@@ -89,7 +89,7 @@ export const SalesChannelListTable = () => {
</Button>
</Link>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -3,7 +3,7 @@ import { Link } from "react-router-dom"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useShippingProfiles } from "../../../../../hooks/api/shipping-profiles"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useShippingProfileTableColumns } from "./use-shipping-profile-table-columns"
@@ -55,7 +55,7 @@ export const ShippingProfileListTable = () => {
</Button>
</div>
</div>
<DataTable
<_DataTable
table={table}
pageSize={PAGE_SIZE}
count={count}
@@ -16,7 +16,7 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCurrencies } from "../../../../../hooks/api/currencies"
import { pricePreferencesQueryKeys } from "../../../../../hooks/api/price-preferences"
@@ -186,7 +186,7 @@ export const AddCurrenciesForm = ({
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-1 flex-col overflow-hidden">
<DataTable
<_DataTable
table={table}
pageSize={PAGE_SIZE}
count={count}
@@ -14,7 +14,7 @@ import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../../components/common/action-menu"
import { DataTable } from "../../../../../../components/table/data-table"
import { _DataTable } from "../../../../../../components/table/data-table"
import { StatusCell } from "../../../../../../components/table/table-cells/common/status-cell"
import { useCurrencies } from "../../../../../../hooks/api/currencies"
import { usePricePreferences } from "../../../../../../hooks/api/price-preferences"
@@ -164,7 +164,7 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => {
]}
/>
</div>
<DataTable
<_DataTable
orderBy={[
{ key: "name", label: t("fields.name") },
{ key: "code", label: t("fields.code") },
@@ -14,7 +14,7 @@ import {
StackedDrawer,
StackedFocusModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import {
useCollections,
useCustomerGroups,
@@ -201,7 +201,7 @@ const CustomerGroupTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -337,7 +337,7 @@ const ProductTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -473,7 +473,7 @@ const ProductCollectionTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -609,7 +609,7 @@ const ProductTypeTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -745,7 +745,7 @@ const ProductTagTable = ({
}
return (
<DataTable
<_DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
@@ -22,7 +22,7 @@ import * as zod from "zod"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { Form } from "../../../../../components/common/form"
import { RouteFocusModal } from "../../../../../components/modals/index.ts"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx"
import {
useCreateInvite,
@@ -159,7 +159,7 @@ export const InviteUserForm = () => {
<div className="flex flex-col gap-y-4">
<Heading level="h2">{t("users.pendingInvites")}</Heading>
<Container className="overflow-hidden p-0">
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -2,7 +2,7 @@ import { Button, Container, Heading } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useUsers } from "../../../../../hooks/api/users"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useUserTableColumns } from "./use-user-table-columns"
@@ -49,7 +49,7 @@ export const UserListTable = () => {
<Link to="invite">{t("users.invite")}</Link>
</Button>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}
@@ -1,7 +1,7 @@
import { Container, Heading, Text } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { DataTable } from "../../../../../components/table/data-table"
import { _DataTable } from "../../../../../components/table/data-table"
import { useWorkflowExecutions } from "../../../../../hooks/api/workflow-executions"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useWorkflowExecutionTableColumns } from "./use-workflow-execution-table-columns"
@@ -50,7 +50,7 @@ export const WorkflowExecutionListTable = () => {
</Text>
</div>
</div>
<DataTable
<_DataTable
table={table}
columns={columns}
count={count}