feat(medusa-react): add product category queries and mutations (#3218)

This commit is contained in:
Riqwan Thamir
2023-02-16 10:22:23 +01:00
committed by GitHub
parent 12d304307a
commit 75924b682f
12 changed files with 491 additions and 4 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@medusajs/medusa-js": patch
"medusa-react": patch
"@medusajs/medusa": patch
---
feat(medusa-react): add product category queries and mutations
@@ -8,8 +8,10 @@ import {
AdminProductCategoriesListRes,
AdminProductCategoriesCategoryRes,
AdminGetProductCategoryParams,
AdminPostProductCategoriesCategoryReq,
} from "@medusajs/medusa"
import qs from "qs"
import { ResponsePromise } from "../../typings"
import BaseResource from "../base"
@@ -55,7 +57,7 @@ class AdminProductCategoriesResource extends BaseResource {
*/
update(
productCategoryId: string,
payload: AdminPostProductCategoriesCategoryParams,
payload: AdminPostProductCategoriesCategoryReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminProductCategoriesCategoryRes> {
const path = `/admin/product-categories/${productCategoryId}`
@@ -63,10 +65,10 @@ class AdminProductCategoriesResource extends BaseResource {
}
/**
* Retrieve a list of product categorys
* Retrieve a list of product categories
* @experimental This feature is under development and may change in the future.
* To use this feature please enable featureflag `product_categories` in your medusa backend project.
* @description Retrieve a list of product categorys
* @description Retrieve a list of product categories
* @returns the list of product category as well as the pagination properties
*/
list(
@@ -356,6 +356,14 @@
"updated_at": "2021-03-16T21:24:13.657Z",
"metadata": null
},
"product_category": {
"id": "pcat_01F0YESBFAZ0DV6V831JXWH0BG",
"name": "Skinny Jeans",
"handle": "skinny-jeans",
"created_at": "2021-03-16T21:24:07.273Z",
"updated_at": "2021-03-16T21:24:07.273Z",
"deleted_at": null
},
"product_variant": {
"id": "variant_01F0YESHR7P2YAYBDBY5B6X3PK",
"title": "Test variant",
@@ -130,6 +130,82 @@ export const adminHandlers = [
)
}),
rest.get("/admin/product-categories/", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
product_categories: fixtures.list("product_category"),
})
)
}),
rest.get("/admin/product-categories/:id", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
product_category: fixtures.get("product_category"),
})
)
}),
rest.post("/admin/product-categories/", (req, res, ctx) => {
const body = req.body as Record<string, any>
return res(
ctx.status(200),
ctx.json({
product_category: {
...fixtures.get("product_category"),
...body,
},
})
)
}),
rest.post("/admin/product-categories/:id", (req, res, ctx) => {
const body = req.body as Record<string, any>
return res(
ctx.status(200),
ctx.json({
product_category: {
...fixtures.get("product_category"),
...body,
},
})
)
}),
rest.delete("/admin/product-categories/:id", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
id: req.params.id,
object: "product-category",
deleted: true,
})
)
}),
rest.post("/admin/product-categories/:id/products/batch", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
product_category: fixtures.get("product_category"),
})
)
}),
rest.delete(
"/admin/product-categories/:id/products/batch",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
product_category: fixtures.get("product_category"),
})
)
}
),
rest.post("/admin/gift-cards/", (req, res, ctx) => {
const body = req.body as Record<string, any>
return res(
@@ -18,6 +18,7 @@ export * from "./price-lists"
export * from "./product-tags"
export * from "./product-types"
export * from "./products"
export * from "./product-categories"
export * from "./publishable-api-keys"
export * from "./regions"
export * from "./return-reasons"
@@ -0,0 +1,2 @@
export * from "./queries"
export * from "./mutations"
@@ -0,0 +1,167 @@
import {
useMutation,
UseMutationOptions,
useQueryClient,
} from "@tanstack/react-query"
import { Response } from "@medusajs/medusa-js"
import {
AdminDeleteProductCategoriesCategoryProductsBatchReq,
AdminPostProductCategoriesCategoryProductsBatchReq,
AdminPostProductCategoriesCategoryReq,
AdminPostProductCategoriesReq,
AdminProductCategoriesCategoryDeleteRes,
AdminProductCategoriesCategoryRes,
} from "@medusajs/medusa"
import { useMedusa } from "../../../contexts"
import { buildOptions } from "../../utils/buildOptions"
import { adminProductCategoryKeys } from "./queries"
import { adminProductKeys } from "../products"
/**
* Hook provides a mutation function for creating product categories.
*
* @experimental This feature is under development and may change in the future.
* To use this feature please enable the corresponding feature flag in your medusa backend project.
*/
export const useAdminCreateProductCategory = (
options?: UseMutationOptions<
Response<AdminProductCategoriesCategoryRes>,
Error,
AdminPostProductCategoriesReq
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
(payload: AdminPostProductCategoriesReq) =>
client.admin.productCategories.create(payload),
buildOptions(queryClient, [adminProductCategoryKeys.list()], options)
)
}
/** Update a product category
*
* @experimental This feature is under development and may change in the future.
* To use this feature please enable feature flag `product_categories` in your medusa backend project.
* @description updates a product category
* @returns the updated medusa product category
*/
export const useAdminUpdateProductCategory = (
id: string,
options?: UseMutationOptions<
Response<AdminProductCategoriesCategoryRes>,
Error,
AdminPostProductCategoriesCategoryReq
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
(payload: AdminPostProductCategoriesCategoryReq) =>
client.admin.productCategories.update(id, payload),
buildOptions(
queryClient,
[adminProductCategoryKeys.lists(), adminProductCategoryKeys.detail(id)],
options
)
)
}
/**
* Delete a product category
*
* @experimental This feature is under development and may change in the future.
* To use this feature please enable featureflag `product_categories` in your medusa backend project.
* @param id
* @param options
*/
export const useAdminDeleteProductCategory = (
id: string,
options?: UseMutationOptions<
Response<AdminProductCategoriesCategoryDeleteRes>,
Error,
void
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
() => client.admin.productCategories.delete(id),
buildOptions(
queryClient,
[adminProductCategoryKeys.lists(), adminProductCategoryKeys.detail(id)],
options
)
)
}
/**
* Add products to a product category
*
* @experimental This feature is under development and may change in the future.
* To use this feature please enable featureflag `product_categories` in your medusa backend project.
* @description Add products to a product category
* @param id
* @param options
*/
export const useAdminAddProductsToCategory = (
id: string,
options?: UseMutationOptions<
Response<AdminProductCategoriesCategoryRes>,
Error,
AdminPostProductCategoriesCategoryProductsBatchReq
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
(payload: AdminPostProductCategoriesCategoryProductsBatchReq) => {
return client.admin.productCategories.addProducts(id, payload)
},
buildOptions(
queryClient,
[
adminProductCategoryKeys.lists(),
adminProductCategoryKeys.detail(id),
adminProductKeys.list({ product_category_id: [id] }),
],
options
)
)
}
/**
* Remove products from a product category
* @experimental This feature is under development and may change in the future.
* To use this feature please enable featureflag `product_categories` in your medusa backend project.
* @description remove products from a product category
* @param id
* @param options
*/
export const useAdminDeleteProductsFromCategory = (
id: string,
options?: UseMutationOptions<
Response<AdminProductCategoriesCategoryRes>,
Error,
AdminDeleteProductCategoriesCategoryProductsBatchReq
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
(payload: AdminDeleteProductCategoriesCategoryProductsBatchReq) => {
return client.admin.productCategories.removeProducts(id, payload)
},
buildOptions(
queryClient,
[
adminProductCategoryKeys.lists(),
adminProductCategoryKeys.detail(id),
adminProductKeys.list({ product_category_id: [id] }),
],
options
)
)
}
@@ -0,0 +1,54 @@
import {
AdminGetProductCategoriesParams,
AdminProductCategoriesListRes,
AdminGetProductCategoryParams,
AdminProductCategoriesCategoryRes,
} from "@medusajs/medusa"
import { Response } from "@medusajs/medusa-js"
import { useQuery } from "@tanstack/react-query"
import { useMedusa } from "../../../contexts"
import { UseQueryOptionsWrapper } from "../../../types"
import { queryKeysFactory } from "../../utils"
const ADMIN_PRODUCT_CATEGORIES_QUERY_KEY = `product_categories` as const
export const adminProductCategoryKeys = queryKeysFactory(
ADMIN_PRODUCT_CATEGORIES_QUERY_KEY
)
type ProductCategoryQueryKeys = typeof adminProductCategoryKeys
export const useAdminProductCategories = (
query?: AdminGetProductCategoriesParams,
options?: UseQueryOptionsWrapper<
Response<AdminProductCategoriesListRes>,
Error,
ReturnType<ProductCategoryQueryKeys["list"]>
>
) => {
const { client } = useMedusa()
const { data, ...rest } = useQuery(
adminProductCategoryKeys.list(query),
() => client.admin.productCategories.list(query),
options
)
return { ...data, ...rest } as const
}
export const useAdminProductCategory = (
id: string,
query?: AdminGetProductCategoryParams,
options?: UseQueryOptionsWrapper<
Response<AdminProductCategoriesCategoryRes>,
Error,
ReturnType<ProductCategoryQueryKeys["detail"]>
>
) => {
const { client } = useMedusa()
const { data, ...rest } = useQuery(
adminProductCategoryKeys.detail(id),
() => client.admin.productCategories.retrieve(id, query),
options
)
return { ...data, ...rest } as const
}
@@ -0,0 +1,132 @@
import { renderHook } from "@testing-library/react-hooks"
import {
useAdminAddProductsToCategory,
useAdminCreateProductCategory,
useAdminDeleteProductCategory,
useAdminDeleteProductsFromCategory,
useAdminUpdateProductCategory,
} from "../../../../src"
import { fixtures } from "../../../../mocks/data"
import { createWrapper } from "../../../utils"
describe("useAdminCreateProductCategory hook", () => {
test("creates a product category", async () => {
const category = {
name: "Jeans category",
}
const { result, waitFor } = renderHook(
() => useAdminCreateProductCategory(),
{
wrapper: createWrapper(),
}
)
result.current.mutate(category)
await waitFor(() => result.current.isSuccess)
expect(result.current.data.response.status).toEqual(200)
expect(result.current.data.product_category).toEqual(
expect.objectContaining({
...fixtures.get("product_category"),
...category,
})
)
})
})
describe("useAdminUpdateProductCategory hook", () => {
test("updates a product category", async () => {
const category = {
name: "Updated name",
}
const categoryId = fixtures.get("product_category").id
const { result, waitFor } = renderHook(
() => useAdminUpdateProductCategory(categoryId),
{
wrapper: createWrapper(),
}
)
result.current.mutate(category)
await waitFor(() => result.current.isSuccess)
expect(result.current.data.response.status).toEqual(200)
expect(result.current.data.product_category).toEqual({
...fixtures.get("product_category"),
...category,
})
})
})
describe("useAdminDeleteProductCategory hook", () => {
test("deletes a product category", async () => {
const id = fixtures.get("product_category").id
const { result, waitFor } = renderHook(
() => useAdminDeleteProductCategory(id),
{ wrapper: createWrapper() }
)
result.current.mutate()
await waitFor(() => result.current.isSuccess)
expect(result.current.data).toEqual(
expect.objectContaining({
id,
object: "product-category",
deleted: true,
})
)
})
})
describe("useAdminAddProductsToCategory hook", () => {
test("add products to a product category", async () => {
const id = fixtures.get("product_category").id
const productId = fixtures.get("product").id
const { result, waitFor } = renderHook(
() => useAdminAddProductsToCategory(id),
{ wrapper: createWrapper() }
)
result.current.mutate({ product_ids: [{ id: productId }] })
await waitFor(() => result.current.isSuccess)
expect(result.current.data).toEqual(
expect.objectContaining({
product_category: fixtures.get("product_category"),
})
)
})
})
describe("useAdminDeleteProductsFromCategory hook", () => {
test("remove products from a product category", async () => {
const id = fixtures.get("product_category").id
const productId = fixtures.get("product").id
const { result, waitFor } = renderHook(
() => useAdminDeleteProductsFromCategory(id),
{ wrapper: createWrapper() }
)
result.current.mutate({ product_ids: [{ id: productId }] })
await waitFor(() => result.current.isSuccess)
expect(result.current.data).toEqual(
expect.objectContaining({
product_category: fixtures.get("product_category"),
})
)
})
})
@@ -0,0 +1,37 @@
import { useAdminProductCategory, useAdminProductCategories } from "../../../../src"
import { renderHook } from "@testing-library/react-hooks"
import { fixtures } from "../../../../mocks/data"
import { createWrapper } from "../../../utils"
describe("useAdminProductCategories hook", () => {
test("returns a list of categories", async () => {
const categories = fixtures.list("product_category")
const { result, waitFor } = renderHook(() => useAdminProductCategories(), {
wrapper: createWrapper(),
})
await waitFor(() => result.current.isSuccess)
expect(result.current.response.status).toEqual(200)
expect(result.current.product_categories).toEqual(categories)
})
})
describe("useAdminProductCategory hook", () => {
test("returns a category", async () => {
const category = fixtures.get("product_category")
const { result, waitFor } = renderHook(
() => useAdminProductCategory(category.id),
{
wrapper: createWrapper(),
}
)
await waitFor(() => result.current.isSuccess)
expect(result.current.response.status).toEqual(200)
expect(result.current.product_category).toEqual(category)
})
})
@@ -144,6 +144,7 @@ export const defaultProductCategoryFields = [
"handle",
"is_active",
"is_internal",
"parent_category_id",
"created_at",
"updated_at",
]
@@ -13,7 +13,7 @@ import { defaultStoreScope } from "."
* description: "Retrieve a list of product categories."
* x-authenticated: false
* parameters:
* - (query) q {string} Query used for searching product category names orhandles.
* - (query) q {string} Query used for searching product category names or handles.
* - (query) parent_category_id {string} Returns categories scoped by parent
* - (query) offset=0 {integer} How many product categories to skip in the result.
* - (query) limit=100 {integer} Limit the number of product categories returned.