feat(dashboard,types,js-sdk): Cleanup collection domain (#7502)
**What** - Adds missing collection HttpTypes - Adds missing sdk functions - Adds usage of sdk to collection domain.
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
"@ariakit/react": "^0.4.1",
|
"@ariakit/react": "^0.4.1",
|
||||||
"@dnd-kit/core": "^6.1.0",
|
"@dnd-kit/core": "^6.1.0",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
"@hookform/resolvers": "3.3.2",
|
"@hookform/resolvers": "3.4.2",
|
||||||
"@medusajs/icons": "1.2.1",
|
"@medusajs/icons": "1.2.1",
|
||||||
"@medusajs/js-sdk": "0.0.1",
|
"@medusajs/js-sdk": "0.0.1",
|
||||||
"@medusajs/ui": "3.0.0",
|
"@medusajs/ui": "3.0.0",
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
"react-jwt": "^1.2.0",
|
"react-jwt": "^1.2.0",
|
||||||
"react-resizable-panels": "^2.0.16",
|
"react-resizable-panels": "^2.0.16",
|
||||||
"react-router-dom": "6.20.1",
|
"react-router-dom": "6.20.1",
|
||||||
"zod": "^3.22.4"
|
"zod": "3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@medusajs/admin-shared": "^0.0.1",
|
"@medusajs/admin-shared": "^0.0.1",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
|
import { FindParams, HttpTypes, PaginatedResponse } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
QueryKey,
|
QueryKey,
|
||||||
UseMutationOptions,
|
UseMutationOptions,
|
||||||
@@ -5,19 +7,10 @@ import {
|
|||||||
useMutation,
|
useMutation,
|
||||||
useQuery,
|
useQuery,
|
||||||
} from "@tanstack/react-query"
|
} from "@tanstack/react-query"
|
||||||
import { client } from "../../lib/client"
|
import { sdk } from "../../lib/client"
|
||||||
import { queryClient } from "../../lib/query-client"
|
import { queryClient } from "../../lib/query-client"
|
||||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||||
import {
|
import { productsQueryKeys } from "./products"
|
||||||
CreateProductCollectionReq,
|
|
||||||
UpdateProductCollectionProductsReq,
|
|
||||||
UpdateProductCollectionReq,
|
|
||||||
} from "../../types/api-payloads"
|
|
||||||
import {
|
|
||||||
ProductCollectionDeleteRes,
|
|
||||||
ProductCollectionListRes,
|
|
||||||
ProductCollectionRes,
|
|
||||||
} from "../../types/api-responses"
|
|
||||||
|
|
||||||
const COLLECTION_QUERY_KEY = "collections" as const
|
const COLLECTION_QUERY_KEY = "collections" as const
|
||||||
export const collectionsQueryKeys = queryKeysFactory(COLLECTION_QUERY_KEY)
|
export const collectionsQueryKeys = queryKeysFactory(COLLECTION_QUERY_KEY)
|
||||||
@@ -26,9 +19,9 @@ export const useCollection = (
|
|||||||
id: string,
|
id: string,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
ProductCollectionRes,
|
{ collection: HttpTypes.AdminCollection },
|
||||||
Error,
|
FetchError,
|
||||||
ProductCollectionRes,
|
{ collection: HttpTypes.AdminCollection },
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
"queryFn" | "queryKey"
|
"queryFn" | "queryKey"
|
||||||
@@ -36,7 +29,7 @@ export const useCollection = (
|
|||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryKey: collectionsQueryKeys.detail(id),
|
queryKey: collectionsQueryKeys.detail(id),
|
||||||
queryFn: async () => client.collections.retrieve(id),
|
queryFn: async () => sdk.admin.collection.retrieve(id),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -44,12 +37,12 @@ export const useCollection = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useCollections = (
|
export const useCollections = (
|
||||||
query?: Record<string, any>,
|
query?: FindParams & HttpTypes.AdminCollectionFilters,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
ProductCollectionListRes,
|
PaginatedResponse<{ collections: HttpTypes.AdminCollection[] }>,
|
||||||
Error,
|
FetchError,
|
||||||
ProductCollectionListRes,
|
PaginatedResponse<{ collections: HttpTypes.AdminCollection[] }>,
|
||||||
QueryKey
|
QueryKey
|
||||||
>,
|
>,
|
||||||
"queryFn" | "queryKey"
|
"queryFn" | "queryKey"
|
||||||
@@ -57,7 +50,7 @@ export const useCollections = (
|
|||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryKey: collectionsQueryKeys.list(query),
|
queryKey: collectionsQueryKeys.list(query),
|
||||||
queryFn: async () => client.collections.list(query),
|
queryFn: async () => sdk.admin.collection.list(query),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -67,14 +60,13 @@ export const useCollections = (
|
|||||||
export const useUpdateCollection = (
|
export const useUpdateCollection = (
|
||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
ProductCollectionRes,
|
{ collection: HttpTypes.AdminCollection },
|
||||||
Error,
|
FetchError,
|
||||||
UpdateProductCollectionReq
|
HttpTypes.AdminUpdateCollection
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (payload: UpdateProductCollectionReq) =>
|
mutationFn: (payload) => sdk.admin.collection.update(id, payload),
|
||||||
client.collections.update(id, payload),
|
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
@@ -90,19 +82,24 @@ export const useUpdateCollection = (
|
|||||||
export const useUpdateCollectionProducts = (
|
export const useUpdateCollectionProducts = (
|
||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
ProductCollectionRes,
|
{ collection: HttpTypes.AdminCollection },
|
||||||
Error,
|
FetchError,
|
||||||
UpdateProductCollectionProductsReq
|
HttpTypes.AdminUpdateCollectionProducts
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (payload: UpdateProductCollectionProductsReq) =>
|
mutationFn: (payload) => sdk.admin.collection.updateProducts(id, payload),
|
||||||
client.collections.updateProducts(id, payload),
|
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: collectionsQueryKeys.detail(id),
|
queryKey: collectionsQueryKeys.detail(id),
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* Invalidate products list query to ensure that the products collections are updated.
|
||||||
|
*/
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: productsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
@@ -112,13 +109,13 @@ export const useUpdateCollectionProducts = (
|
|||||||
|
|
||||||
export const useCreateCollection = (
|
export const useCreateCollection = (
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
ProductCollectionRes,
|
{ collection: HttpTypes.AdminCollection },
|
||||||
Error,
|
FetchError,
|
||||||
CreateProductCollectionReq
|
HttpTypes.AdminCreateCollection
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (payload) => client.collections.create(payload),
|
mutationFn: (payload) => sdk.admin.collection.create(payload),
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
||||||
|
|
||||||
@@ -130,10 +127,14 @@ export const useCreateCollection = (
|
|||||||
|
|
||||||
export const useDeleteCollection = (
|
export const useDeleteCollection = (
|
||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<ProductCollectionDeleteRes, Error, void>
|
options?: UseMutationOptions<
|
||||||
|
HttpTypes.DeleteResponse<"collection">,
|
||||||
|
FetchError,
|
||||||
|
void
|
||||||
|
>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: () => client.collections.delete(id),
|
mutationFn: () => sdk.admin.collection.delete(id),
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() })
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import Medusa from "@medusajs/js-sdk"
|
|||||||
import { apiKeys } from "./api-keys"
|
import { apiKeys } from "./api-keys"
|
||||||
import { campaigns } from "./campaigns"
|
import { campaigns } from "./campaigns"
|
||||||
import { categories } from "./categories"
|
import { categories } from "./categories"
|
||||||
import { collections } from "./collections"
|
|
||||||
import { currencies } from "./currencies"
|
import { currencies } from "./currencies"
|
||||||
import { customerGroups } from "./customer-groups"
|
import { customerGroups } from "./customer-groups"
|
||||||
import { fulfillmentProviders } from "./fulfillment-providers"
|
import { fulfillmentProviders } from "./fulfillment-providers"
|
||||||
@@ -34,7 +33,6 @@ export const client = {
|
|||||||
categories: categories,
|
categories: categories,
|
||||||
customerGroups: customerGroups,
|
customerGroups: customerGroups,
|
||||||
currencies: currencies,
|
currencies: currencies,
|
||||||
collections: collections,
|
|
||||||
promotions: promotions,
|
promotions: promotions,
|
||||||
payments: payments,
|
payments: payments,
|
||||||
stores: stores,
|
stores: stores,
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import {
|
|
||||||
CreateProductCollectionReq,
|
|
||||||
UpdateProductCollectionProductsReq,
|
|
||||||
UpdateProductCollectionReq,
|
|
||||||
} from "../../types/api-payloads"
|
|
||||||
import {
|
|
||||||
ProductCollectionDeleteRes,
|
|
||||||
ProductCollectionListRes,
|
|
||||||
ProductCollectionRes,
|
|
||||||
} from "../../types/api-responses"
|
|
||||||
import { deleteRequest, getRequest, postRequest } from "./common"
|
|
||||||
|
|
||||||
async function listProductCollections(query?: Record<string, any>) {
|
|
||||||
return getRequest<ProductCollectionListRes>(`/admin/collections`, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function retrieveProductCollection(
|
|
||||||
id: string,
|
|
||||||
query?: Record<string, any>
|
|
||||||
) {
|
|
||||||
return getRequest<ProductCollectionRes>(`/admin/collections/${id}`, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateProductCollection(
|
|
||||||
id: string,
|
|
||||||
payload: UpdateProductCollectionReq
|
|
||||||
) {
|
|
||||||
return postRequest<ProductCollectionRes>(`/admin/collections/${id}`, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createProductCollection(payload: CreateProductCollectionReq) {
|
|
||||||
return postRequest<ProductCollectionRes>(`/admin/collections`, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateProductCollectionProducts(
|
|
||||||
id: string,
|
|
||||||
payload: UpdateProductCollectionProductsReq
|
|
||||||
) {
|
|
||||||
return postRequest<ProductCollectionRes>(
|
|
||||||
`/admin/collections/${id}/products`,
|
|
||||||
payload
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteProductCollection(id: string) {
|
|
||||||
return deleteRequest<ProductCollectionDeleteRes>(`/admin/collections/${id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const collections = {
|
|
||||||
list: listProductCollections,
|
|
||||||
retrieve: retrieveProductCollection,
|
|
||||||
update: updateProductCollection,
|
|
||||||
updateProducts: updateProductCollectionProducts,
|
|
||||||
create: createProductCollection,
|
|
||||||
delete: deleteProductCollection,
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
import {
|
|
||||||
ProductCollectionListRes,
|
|
||||||
ProductCollectionRes,
|
|
||||||
} from "../../types/api-responses"
|
|
||||||
import { getRequest } from "./common"
|
import { getRequest } from "./common"
|
||||||
|
|
||||||
async function listProductTags(query?: Record<string, any>) {
|
async function listProductTags(query?: Record<string, any>) {
|
||||||
return getRequest<ProductCollectionListRes>(`/admin/tags`, query)
|
return getRequest<any>(`/admin/tags`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function retrieveProductTag(id: string, query?: Record<string, any>) {
|
async function retrieveProductTag(id: string, query?: Record<string, any>) {
|
||||||
return getRequest<ProductCollectionRes>(`/admin/tags/${id}`, query)
|
return getRequest<any>(`/admin/tags/${id}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tags = {
|
export const tags = {
|
||||||
|
|||||||
+84
-191
@@ -1,59 +1,39 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { ProductCollectionDTO, ProductDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import {
|
import { Button, Checkbox, Hint, Tooltip, toast } from "@medusajs/ui"
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Hint,
|
|
||||||
Table,
|
|
||||||
Tooltip,
|
|
||||||
clx,
|
|
||||||
toast,
|
|
||||||
} from "@medusajs/ui"
|
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
import {
|
import {
|
||||||
PaginationState,
|
OnChangeFn,
|
||||||
RowSelectionState,
|
RowSelectionState,
|
||||||
createColumnHelper,
|
createColumnHelper,
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table"
|
} from "@tanstack/react-table"
|
||||||
import { Fragment, useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
import {
|
|
||||||
NoRecords,
|
|
||||||
NoResults,
|
|
||||||
} from "../../../../../components/common/empty-table-content/index.ts"
|
|
||||||
import { OrderBy } from "../../../../../components/filtering/order-by/index.ts"
|
|
||||||
import { Query } from "../../../../../components/filtering/query/index.ts"
|
|
||||||
import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination/index.ts"
|
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal/index.ts"
|
} from "../../../../../components/route-modal/index.ts"
|
||||||
|
import { DataTable } from "../../../../../components/table/data-table/data-table.tsx"
|
||||||
import { useUpdateCollectionProducts } from "../../../../../hooks/api/collections.tsx"
|
import { useUpdateCollectionProducts } from "../../../../../hooks/api/collections.tsx"
|
||||||
import {
|
import { useProducts } from "../../../../../hooks/api/products.tsx"
|
||||||
productsQueryKeys,
|
|
||||||
useProducts,
|
|
||||||
} from "../../../../../hooks/api/products.tsx"
|
|
||||||
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns.tsx"
|
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns.tsx"
|
||||||
import { useHandleTableScroll } from "../../../../../hooks/use-handle-table-scroll.tsx"
|
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters.tsx"
|
||||||
import { useQueryParams } from "../../../../../hooks/use-query-params.tsx"
|
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query.tsx"
|
||||||
import { queryClient } from "../../../../../lib/query-client.ts"
|
import { useDataTable } from "../../../../../hooks/use-data-table.tsx"
|
||||||
|
import { ExtendedProductDTO } from "../../../../../types/api-responses.ts"
|
||||||
// Re-add when supported on the backend
|
|
||||||
|
|
||||||
type AddProductsToCollectionFormProps = {
|
type AddProductsToCollectionFormProps = {
|
||||||
collection: ProductCollectionDTO
|
collection: HttpTypes.AdminCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddProductsToCollectionSchema = zod.object({
|
const AddProductsToCollectionSchema = zod.object({
|
||||||
product_ids: zod.array(zod.string()).min(1),
|
add: zod.array(zod.string()).min(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
const PAGE_SIZE = 50
|
const PAGE_SIZE = 50
|
||||||
|
const PREFIX = "p"
|
||||||
|
|
||||||
export const AddProductsToCollectionForm = ({
|
export const AddProductsToCollectionForm = ({
|
||||||
collection,
|
collection,
|
||||||
@@ -63,7 +43,7 @@ export const AddProductsToCollectionForm = ({
|
|||||||
|
|
||||||
const form = useForm<zod.infer<typeof AddProductsToCollectionSchema>>({
|
const form = useForm<zod.infer<typeof AddProductsToCollectionSchema>>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
product_ids: [],
|
add: [],
|
||||||
},
|
},
|
||||||
resolver: zodResolver(AddProductsToCollectionSchema),
|
resolver: zodResolver(AddProductsToCollectionSchema),
|
||||||
})
|
})
|
||||||
@@ -71,27 +51,32 @@ export const AddProductsToCollectionForm = ({
|
|||||||
const { setValue } = form
|
const { setValue } = form
|
||||||
|
|
||||||
const { mutateAsync, isPending: isMutating } = useUpdateCollectionProducts(
|
const { mutateAsync, isPending: isMutating } = useUpdateCollectionProducts(
|
||||||
collection.id
|
collection.id!
|
||||||
)
|
|
||||||
|
|
||||||
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: PAGE_SIZE,
|
|
||||||
})
|
|
||||||
|
|
||||||
const pagination = useMemo(
|
|
||||||
() => ({
|
|
||||||
pageIndex,
|
|
||||||
pageSize,
|
|
||||||
}),
|
|
||||||
[pageIndex, pageSize]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
|
|
||||||
|
const updater: OnChangeFn<RowSelectionState> = (newSelection) => {
|
||||||
|
const update =
|
||||||
|
typeof newSelection === "function"
|
||||||
|
? newSelection(rowSelection)
|
||||||
|
: newSelection
|
||||||
|
|
||||||
|
setValue(
|
||||||
|
"add",
|
||||||
|
Object.keys(update).filter((k) => update[k]),
|
||||||
|
{
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
setRowSelection(update)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(
|
setValue(
|
||||||
"product_ids",
|
"add",
|
||||||
Object.keys(rowSelection).filter((k) => rowSelection[k]),
|
Object.keys(rowSelection).filter((k) => rowSelection[k]),
|
||||||
{
|
{
|
||||||
shouldDirty: true,
|
shouldDirty: true,
|
||||||
@@ -100,12 +85,15 @@ export const AddProductsToCollectionForm = ({
|
|||||||
)
|
)
|
||||||
}, [rowSelection, setValue])
|
}, [rowSelection, setValue])
|
||||||
|
|
||||||
const params = useQueryParams(["q", "order"])
|
const { searchParams, raw } = useProductTableQuery({
|
||||||
|
prefix: PREFIX,
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
})
|
||||||
|
|
||||||
const { products, count, isLoading, isError, error } = useProducts(
|
const { products, count, isLoading, isError, error } = useProducts(
|
||||||
{
|
{
|
||||||
fields: "*variants,*sales_channels",
|
fields: "*variants,*sales_channels",
|
||||||
...params,
|
...searchParams,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
@@ -113,54 +101,45 @@ export const AddProductsToCollectionForm = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const columns = useColumns()
|
const columns = useColumns()
|
||||||
|
const filters = useProductTableFilters(["collections"])
|
||||||
|
|
||||||
const table = useReactTable({
|
const { table } = useDataTable({
|
||||||
data: (products ?? []) as ProductDTO[],
|
data: (products ?? []) as ExtendedProductDTO[],
|
||||||
columns,
|
columns,
|
||||||
pageCount: Math.ceil((count ?? 0) / PAGE_SIZE),
|
count,
|
||||||
state: {
|
pageSize: PAGE_SIZE,
|
||||||
pagination,
|
prefix: PREFIX,
|
||||||
rowSelection,
|
|
||||||
},
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
manualPagination: true,
|
|
||||||
getRowId: (row) => row.id,
|
getRowId: (row) => row.id,
|
||||||
enableRowSelection(row) {
|
enableRowSelection: true,
|
||||||
return row.original.collection_id !== collection.id
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
},
|
},
|
||||||
|
enablePagination: true,
|
||||||
meta: {
|
meta: {
|
||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
try {
|
await mutateAsync(
|
||||||
await mutateAsync({
|
{
|
||||||
add: values.product_ids,
|
add: values.add,
|
||||||
})
|
},
|
||||||
/**
|
{
|
||||||
* Invalidate the products list query to refetch products and
|
onSuccess: () => {
|
||||||
* determine if they are added to the collection or not.
|
handleSuccess()
|
||||||
*/
|
},
|
||||||
queryClient.invalidateQueries(productsQueryKeys.lists())
|
onError: (e) => {
|
||||||
handleSuccess()
|
toast.error(t("general.error"), {
|
||||||
} catch (e) {
|
description: e.message,
|
||||||
toast.error(t("general.error"), {
|
dismissLabel: t("actions.close"),
|
||||||
description: e.message,
|
})
|
||||||
dismissLabel: t("actions.close"),
|
},
|
||||||
})
|
}
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const { handleScroll, isScrolled, tableContainerRef } = useHandleTableScroll()
|
|
||||||
|
|
||||||
const noRecords =
|
|
||||||
!isLoading &&
|
|
||||||
products?.length === 0 &&
|
|
||||||
!Object.values(params).filter((v) => v).length
|
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -173,10 +152,8 @@ export const AddProductsToCollectionForm = ({
|
|||||||
>
|
>
|
||||||
<RouteFocusModal.Header>
|
<RouteFocusModal.Header>
|
||||||
<div className="flex items-center justify-end gap-x-2">
|
<div className="flex items-center justify-end gap-x-2">
|
||||||
{form.formState.errors.product_ids && (
|
{form.formState.errors.add && (
|
||||||
<Hint variant="error">
|
<Hint variant="error">{form.formState.errors.add.message}</Hint>
|
||||||
{form.formState.errors.product_ids.message}
|
|
||||||
</Hint>
|
|
||||||
)}
|
)}
|
||||||
<RouteFocusModal.Close asChild>
|
<RouteFocusModal.Close asChild>
|
||||||
<Button size="small" variant="secondary">
|
<Button size="small" variant="secondary">
|
||||||
@@ -188,112 +165,28 @@ export const AddProductsToCollectionForm = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</RouteFocusModal.Header>
|
</RouteFocusModal.Header>
|
||||||
<RouteFocusModal.Body className="flex h-full w-full flex-col items-center divide-y overflow-y-auto">
|
<RouteFocusModal.Body className="size-full overflow-hidden">
|
||||||
{!noRecords && (
|
<DataTable
|
||||||
<div className="flex w-full items-center justify-between px-6 py-4">
|
table={table}
|
||||||
<div></div>
|
columns={columns}
|
||||||
<div className="flex items-center gap-x-2">
|
pageSize={PAGE_SIZE}
|
||||||
<Query />
|
count={count}
|
||||||
<OrderBy keys={["title"]} />
|
queryObject={raw}
|
||||||
</div>
|
filters={filters}
|
||||||
</div>
|
orderBy={["title", "created_at", "updated_at"]}
|
||||||
)}
|
prefix={PREFIX}
|
||||||
{!noRecords ? (
|
isLoading={isLoading}
|
||||||
<Fragment>
|
layout="fill"
|
||||||
<div
|
pagination
|
||||||
ref={tableContainerRef}
|
search
|
||||||
className="w-full flex-1 overflow-y-auto"
|
/>
|
||||||
onScroll={handleScroll}
|
|
||||||
>
|
|
||||||
{!isLoading && !products?.length ? (
|
|
||||||
<div className="flex h-full flex-1 items-center justify-center">
|
|
||||||
<NoResults />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Table>
|
|
||||||
<Table.Header
|
|
||||||
className={clx(
|
|
||||||
"bg-ui-bg-base transition-fg sticky inset-x-0 top-0 z-10 border-t-0",
|
|
||||||
{
|
|
||||||
"shadow-elevation-card-hover": isScrolled,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => {
|
|
||||||
return (
|
|
||||||
<Table.Row
|
|
||||||
key={headerGroup.id}
|
|
||||||
className="[&_th:first-of-type]:w-[1%] [&_th:first-of-type]:whitespace-nowrap [&_th]:w-1/5"
|
|
||||||
>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<Table.HeaderCell key={header.id}>
|
|
||||||
{flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</Table.HeaderCell>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Table.Row>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Table.Header>
|
|
||||||
<Table.Body className="border-b-0">
|
|
||||||
{table.getRowModel().rows.map((row) => (
|
|
||||||
<Table.Row
|
|
||||||
key={row.id}
|
|
||||||
className={clx(
|
|
||||||
"transition-fg",
|
|
||||||
{
|
|
||||||
"bg-ui-bg-highlight hover:bg-ui-bg-highlight-hover":
|
|
||||||
row.getIsSelected(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bg-ui-bg-disabled hover:bg-ui-bg-disabled":
|
|
||||||
row.original.collection_id === collection.id,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<Table.Cell key={cell.id}>
|
|
||||||
{flexRender(
|
|
||||||
cell.column.columnDef.cell,
|
|
||||||
cell.getContext()
|
|
||||||
)}
|
|
||||||
</Table.Cell>
|
|
||||||
))}
|
|
||||||
</Table.Row>
|
|
||||||
))}
|
|
||||||
</Table.Body>
|
|
||||||
</Table>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="w-full border-t">
|
|
||||||
<LocalizedTablePagination
|
|
||||||
canNextPage={table.getCanNextPage()}
|
|
||||||
canPreviousPage={table.getCanPreviousPage()}
|
|
||||||
nextPage={table.nextPage}
|
|
||||||
previousPage={table.previousPage}
|
|
||||||
count={count ?? 0}
|
|
||||||
pageIndex={pageIndex}
|
|
||||||
pageCount={table.getPageCount()}
|
|
||||||
pageSize={PAGE_SIZE}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-1 items-center justify-center">
|
|
||||||
<NoRecords />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</RouteFocusModal.Body>
|
</RouteFocusModal.Body>
|
||||||
</form>
|
</form>
|
||||||
</RouteFocusModal.Form>
|
</RouteFocusModal.Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<ProductDTO>()
|
const columnHelper = createColumnHelper<ExtendedProductDTO>()
|
||||||
|
|
||||||
const useColumns = () => {
|
const useColumns = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ export const CollectionDetail = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-3">
|
||||||
{before.widgets.map((w, i) => {
|
{before.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
|
|||||||
+5
-3
@@ -1,12 +1,12 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { ProductCollectionDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { Container, Heading, Text, usePrompt } from "@medusajs/ui"
|
import { Container, Heading, Text, usePrompt } from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
import { useDeleteCollection } from "../../../../../hooks/api/collections"
|
import { useDeleteCollection } from "../../../../../hooks/api/collections"
|
||||||
|
|
||||||
type CollectionGeneralSectionProps = {
|
type CollectionGeneralSectionProps = {
|
||||||
collection: ProductCollectionDTO
|
collection: HttpTypes.AdminCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CollectionGeneralSection = ({
|
export const CollectionGeneralSection = ({
|
||||||
@@ -15,7 +15,7 @@ export const CollectionGeneralSection = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
|
|
||||||
const { mutateAsync } = useDeleteCollection(collection.id)
|
const { mutateAsync } = useDeleteCollection(collection.id!)
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
const res = await prompt({
|
const res = await prompt({
|
||||||
@@ -44,6 +44,7 @@ export const CollectionGeneralSection = ({
|
|||||||
icon: <PencilSquare />,
|
icon: <PencilSquare />,
|
||||||
label: t("actions.edit"),
|
label: t("actions.edit"),
|
||||||
to: `/collections/${collection.id}/edit`,
|
to: `/collections/${collection.id}/edit`,
|
||||||
|
disabled: !collection.id,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -53,6 +54,7 @@ export const CollectionGeneralSection = ({
|
|||||||
icon: <Trash />,
|
icon: <Trash />,
|
||||||
label: t("actions.delete"),
|
label: t("actions.delete"),
|
||||||
onClick: handleDelete,
|
onClick: handleDelete,
|
||||||
|
disabled: !collection.id,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
+29
-30
@@ -1,5 +1,5 @@
|
|||||||
import { PencilSquare, Plus, Trash } from "@medusajs/icons"
|
import { PencilSquare, Plus, Trash } from "@medusajs/icons"
|
||||||
import { ProductCollectionDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { Checkbox, Container, Heading, toast, usePrompt } from "@medusajs/ui"
|
import { Checkbox, Container, Heading, toast, usePrompt } from "@medusajs/ui"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
@@ -8,19 +8,15 @@ import { useTranslation } from "react-i18next"
|
|||||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
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 { useUpdateCollectionProducts } from "../../../../../hooks/api/collections"
|
||||||
import {
|
import { useProducts } from "../../../../../hooks/api/products"
|
||||||
productsQueryKeys,
|
|
||||||
useProducts,
|
|
||||||
} from "../../../../../hooks/api/products"
|
|
||||||
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
|
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
|
||||||
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
|
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
|
||||||
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
|
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
import { queryClient } from "../../../../../lib/query-client"
|
|
||||||
import { ExtendedProductDTO } from "../../../../../types/api-responses"
|
import { ExtendedProductDTO } from "../../../../../types/api-responses"
|
||||||
|
|
||||||
type CollectionProductSectionProps = {
|
type CollectionProductSectionProps = {
|
||||||
collection: ProductCollectionDTO
|
collection: HttpTypes.AdminCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAGE_SIZE = 10
|
const PAGE_SIZE = 10
|
||||||
@@ -60,7 +56,7 @@ export const CollectionProductSection = ({
|
|||||||
|
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
|
|
||||||
const { mutateAsync } = useUpdateCollectionProducts(collection.id)
|
const { mutateAsync } = useUpdateCollectionProducts(collection.id!)
|
||||||
|
|
||||||
const handleRemove = async (selection: Record<string, boolean>) => {
|
const handleRemove = async (selection: Record<string, boolean>) => {
|
||||||
const ids = Object.keys(selection)
|
const ids = Object.keys(selection)
|
||||||
@@ -78,18 +74,19 @@ export const CollectionProductSection = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
await mutateAsync(
|
||||||
await mutateAsync({
|
{
|
||||||
remove: ids,
|
remove: ids,
|
||||||
})
|
},
|
||||||
|
{
|
||||||
queryClient.invalidateQueries(productsQueryKeys.lists())
|
onError: (e) => {
|
||||||
} catch (e) {
|
toast.error(t("general.error"), {
|
||||||
toast.error(t("general.error"), {
|
description: e.message,
|
||||||
description: e.message,
|
dismissLabel: t("actions.close"),
|
||||||
dismissLabel: t("actions.close"),
|
})
|
||||||
})
|
},
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
@@ -162,18 +159,20 @@ const ProductActions = ({
|
|||||||
if (!res) {
|
if (!res) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
await mutateAsync({
|
|
||||||
remove: [product.id],
|
|
||||||
})
|
|
||||||
|
|
||||||
queryClient.invalidateQueries(productsQueryKeys.lists())
|
await mutateAsync(
|
||||||
} catch (e) {
|
{
|
||||||
toast.error(t("general.error"), {
|
remove: [product.id],
|
||||||
description: e.message,
|
},
|
||||||
dismissLabel: t("actions.close"),
|
{
|
||||||
})
|
onError: (e) => {
|
||||||
}
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { LoaderFunctionArgs } from "react-router-dom"
|
import { LoaderFunctionArgs } from "react-router-dom"
|
||||||
|
|
||||||
import { collectionsQueryKeys } from "../../../hooks/api/collections"
|
import { collectionsQueryKeys } from "../../../hooks/api/collections"
|
||||||
import { client } from "../../../lib/client"
|
import { sdk } from "../../../lib/client"
|
||||||
import { queryClient } from "../../../lib/query-client"
|
import { queryClient } from "../../../lib/query-client"
|
||||||
import { ProductCollectionRes } from "../../../types/api-responses"
|
|
||||||
|
|
||||||
const collectionDetailQuery = (id: string) => ({
|
const collectionDetailQuery = (id: string) => ({
|
||||||
queryKey: collectionsQueryKeys.detail(id),
|
queryKey: collectionsQueryKeys.detail(id),
|
||||||
queryFn: async () => client.collections.retrieve(id),
|
queryFn: async () => sdk.admin.collection.retrieve(id),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const collectionLoader = async ({ params }: LoaderFunctionArgs) => {
|
export const collectionLoader = async ({ params }: LoaderFunctionArgs) => {
|
||||||
@@ -15,7 +15,8 @@ export const collectionLoader = async ({ params }: LoaderFunctionArgs) => {
|
|||||||
const query = collectionDetailQuery(id!)
|
const query = collectionDetailQuery(id!)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
queryClient.getQueryData<ProductCollectionRes>(query.queryKey) ??
|
queryClient.getQueryData<{ collection: HttpTypes.AdminCollection }>(
|
||||||
(await queryClient.fetchQuery(query))
|
query.queryKey
|
||||||
|
) ?? (await queryClient.fetchQuery(query))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -18,6 +18,7 @@ export const CollectionListTable = () => {
|
|||||||
const { collections, count, isError, error, isLoading } = useCollections(
|
const { collections, count, isError, error, isLoading } = useCollections(
|
||||||
{
|
{
|
||||||
...searchParams,
|
...searchParams,
|
||||||
|
fields: "+products.id",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
@@ -32,7 +33,7 @@ export const CollectionListTable = () => {
|
|||||||
columns,
|
columns,
|
||||||
count,
|
count,
|
||||||
enablePagination: true,
|
enablePagination: true,
|
||||||
getRowId: (row) => row.id,
|
getRowId: (row, index) => row.id ?? `${index}`,
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { usePrompt } from "@medusajs/ui"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
|
import { useDeleteCollection } from "../../../../../hooks/api/collections"
|
||||||
|
|
||||||
|
export const CollectionRowActions = ({
|
||||||
|
collection,
|
||||||
|
}: {
|
||||||
|
collection: HttpTypes.AdminCollection
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const prompt = usePrompt()
|
||||||
|
|
||||||
|
const { mutateAsync } = useDeleteCollection(collection.id!)
|
||||||
|
|
||||||
|
const handleDeleteCollection = async () => {
|
||||||
|
const res = await prompt({
|
||||||
|
title: t("general.areYouSure"),
|
||||||
|
description: t("collections.deleteWarning", {
|
||||||
|
title: collection.title,
|
||||||
|
}),
|
||||||
|
verificationText: collection.title,
|
||||||
|
verificationInstruction: t("general.typeToConfirm"),
|
||||||
|
confirmText: t("actions.delete"),
|
||||||
|
cancelText: t("actions.cancel"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutateAsync()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionMenu
|
||||||
|
groups={[
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: t("actions.edit"),
|
||||||
|
to: `/collections/${collection.id}/edit`,
|
||||||
|
icon: <PencilSquare />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: t("actions.delete"),
|
||||||
|
onClick: handleDeleteCollection,
|
||||||
|
icon: <Trash />,
|
||||||
|
disabled: !collection.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
+5
-62
@@ -1,13 +1,11 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { ProductCollectionDTO } from "@medusajs/types"
|
|
||||||
import { usePrompt } from "@medusajs/ui"
|
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
|
||||||
import { useDeleteCollection } from "../../../../../hooks/api/collections"
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<ProductCollectionDTO>()
|
import { CollectionRowActions } from "./collection-row-actions"
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<HttpTypes.AdminCollection>()
|
||||||
|
|
||||||
export const useCollectionTableColumns = () => {
|
export const useCollectionTableColumns = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -31,64 +29,9 @@ export const useCollectionTableColumns = () => {
|
|||||||
}),
|
}),
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: "actions",
|
id: "actions",
|
||||||
cell: ({ row }) => <CollectionActions collection={row.original} />,
|
cell: ({ row }) => <CollectionRowActions collection={row.original} />,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[t]
|
[t]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const CollectionActions = ({
|
|
||||||
collection,
|
|
||||||
}: {
|
|
||||||
collection: ProductCollectionDTO
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const prompt = usePrompt()
|
|
||||||
|
|
||||||
const { mutateAsync } = useDeleteCollection(collection.id)
|
|
||||||
|
|
||||||
const handleDeleteCollection = async () => {
|
|
||||||
const res = await prompt({
|
|
||||||
title: t("general.areYouSure"),
|
|
||||||
description: t("collections.deleteWarning", {
|
|
||||||
title: collection.title,
|
|
||||||
}),
|
|
||||||
verificationText: collection.title,
|
|
||||||
verificationInstruction: t("general.typeToConfirm"),
|
|
||||||
confirmText: t("actions.delete"),
|
|
||||||
cancelText: t("actions.cancel"),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!res) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await mutateAsync()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ActionMenu
|
|
||||||
groups={[
|
|
||||||
{
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: t("actions.edit"),
|
|
||||||
to: `/collections/${collection.id}/edit`,
|
|
||||||
icon: <PencilSquare />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: t("actions.delete"),
|
|
||||||
onClick: handleDeleteCollection,
|
|
||||||
icon: <Trash />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import { AdminGetCollectionsParams } from "@medusajs/medusa"
|
import { AdminCollectionFilters, FindParams } from "@medusajs/types"
|
||||||
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
||||||
|
|
||||||
type UseCollectionTableQueryProps = {
|
type UseCollectionTableQueryProps = {
|
||||||
@@ -17,7 +17,7 @@ export const useCollectionTableQuery = ({
|
|||||||
|
|
||||||
const { offset, created_at, updated_at, q, order } = queryObject
|
const { offset, created_at, updated_at, q, order } = queryObject
|
||||||
|
|
||||||
const searchParams: AdminGetCollectionsParams = {
|
const searchParams: FindParams & AdminCollectionFilters = {
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
offset: offset ? Number(offset) : 0,
|
offset: offset ? Number(offset) : 0,
|
||||||
order,
|
order,
|
||||||
|
|||||||
+4
-4
@@ -6,7 +6,7 @@ import { ChipGroup } from "../../../../../../../components/common/chip-group"
|
|||||||
import { Form } from "../../../../../../../components/common/form"
|
import { Form } from "../../../../../../../components/common/form"
|
||||||
import { Combobox } from "../../../../../../../components/inputs/combobox"
|
import { Combobox } from "../../../../../../../components/inputs/combobox"
|
||||||
import { useComboboxData } from "../../../../../../../hooks/use-combobox-data"
|
import { useComboboxData } from "../../../../../../../hooks/use-combobox-data"
|
||||||
import { client } from "../../../../../../../lib/client"
|
import { client, sdk } from "../../../../../../../lib/client"
|
||||||
import { CategoryCombobox } from "../../../../../common/components/category-combobox"
|
import { CategoryCombobox } from "../../../../../common/components/category-combobox"
|
||||||
import { ProductCreateSchemaType } from "../../../../types"
|
import { ProductCreateSchemaType } from "../../../../types"
|
||||||
import { useProductCreateDetailsContext } from "../product-create-organize-context"
|
import { useProductCreateDetailsContext } from "../product-create-organize-context"
|
||||||
@@ -23,11 +23,11 @@ export const ProductCreateOrganizationSection = ({
|
|||||||
|
|
||||||
const collections = useComboboxData({
|
const collections = useComboboxData({
|
||||||
queryKey: ["product_collections"],
|
queryKey: ["product_collections"],
|
||||||
queryFn: client.collections.list,
|
queryFn: sdk.admin.collection.list,
|
||||||
getOptions: (data) =>
|
getOptions: (data) =>
|
||||||
data.collections.map((collection) => ({
|
data.collections.map((collection) => ({
|
||||||
label: collection.title,
|
label: collection.title!,
|
||||||
value: collection.id,
|
value: collection.id!,
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
CreateFulfillmentSetDTO,
|
CreateFulfillmentSetDTO,
|
||||||
CreateInviteDTO,
|
CreateInviteDTO,
|
||||||
CreatePriceListDTO,
|
CreatePriceListDTO,
|
||||||
CreateProductCollectionDTO,
|
|
||||||
CreatePromotionDTO,
|
CreatePromotionDTO,
|
||||||
CreatePromotionRuleDTO,
|
CreatePromotionRuleDTO,
|
||||||
CreateSalesChannelDTO,
|
CreateSalesChannelDTO,
|
||||||
@@ -20,7 +19,6 @@ import {
|
|||||||
UpdateApiKeyDTO,
|
UpdateApiKeyDTO,
|
||||||
UpdateCampaignDTO,
|
UpdateCampaignDTO,
|
||||||
UpdatePriceListDTO,
|
UpdatePriceListDTO,
|
||||||
UpdateProductCollectionDTO,
|
|
||||||
UpdatePromotionDTO,
|
UpdatePromotionDTO,
|
||||||
UpdatePromotionRuleDTO,
|
UpdatePromotionRuleDTO,
|
||||||
UpdateSalesChannelDTO,
|
UpdateSalesChannelDTO,
|
||||||
@@ -71,14 +69,6 @@ export type UpdateShippingOptionReq = UpdateShippingOptionDTO
|
|||||||
// Shipping Profile
|
// Shipping Profile
|
||||||
export type CreateShippingProfileReq = CreateShippingProfileDTO
|
export type CreateShippingProfileReq = CreateShippingProfileDTO
|
||||||
|
|
||||||
// Product Collections
|
|
||||||
export type CreateProductCollectionReq = CreateProductCollectionDTO
|
|
||||||
export type UpdateProductCollectionReq = UpdateProductCollectionDTO
|
|
||||||
export type UpdateProductCollectionProductsReq = {
|
|
||||||
add?: string[]
|
|
||||||
remove?: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Price Lists
|
// Price Lists
|
||||||
export type CreatePriceListReq = CreatePriceListDTO
|
export type CreatePriceListReq = CreatePriceListDTO
|
||||||
export type UpdatePriceListReq = UpdatePriceListDTO
|
export type UpdatePriceListReq = UpdatePriceListDTO
|
||||||
|
|||||||
@@ -172,13 +172,6 @@ export type WorkflowExecutionListRes = {
|
|||||||
workflow_executions: WorkflowExecutionDTO[]
|
workflow_executions: WorkflowExecutionDTO[]
|
||||||
} & ListRes
|
} & ListRes
|
||||||
|
|
||||||
// Product Collections
|
|
||||||
export type ProductCollectionRes = { collection: ProductCollectionDTO }
|
|
||||||
export type ProductCollectionListRes = {
|
|
||||||
collections: ProductCollectionDTO[]
|
|
||||||
} & ListRes
|
|
||||||
export type ProductCollectionDeleteRes = DeleteRes
|
|
||||||
|
|
||||||
// Taxes
|
// Taxes
|
||||||
export type TaxRegionDeleteRes = DeleteRes
|
export type TaxRegionDeleteRes = DeleteRes
|
||||||
export type TaxRateDeleteRes = DeleteRes
|
export type TaxRateDeleteRes = DeleteRes
|
||||||
|
|||||||
@@ -203,7 +203,10 @@ export class Admin {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
list: async (queryParams?: FindParams, headers?: ClientHeaders) => {
|
list: async (
|
||||||
|
queryParams?: FindParams & HttpTypes.AdminCollectionFilters,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) => {
|
||||||
return this.client.fetch<
|
return this.client.fetch<
|
||||||
PaginatedResponse<{ customers: HttpTypes.AdminCustomer[] }>
|
PaginatedResponse<{ customers: HttpTypes.AdminCustomer[] }>
|
||||||
>(`/admin/customers`, {
|
>(`/admin/customers`, {
|
||||||
@@ -234,4 +237,82 @@ export class Admin {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public collection = {
|
||||||
|
create: async (
|
||||||
|
body: HttpTypes.AdminCreateCollection,
|
||||||
|
query?: SelectParams,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) => {
|
||||||
|
return this.client.fetch<{ collection: HttpTypes.AdminCollection }>(
|
||||||
|
`/admin/collections`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
update: async (
|
||||||
|
id: string,
|
||||||
|
body: HttpTypes.AdminUpdateCollection,
|
||||||
|
query?: SelectParams,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) => {
|
||||||
|
return this.client.fetch<{ collection: HttpTypes.AdminCollection }>(
|
||||||
|
`/admin/collections/${id}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
list: async (queryParams?: FindParams, headers?: ClientHeaders) => {
|
||||||
|
return this.client.fetch<
|
||||||
|
PaginatedResponse<{ collections: HttpTypes.AdminCollection[] }>
|
||||||
|
>(`/admin/collections`, {
|
||||||
|
headers,
|
||||||
|
query: queryParams,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
retrieve: async (
|
||||||
|
id: string,
|
||||||
|
query?: SelectParams,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) => {
|
||||||
|
return this.client.fetch<{ collection: HttpTypes.AdminCollection }>(
|
||||||
|
`/admin/collections/${id}`,
|
||||||
|
{
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
delete: async (id: string, headers?: ClientHeaders) => {
|
||||||
|
return this.client.fetch<DeleteResponse<"collection">>(
|
||||||
|
`/admin/collections/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
updateProducts: async (
|
||||||
|
id: string,
|
||||||
|
body: HttpTypes.AdminUpdateCollectionProducts,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) => {
|
||||||
|
return this.client.fetch<{ collection: HttpTypes.AdminCollection }>(
|
||||||
|
`/admin/collections/${id}/products`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
import { BaseProductCollection, BaseProductCollectionFilters } from "./common"
|
import { BaseCollection, BaseCollectionFilters } from "./common"
|
||||||
|
|
||||||
export interface AdminCollection extends BaseProductCollection {}
|
export interface AdminCollection extends BaseCollection {}
|
||||||
export interface AdminCollectionFilters extends BaseProductCollectionFilters {}
|
export interface AdminCollectionFilters extends BaseCollectionFilters {}
|
||||||
|
|
||||||
|
export interface AdminCreateCollection {
|
||||||
|
title: string
|
||||||
|
handle?: string
|
||||||
|
metadata?: Record<string, any>
|
||||||
|
}
|
||||||
|
export interface AdminUpdateCollection extends Partial<AdminCreateCollection> {}
|
||||||
|
|
||||||
|
export interface AdminUpdateCollectionProducts {
|
||||||
|
add?: string[]
|
||||||
|
remove?: string[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import { BaseFilterable } from "../../dal"
|
import { BaseFilterable, OperatorMap } from "../../dal"
|
||||||
import { BaseProduct } from "../product/common"
|
import { AdminProduct } from "../product"
|
||||||
|
|
||||||
export interface BaseProductCollection {
|
export interface BaseCollection {
|
||||||
id?: string
|
id?: string
|
||||||
title?: string
|
title?: string
|
||||||
handle?: string
|
handle?: string
|
||||||
created_at?: string
|
created_at?: string
|
||||||
updated_at?: string
|
updated_at?: string
|
||||||
deleted_at?: string | null
|
deleted_at?: string | null
|
||||||
products?: BaseProduct[]
|
products?: AdminProduct[]
|
||||||
metadata?: Record<string, unknown> | null
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseProductCollectionFilters
|
export interface BaseCollectionFilters
|
||||||
extends BaseFilterable<BaseProductCollectionFilters> {
|
extends BaseFilterable<BaseCollectionFilters> {
|
||||||
q?: string
|
q?: string
|
||||||
id?: string | string[]
|
id?: string | string[]
|
||||||
handle?: string | string[]
|
handle?: string | string[]
|
||||||
title?: string | string[]
|
title?: string | string[]
|
||||||
|
created_at?: OperatorMap<string>
|
||||||
|
updated_at?: OperatorMap<string>
|
||||||
|
deleted_at?: OperatorMap<string>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseProductCollection, BaseProductCollectionFilters } from "./common"
|
import { BaseCollection, BaseCollectionFilters } from "./common"
|
||||||
|
|
||||||
export interface StoreCollection extends BaseProductCollection {}
|
export interface StoreCollection extends BaseCollection {}
|
||||||
export interface StoreCollectionFilters extends BaseProductCollectionFilters {}
|
export interface StoreCollectionFilters extends BaseCollectionFilters {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BaseFilterable, OperatorMap } from "../../dal"
|
import { BaseFilterable, OperatorMap } from "../../dal"
|
||||||
import { BaseProductCollection } from "../collection/common"
|
import { BaseCollection } from "../collection/common"
|
||||||
|
|
||||||
export type ProductStatus = "draft" | "proposed" | "published" | "rejected"
|
export type ProductStatus = "draft" | "proposed" | "published" | "rejected"
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export interface BaseProduct {
|
|||||||
hs_code?: string
|
hs_code?: string
|
||||||
mid_code?: string
|
mid_code?: string
|
||||||
material?: string
|
material?: string
|
||||||
collection?: BaseProductCollection
|
collection?: BaseCollection
|
||||||
collection_id?: string | null
|
collection_id?: string | null
|
||||||
categories?: BaseProductCategory[]
|
categories?: BaseProductCategory[]
|
||||||
type?: BaseProductType | null
|
type?: BaseProductType | null
|
||||||
|
|||||||
@@ -3908,12 +3908,12 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@hookform/resolvers@npm:3.3.2":
|
"@hookform/resolvers@npm:3.4.2":
|
||||||
version: 3.3.2
|
version: 3.4.2
|
||||||
resolution: "@hookform/resolvers@npm:3.3.2"
|
resolution: "@hookform/resolvers@npm:3.4.2"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react-hook-form: ^7.0.0
|
react-hook-form: ^7.0.0
|
||||||
checksum: 538c86a744c71735a5c3be7b450c704111fe53a52634463ed3c801681d57504d3717ab5f7bd20e99ee87d81a9da0aca640af0837eb807486d9d5c927269a9b37
|
checksum: cb8a3e0cc1c9aeaf2adad585f29badf342a96da9cfdf4efa1168aadb7ef52f440cac5cc6fedf0fda6a52effd27a5c859692c89bb99ed5c76d03064aea1f0d684
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -5175,7 +5175,7 @@ __metadata:
|
|||||||
"@ariakit/react": ^0.4.1
|
"@ariakit/react": ^0.4.1
|
||||||
"@dnd-kit/core": ^6.1.0
|
"@dnd-kit/core": ^6.1.0
|
||||||
"@dnd-kit/sortable": ^8.0.0
|
"@dnd-kit/sortable": ^8.0.0
|
||||||
"@hookform/resolvers": 3.3.2
|
"@hookform/resolvers": 3.4.2
|
||||||
"@medusajs/admin-shared": ^0.0.1
|
"@medusajs/admin-shared": ^0.0.1
|
||||||
"@medusajs/admin-vite-plugin": 0.0.1
|
"@medusajs/admin-vite-plugin": 0.0.1
|
||||||
"@medusajs/icons": 1.2.1
|
"@medusajs/icons": 1.2.1
|
||||||
@@ -5217,7 +5217,7 @@ __metadata:
|
|||||||
tsup: ^8.0.2
|
tsup: ^8.0.2
|
||||||
typescript: 5.2.2
|
typescript: 5.2.2
|
||||||
vite: ^5.2.11
|
vite: ^5.2.11
|
||||||
zod: ^3.22.4
|
zod: 3.22.4
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@@ -33295,10 +33295,3 @@ __metadata:
|
|||||||
checksum: 7578ab283dac0eee66a0ad0fc4a7f28c43e6745aadb3a529f59a4b851aa10872b3890398b3160f257f4b6817b4ce643debdda4fb21a2c040adda7862cab0a587
|
checksum: 7578ab283dac0eee66a0ad0fc4a7f28c43e6745aadb3a529f59a4b851aa10872b3890398b3160f257f4b6817b4ce643debdda4fb21a2c040adda7862cab0a587
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"zod@npm:^3.22.4":
|
|
||||||
version: 3.23.8
|
|
||||||
resolution: "zod@npm:3.23.8"
|
|
||||||
checksum: 8f14c87d6b1b53c944c25ce7a28616896319d95bc46a9660fe441adc0ed0a81253b02b5abdaeffedbeb23bdd25a0bf1c29d2c12dd919aef6447652dd295e3e69
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|||||||
Reference in New Issue
Block a user