diff --git a/packages/medusa-js/src/resources/admin/customer-groups.ts b/packages/medusa-js/src/resources/admin/customer-groups.ts index b50562ef16..3b70668f59 100644 --- a/packages/medusa-js/src/resources/admin/customer-groups.ts +++ b/packages/medusa-js/src/resources/admin/customer-groups.ts @@ -17,7 +17,7 @@ class AdminCustomerGroupsResource extends BaseResource { /** * Create a customer group. * - * @param payload - Customer group info. + * @param payload - customer group info * @param customHeaders */ create( @@ -44,8 +44,8 @@ class AdminCustomerGroupsResource extends BaseResource { /** * Updates a customer group * - * @param id customer group id - * @param payload data to update customer group with + * @param id - customer group id + * @param payload - data to update customer group with * @param customHeaders */ update( @@ -102,7 +102,7 @@ class AdminCustomerGroupsResource extends BaseResource { id: string, payload: AdminPostCustomerGroupsGroupCustomersBatchReq, customHeaders: Record = {} - ): Promise { + ): ResponsePromise { const path = `/admin/customer-groups/${id}/customers/batch` return this.client.request("POST", path, payload, {}, customHeaders) } @@ -118,7 +118,7 @@ class AdminCustomerGroupsResource extends BaseResource { id: string, payload: AdminDeleteCustomerGroupsGroupCustomerBatchReq, customHeaders: Record = {} - ): Promise { + ): ResponsePromise { const path = `/admin/customer-groups/${id}/customers/batch` return this.client.request("DELETE", path, payload, {}, customHeaders) } diff --git a/packages/medusa-react/.storybook/preview.js b/packages/medusa-react/.storybook/preview.js index a626fc7b93..7c4b9646ce 100644 --- a/packages/medusa-react/.storybook/preview.js +++ b/packages/medusa-react/.storybook/preview.js @@ -1,7 +1,7 @@ import React from "react" import DefaultMedusaProvider from "./medusa-context" import { initialize, mswDecorator } from "msw-storybook-addon" -import { handlers } from "../mocks/handlers" +import { adminHandlers, storeHandlers } from "../mocks/handlers" initialize() @@ -10,7 +10,7 @@ export const parameters = { // https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args actions: { argTypesRegex: "^on.*" }, msw: { - handlers, + handlers: [...adminHandlers, ...storeHandlers], }, } diff --git a/packages/medusa-react/mocks/data/fixtures.json b/packages/medusa-react/mocks/data/fixtures.json index 7e6b6c634e..9d32ddae57 100644 --- a/packages/medusa-react/mocks/data/fixtures.json +++ b/packages/medusa-react/mocks/data/fixtures.json @@ -516,6 +516,14 @@ "deleted_at": null, "metadata": null }, + "customer_group": { + "name": "test group", + "id": "cgrp_01FX8NJYPPDEH7YK9NYM6BXPHV", + "deleted_at": null, + "metadata": null, + "created_at": "2022-03-03T19:54:53.014Z", + "updated_at": "2022-03-03T19:54:53.014Z" + }, "shipping_profile": { "id": "sp_01F0YESTZQFW15SFEZSS29EZ5R", "name": "Default Shipping Profile", diff --git a/packages/medusa-react/mocks/handlers/admin.ts b/packages/medusa-react/mocks/handlers/admin.ts index efc46f1ce5..c43faf69ac 100644 --- a/packages/medusa-react/mocks/handlers/admin.ts +++ b/packages/medusa-react/mocks/handlers/admin.ts @@ -509,6 +509,48 @@ export const adminHandlers = [ ) }), + rest.get("/admin/customer-groups/", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + customer_groups: fixtures.list("customer_group"), + }) + ) + }), + + rest.get("/admin/customer-groups/:id", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + customer_group: fixtures.get("customer_group"), + }) + ) + }), + + rest.post("/admin/customer-groups/", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + customer_group: { + ...fixtures.get("customer_group"), + ...(req.body as any), + }, + }) + ) + }), + + rest.post("/admin/customer-groups/:id", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + customer_group: { + ...fixtures.get("customer_group"), + ...(req.body as any), + }, + }) + ) + }), + rest.get("/admin/discounts/", (req, res, ctx) => { return res( ctx.status(200), diff --git a/packages/medusa-react/mocks/server.ts b/packages/medusa-react/mocks/server.ts index eb8352c4ef..b19115e9e6 100644 --- a/packages/medusa-react/mocks/server.ts +++ b/packages/medusa-react/mocks/server.ts @@ -1,4 +1,4 @@ import { setupServer } from "msw/node" import { storeHandlers, adminHandlers } from "./handlers" -export const server = setupServer(...[...storeHandlers, ...adminHandlers]) +export const server = setupServer(...storeHandlers, ...adminHandlers) diff --git a/packages/medusa-react/src/hooks/admin/customer-groups/index.ts b/packages/medusa-react/src/hooks/admin/customer-groups/index.ts new file mode 100644 index 0000000000..a494946b87 --- /dev/null +++ b/packages/medusa-react/src/hooks/admin/customer-groups/index.ts @@ -0,0 +1,2 @@ +export * from "./queries" +export * from "./mutations" diff --git a/packages/medusa-react/src/hooks/admin/customer-groups/mutations.ts b/packages/medusa-react/src/hooks/admin/customer-groups/mutations.ts new file mode 100644 index 0000000000..a330ddcbfd --- /dev/null +++ b/packages/medusa-react/src/hooks/admin/customer-groups/mutations.ts @@ -0,0 +1,116 @@ +import { + AdminCustomerGroupsRes, + AdminDeleteCustomerGroupsGroupCustomerBatchReq, + AdminPostCustomerGroupsGroupCustomersBatchReq, + AdminPostCustomerGroupsGroupReq, + AdminPostCustomerGroupsReq, +} from "@medusajs/medusa" +import { useMutation, UseMutationOptions, useQueryClient } from "react-query" +import { Response } from "@medusajs/medusa-js" + +import { useMedusa } from "../../../contexts/medusa" +import { buildOptions } from "../../utils/buildOptions" +import { adminCustomerGroupKeys } from "./queries" + +/** + * Hook returns functions for creating customer groups. + * + * @param options + */ +export const useAdminCreateCustomerGroup = ( + options?: UseMutationOptions< + Response, + Error, + AdminPostCustomerGroupsReq + > +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + + return useMutation( + (payload: AdminPostCustomerGroupsReq) => + client.admin.customerGroups.create(payload), + buildOptions(queryClient, adminCustomerGroupKeys.lists(), options) + ) +} + +/** + * Hook return functions for updating a customer group. + * + * @param id - id of the customer group that is being updated + * @param options + */ +export const useAdminUpdateCustomerGroup = ( + id: string, + options?: UseMutationOptions< + Response, + Error, + AdminPostCustomerGroupsGroupReq + > +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + return useMutation( + (payload: AdminPostCustomerGroupsGroupReq) => + client.admin.customerGroups.update(id, payload), + buildOptions( + queryClient, + [adminCustomerGroupKeys.lists(), adminCustomerGroupKeys.detail(id)], + options + ) + ) +} + +/** + * Hook returns functions for addition of multiple customers to a customer group. + * + * @param id - id of the customer group in which customers are being added + * @param options + */ +export const useAdminAddCustomersToCustomerGroup = ( + id: string, + options?: UseMutationOptions< + Response, + Error, + AdminPostCustomerGroupsGroupCustomersBatchReq + > +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + return useMutation( + (payload: AdminPostCustomerGroupsGroupCustomersBatchReq) => + client.admin.customerGroups.addCustomers(id, payload), + buildOptions( + queryClient, + [adminCustomerGroupKeys.lists(), adminCustomerGroupKeys.detail(id)], + options + ) + ) +} + +/** + * Hook returns function for removal of multiple customers from a customer group. + * + * @param id - id of a group from which customers will be removed + * @param options + */ +export const useAdminRemoveCustomersFromCustomerGroup = ( + id: string, + options?: UseMutationOptions< + Response, + Error, + AdminDeleteCustomerGroupsGroupCustomerBatchReq + > +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + return useMutation( + (payload: AdminDeleteCustomerGroupsGroupCustomerBatchReq) => + client.admin.customerGroups.removeCustomers(id, payload), + buildOptions( + queryClient, + [adminCustomerGroupKeys.lists(), adminCustomerGroupKeys.detail(id)], + options + ) + ) +} diff --git a/packages/medusa-react/src/hooks/admin/customer-groups/queries.ts b/packages/medusa-react/src/hooks/admin/customer-groups/queries.ts new file mode 100644 index 0000000000..e37e3b3aec --- /dev/null +++ b/packages/medusa-react/src/hooks/admin/customer-groups/queries.ts @@ -0,0 +1,65 @@ +import { + AdminCustomerGroupsListRes, + AdminCustomerGroupsRes, + AdminGetCustomerGroupsParams, +} from "@medusajs/medusa" +import { Response } from "@medusajs/medusa-js" +import { useQuery } from "react-query" + +import { useMedusa } from "../../../contexts" +import { UseQueryOptionsWrapper } from "../../../types" +import { queryKeysFactory } from "../../utils/index" + +const ADMIN_CUSTOMER_GROUPS_QUERY_KEY = `admin_customer_groups` as const + +export const adminCustomerGroupKeys = queryKeysFactory( + ADMIN_CUSTOMER_GROUPS_QUERY_KEY +) + +type CustomerGroupQueryKeys = typeof adminCustomerGroupKeys + +/** + * Hook retrieves a customer group by id. + * + * @param id - customer group id + * @param options + */ +export const useAdminCustomerGroup = ( + id: string, + options?: UseQueryOptionsWrapper< + Response, + Error, + ReturnType + > +) => { + const { client } = useMedusa() + const { data, ...rest } = useQuery( + adminCustomerGroupKeys.detail(id), + () => client.admin.customerGroups.retrieve(id), + options + ) + return { ...data, ...rest } as const +} + +/** + * Hook retrieves a list of customer gorups. + * + * @param query - pagination/filtering params + * @param options + */ +export const useAdminCustomerGroups = ( + query?: AdminGetCustomerGroupsParams, + options?: UseQueryOptionsWrapper< + Response, + Error, + ReturnType + > +) => { + const { client } = useMedusa() + const { data, ...rest } = useQuery( + adminCustomerGroupKeys.list(query), + () => client.admin.customerGroups.list(query), + options + ) + return { ...data, ...rest } as const +} diff --git a/packages/medusa-react/src/hooks/admin/index.ts b/packages/medusa-react/src/hooks/admin/index.ts index b16812a9cb..984ad38311 100644 --- a/packages/medusa-react/src/hooks/admin/index.ts +++ b/packages/medusa-react/src/hooks/admin/index.ts @@ -2,6 +2,7 @@ export * from "./auth" export * from "./collections" export * from "./claims" export * from "./customers" +export * from "./customer-groups" export * from "./discounts" export * from "./draft-orders" export * from "./gift-cards" diff --git a/packages/medusa-react/test/hooks/admin/customer-groups/mutations.test.ts b/packages/medusa-react/test/hooks/admin/customer-groups/mutations.test.ts new file mode 100644 index 0000000000..7dbdd5be02 --- /dev/null +++ b/packages/medusa-react/test/hooks/admin/customer-groups/mutations.test.ts @@ -0,0 +1,59 @@ +import { renderHook } from "@testing-library/react-hooks" + +import { + useAdminCreateCustomerGroup, + useAdminUpdateCustomerGroup, +} from "../../../../src/" +import { fixtures } from "../../../../mocks/data" +import { createWrapper } from "../../../utils" + +describe("useAdminCreateCustomerGroup hook", () => { + test("creates a customer group and returns it", async () => { + const group = { + name: "Group 1", + } + + const { result, waitFor } = renderHook( + () => useAdminCreateCustomerGroup(), + { + wrapper: createWrapper(), + } + ) + + result.current.mutate(group) + + await waitFor(() => result.current.isSuccess) + + expect(result.current.data.response.status).toEqual(200) + expect(result.current.data.customer_group).toEqual( + expect.objectContaining(group) + ) + }) + + describe("useAdminUpdateCustomerGroup hook", () => { + test("updates a customer group and returns it", async () => { + const group = { + name: "Changeed name", + } + + const { result, waitFor } = renderHook( + () => useAdminUpdateCustomerGroup(fixtures.get("customer_group").id), + { + wrapper: createWrapper(), + } + ) + + result.current.mutate(group) + + await waitFor(() => result.current.isSuccess) + + expect(result.current.data.response.status).toEqual(200) + expect(result.current.data.customer_group).toEqual( + expect.objectContaining({ + ...fixtures.get("customer_group"), + ...group, + }) + ) + }) + }) +}) diff --git a/packages/medusa-react/test/hooks/admin/customer-groups/queries.test.ts b/packages/medusa-react/test/hooks/admin/customer-groups/queries.test.ts new file mode 100644 index 0000000000..34bfce4a49 --- /dev/null +++ b/packages/medusa-react/test/hooks/admin/customer-groups/queries.test.ts @@ -0,0 +1,34 @@ +import { renderHook } from "@testing-library/react-hooks" + +import { useAdminCustomerGroup, useAdminCustomerGroups } from "../../../../src" +import { fixtures } from "../../../../mocks/data" +import { createWrapper } from "../../../utils" + +describe("useAdminCustomerGroup hook", () => { + test("returns a customer group", async () => { + const group = fixtures.get("customer_group") + const { result, waitFor } = renderHook( + () => useAdminCustomerGroup(group.id), + { + wrapper: createWrapper(), + } + ) + + await waitFor(() => result.current.isSuccess) + + expect(result.current.response.status).toEqual(200) + expect(result.current.customer_group).toEqual(group) + }) + + test("returns a list of customer groups", async () => { + const groups = fixtures.list("customer_group") + const { result, waitFor } = renderHook(() => useAdminCustomerGroups(), { + wrapper: createWrapper(), + }) + + await waitFor(() => result.current.isSuccess) + + expect(result.current.response.status).toEqual(200) + expect(result.current.customer_groups).toEqual(groups) + }) +})