feat: Admin V2 customer group (#7000)
This commit is contained in:
1 parent
dbddfc12ed
commit
3882cac622
43 files changed
+575
-301
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
import { ICustomerModuleService } from "@medusajs/types"
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
import { ICustomerModuleService } from "@medusajs/types"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ const adminHeaders = {
|
|||||||
medusaIntegrationTestRunner({
|
medusaIntegrationTestRunner({
|
||||||
env,
|
env,
|
||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
describe("POST /admin/customer-groups/:id/customers/batch", () => {
|
describe("POST /admin/customer-groups/:id/customers/batch/add", () => {
|
||||||
let appContainer
|
let appContainer
|
||||||
let customerModuleService: ICustomerModuleService
|
let customerModuleService: ICustomerModuleService
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ medusaIntegrationTestRunner({
|
|||||||
])
|
])
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/customer-groups/${group.id}/customers/batch`,
|
`/admin/customer-groups/${group.id}/customers/batch/add`,
|
||||||
{
|
{
|
||||||
customer_ids: customers.map((c) => ({ id: c.id })),
|
customer_ids: customers.map((c) => ({ id: c.id })),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ICustomerModuleService } from "@medusajs/types"
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
import { ICustomerModuleService } from "@medusajs/types"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ const adminHeaders = {
|
|||||||
medusaIntegrationTestRunner({
|
medusaIntegrationTestRunner({
|
||||||
env,
|
env,
|
||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
describe("DELETE /admin/customer-groups/:id/customers/remove", () => {
|
describe("POST /admin/customer-groups/:id/customers/batch/remove", () => {
|
||||||
let appContainer
|
let appContainer
|
||||||
let customerModuleService: ICustomerModuleService
|
let customerModuleService: ICustomerModuleService
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/customer-groups/${group.id}/customers/remove`,
|
`/admin/customer-groups/${group.id}/customers/batch/remove`,
|
||||||
{
|
{
|
||||||
customer_ids: customers.map((c) => ({ id: c.id })),
|
customer_ids: customers.map((c) => ({ id: c.id })),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
|
import {
|
||||||
|
QueryKey,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseQueryOptions,
|
||||||
|
useMutation,
|
||||||
|
useQuery,
|
||||||
|
} from "@tanstack/react-query"
|
||||||
|
import { client } from "../../lib/client"
|
||||||
|
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||||
import {
|
import {
|
||||||
AdminCustomerGroupListResponse,
|
AdminCustomerGroupListResponse,
|
||||||
AdminCustomerGroupResponse,
|
AdminCustomerGroupResponse,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query"
|
import { z } from "zod"
|
||||||
import { client } from "../../lib/client"
|
import { CreateCustomerGroupSchema } from "../../v2-routes/customer-groups/customer-group-create/components/create-customer-group-form"
|
||||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
import { queryClient } from "../../lib/medusa"
|
||||||
|
import { EditCustomerGroupSchema } from "../../v2-routes/customer-groups/customer-group-edit/components/edit-customer-group-form"
|
||||||
|
import { customersQueryKeys } from "./customers"
|
||||||
|
|
||||||
const CUSTOMER_GROUPS_QUERY_KEY = "customer_groups" as const
|
const CUSTOMER_GROUPS_QUERY_KEY = "customer_groups" as const
|
||||||
const customerGroupsQueryKeys = queryKeysFactory(CUSTOMER_GROUPS_QUERY_KEY)
|
const customerGroupsQueryKeys = queryKeysFactory(CUSTOMER_GROUPS_QUERY_KEY)
|
||||||
@@ -51,3 +62,124 @@ export const useCustomerGroups = (
|
|||||||
|
|
||||||
return { ...data, ...rest }
|
return { ...data, ...rest }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useCreateCustomerGroup = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
AdminCustomerGroupResponse,
|
||||||
|
Error,
|
||||||
|
z.infer<typeof CreateCustomerGroupSchema>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (payload) => client.customerGroups.create(payload),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUpdateCustomerGroup = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
AdminCustomerGroupResponse,
|
||||||
|
Error,
|
||||||
|
z.infer<typeof EditCustomerGroupSchema>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (payload) => client.customerGroups.update(id, payload),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.detail(id),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDeleteCustomerGroup = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
{ id: string; object: "customer-group"; deleted: boolean },
|
||||||
|
Error,
|
||||||
|
void
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: () => client.customerGroups.delete(id),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.detail(id),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAddCustomersToGroup = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
AdminCustomerGroupResponse,
|
||||||
|
Error,
|
||||||
|
{ customer_ids: { id: string }[] }
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (payload) => client.customerGroups.addCustomers(id, payload),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.detail(id),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customersQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useRemoveCustomersFromGroup = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
AdminCustomerGroupResponse,
|
||||||
|
Error,
|
||||||
|
{ customer_ids: { id: string }[] }
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (payload) => client.customerGroups.removeCustomers(id, payload),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.detail(id),
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: customersQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
|
||||||
const CUSTOMERS_QUERY_KEY = "customers" as const
|
const CUSTOMERS_QUERY_KEY = "customers" as const
|
||||||
const customersQueryKeys = queryKeysFactory(CUSTOMERS_QUERY_KEY)
|
export const customersQueryKeys = queryKeysFactory(CUSTOMERS_QUERY_KEY)
|
||||||
|
|
||||||
export const useCustomer = (
|
export const useCustomer = (
|
||||||
id: string,
|
id: string,
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import {
|
|||||||
AdminCustomerGroupListResponse,
|
AdminCustomerGroupListResponse,
|
||||||
AdminCustomerGroupResponse,
|
AdminCustomerGroupResponse,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { getRequest } from "./common"
|
import { z } from "zod"
|
||||||
|
import { CreateCustomerGroupSchema } from "../../v2-routes/customer-groups/customer-group-create/components/create-customer-group-form"
|
||||||
|
import { EditCustomerGroupSchema } from "../../v2-routes/customer-groups/customer-group-edit/components/edit-customer-group-form"
|
||||||
|
import { deleteRequest, getRequest, postRequest } from "./common"
|
||||||
|
|
||||||
async function retrieveCustomerGroup(id: string, query?: Record<string, any>) {
|
async function retrieveCustomerGroup(id: string, query?: Record<string, any>) {
|
||||||
return getRequest<AdminCustomerGroupResponse>(
|
return getRequest<AdminCustomerGroupResponse>(
|
||||||
@@ -18,7 +21,59 @@ async function listCustomerGroups(query?: Record<string, any>) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createCustomerGroup(
|
||||||
|
payload: z.infer<typeof CreateCustomerGroupSchema>
|
||||||
|
) {
|
||||||
|
return postRequest<AdminCustomerGroupResponse>(
|
||||||
|
`/admin/customer-groups`,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCustomerGroup(
|
||||||
|
id: string,
|
||||||
|
payload: z.infer<typeof EditCustomerGroupSchema>
|
||||||
|
) {
|
||||||
|
return postRequest<AdminCustomerGroupResponse>(
|
||||||
|
`/admin/customer-groups/${id}`,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCustomerGroup(id: string) {
|
||||||
|
return deleteRequest<{
|
||||||
|
id: string
|
||||||
|
deleted: boolean
|
||||||
|
object: "customer-group"
|
||||||
|
}>(`/admin/customer-groups/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchAddCustomers(
|
||||||
|
id: string,
|
||||||
|
payload: { customer_ids: { id: string }[] }
|
||||||
|
) {
|
||||||
|
return postRequest<AdminCustomerGroupResponse>(
|
||||||
|
`/admin/customer-groups/${id}/customers/batch/add`,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchRemoveCustomers(
|
||||||
|
id: string,
|
||||||
|
payload: { customer_ids: { id: string }[] }
|
||||||
|
) {
|
||||||
|
return postRequest<AdminCustomerGroupResponse>(
|
||||||
|
`/admin/customer-groups/${id}/customers/batch/remove`,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const customerGroups = {
|
export const customerGroups = {
|
||||||
retrieve: retrieveCustomerGroup,
|
retrieve: retrieveCustomerGroup,
|
||||||
list: listCustomerGroups,
|
list: listCustomerGroups,
|
||||||
|
create: createCustomerGroup,
|
||||||
|
update: updateCustomerGroup,
|
||||||
|
delete: deleteCustomerGroup,
|
||||||
|
addCustomers: batchAddCustomers,
|
||||||
|
removeCustomers: batchRemoveCustomers,
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
AdminProductCategoryResponse,
|
AdminProductCategoryResponse,
|
||||||
SalesChannelDTO,
|
SalesChannelDTO,
|
||||||
UserDTO,
|
UserDTO,
|
||||||
|
AdminCustomerGroupResponse,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { Navigate, Outlet, RouteObject, useLocation } from "react-router-dom"
|
import { Navigate, Outlet, RouteObject, useLocation } from "react-router-dom"
|
||||||
import { ErrorBoundary } from "../../components/error/error-boundary"
|
import { ErrorBoundary } from "../../components/error/error-boundary"
|
||||||
@@ -343,6 +344,55 @@ export const v2Routes: RouteObject[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/customer-groups",
|
||||||
|
handle: {
|
||||||
|
crumb: () => "Customer Groups",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
lazy: () =>
|
||||||
|
import("../../v2-routes/customer-groups/customer-group-list"),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "create",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../v2-routes/customer-groups/customer-group-create"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":id",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../v2-routes/customer-groups/customer-group-detail"
|
||||||
|
),
|
||||||
|
handle: {
|
||||||
|
crumb: (data: AdminCustomerGroupResponse) =>
|
||||||
|
data.customer_group.name,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../v2-routes/customer-groups/customer-group-edit"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "add-customers",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../v2-routes/customer-groups/customer-group-add-customers"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
+61
-121
@@ -2,6 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
|||||||
import { Customer } from "@medusajs/medusa"
|
import { Customer } from "@medusajs/medusa"
|
||||||
import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui"
|
import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui"
|
||||||
import {
|
import {
|
||||||
|
OnChangeFn,
|
||||||
PaginationState,
|
PaginationState,
|
||||||
RowSelectionState,
|
RowSelectionState,
|
||||||
createColumnHelper,
|
createColumnHelper,
|
||||||
@@ -31,12 +32,19 @@ import {
|
|||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
||||||
import { queryClient } from "../../../../../lib/medusa"
|
import { queryClient } from "../../../../../lib/medusa"
|
||||||
|
import { useCustomers } from "../../../../../hooks/api/customers"
|
||||||
|
import { useAddCustomersToGroup } from "../../../../../hooks/api/customer-groups"
|
||||||
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
|
import { useCustomerTableFilters } from "../../../../../hooks/table/filters/use-customer-table-filters"
|
||||||
|
import { useCustomerTableQuery } from "../../../../../hooks/table/query/use-customer-table-query"
|
||||||
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
|
import { AdminCustomerResponse } from "@medusajs/types"
|
||||||
|
|
||||||
type AddCustomersFormProps = {
|
type AddCustomersFormProps = {
|
||||||
customerGroupId: string
|
customerGroupId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddCustomersSchema = zod.object({
|
export const AddCustomersSchema = zod.object({
|
||||||
customer_ids: zod.array(zod.string()).min(1),
|
customer_ids: zod.array(zod.string()).min(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -83,36 +91,47 @@ export const AddCustomersForm = ({
|
|||||||
)
|
)
|
||||||
}, [rowSelection, setValue])
|
}, [rowSelection, setValue])
|
||||||
|
|
||||||
const params = useQueryParams(["q"])
|
const { searchParams, raw } = useCustomerTableQuery({ pageSize: PAGE_SIZE })
|
||||||
const { customers, count, isLoading, isError, error } = useAdminCustomers({
|
const filters = useCustomerTableFilters()
|
||||||
expand: "groups",
|
|
||||||
limit: PAGE_SIZE,
|
const { customers, count, isLoading, isError, error } = useCustomers({
|
||||||
offset: pageIndex * PAGE_SIZE,
|
fields: "id,email,first_name,last_name,*groups",
|
||||||
...params,
|
...searchParams,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const updater: OnChangeFn<RowSelectionState> = (fn) => {
|
||||||
|
const state = typeof fn === "function" ? fn(rowSelection) : fn
|
||||||
|
|
||||||
|
const ids = Object.keys(state)
|
||||||
|
|
||||||
|
setValue("customer_ids", ids, {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
const columns = useColumns()
|
const columns = useColumns()
|
||||||
|
|
||||||
const table = useReactTable({
|
const { table } = useDataTable({
|
||||||
data: customers ?? [],
|
data: customers ?? [],
|
||||||
columns,
|
columns,
|
||||||
pageCount: Math.ceil((count ?? 0) / PAGE_SIZE),
|
count,
|
||||||
state: {
|
enablePagination: true,
|
||||||
pagination,
|
enableRowSelection: (row) => {
|
||||||
rowSelection,
|
return !row.original.groups?.map((gc) => gc.id).includes(customerGroupId)
|
||||||
},
|
},
|
||||||
getRowId: (row) => row.id,
|
getRowId: (row) => row.id,
|
||||||
onPaginationChange: setPagination,
|
pageSize: PAGE_SIZE,
|
||||||
onRowSelectionChange: setRowSelection,
|
rowSelection: {
|
||||||
getCoreRowModel: getCoreRowModel(),
|
state: rowSelection,
|
||||||
manualPagination: true,
|
updater,
|
||||||
meta: {
|
|
||||||
customerGroupId,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync, isLoading: isMutating } =
|
const { mutateAsync, isLoading: isMutating } =
|
||||||
useAdminAddCustomersToCustomerGroup(customerGroupId)
|
useAddCustomersToGroup(customerGroupId)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (data) => {
|
const handleSubmit = form.handleSubmit(async (data) => {
|
||||||
await mutateAsync(
|
await mutateAsync(
|
||||||
@@ -121,18 +140,12 @@ export const AddCustomersForm = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(adminCustomerKeys.lists())
|
|
||||||
handleSuccess(`/customer-groups/${customerGroupId}`)
|
handleSuccess(`/customer-groups/${customerGroupId}`)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const noRecords =
|
|
||||||
!isLoading &&
|
|
||||||
!customers?.length &&
|
|
||||||
!Object.values(params).filter(Boolean).length
|
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -165,98 +178,32 @@ export const AddCustomersForm = ({
|
|||||||
</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>
|
||||||
{noRecords ? (
|
<DataTable
|
||||||
<div className="flex w-full flex-1 items-center justify-center">
|
table={table}
|
||||||
<NoRecords />
|
columns={columns}
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex w-full flex-1 flex-col divide-y">
|
|
||||||
<div className="flex w-full items-center justify-between px-6 py-4">
|
|
||||||
<div></div>
|
|
||||||
<div className="flex items-center gap-x-2">
|
|
||||||
<Query />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex-1 overflow-y-auto">
|
|
||||||
{(customers?.length || 0) > 0 ? (
|
|
||||||
<Table>
|
|
||||||
<Table.Header className="border-t-0">
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => {
|
|
||||||
return (
|
|
||||||
<Table.Row
|
|
||||||
key={headerGroup.id}
|
|
||||||
className="[&_th:first-of-type]:w-[1%] [&_th:first-of-type]:whitespace-nowrap"
|
|
||||||
>
|
|
||||||
{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.groups
|
|
||||||
?.map((cg) => cg.id)
|
|
||||||
.includes(customerGroupId),
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<Table.Cell key={cell.id}>
|
|
||||||
{flexRender(
|
|
||||||
cell.column.columnDef.cell,
|
|
||||||
cell.getContext()
|
|
||||||
)}
|
|
||||||
</Table.Cell>
|
|
||||||
))}
|
|
||||||
</Table.Row>
|
|
||||||
))}
|
|
||||||
</Table.Body>
|
|
||||||
</Table>
|
|
||||||
) : (
|
|
||||||
<div className="flex min-h-full flex-1 items-center justify-center">
|
|
||||||
<NoResults />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<LocalizedTablePagination
|
|
||||||
canNextPage={table.getCanNextPage()}
|
|
||||||
canPreviousPage={table.getCanPreviousPage()}
|
|
||||||
nextPage={table.nextPage}
|
|
||||||
previousPage={table.previousPage}
|
|
||||||
count={count ?? 0}
|
|
||||||
pageIndex={pageIndex}
|
|
||||||
pageCount={table.getPageCount()}
|
|
||||||
pageSize={PAGE_SIZE}
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={[
|
||||||
|
"email",
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"has_account",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
]}
|
||||||
|
isLoading={isLoading}
|
||||||
|
search
|
||||||
|
queryObject={raw}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</RouteFocusModal.Body>
|
</RouteFocusModal.Body>
|
||||||
</form>
|
</form>
|
||||||
</RouteFocusModal.Form>
|
</RouteFocusModal.Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Customer>()
|
const columnHelper = createColumnHelper<AdminCustomerResponse["customer"]>()
|
||||||
|
|
||||||
const useColumns = () => {
|
const useColumns = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -279,21 +226,14 @@ const useColumns = () => {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
cell: ({ row, table }) => {
|
cell: ({ row }) => {
|
||||||
const { customerGroupId } = table.options.meta as {
|
const isPreSelected = !row.getCanSelect()
|
||||||
customerGroupId: string
|
const isSelected = row.getIsSelected() || isPreSelected
|
||||||
}
|
|
||||||
|
|
||||||
const isAdded = row.original.groups
|
|
||||||
?.map((gc) => gc.id)
|
|
||||||
.includes(customerGroupId)
|
|
||||||
|
|
||||||
const isSelected = row.getIsSelected() || isAdded
|
|
||||||
|
|
||||||
const Component = (
|
const Component = (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={isAdded}
|
disabled={isPreSelected}
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -301,7 +241,7 @@ const useColumns = () => {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isAdded) {
|
if (isPreSelected) {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={t("customerGroups.customerAlreadyAdded")}
|
content={t("customerGroups.customerAlreadyAdded")}
|
||||||
File renamed without changes.
-1
@@ -1,5 +1,4 @@
|
|||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
|
|
||||||
import { RouteFocusModal } from "../../../components/route-modal"
|
import { RouteFocusModal } from "../../../components/route-modal"
|
||||||
import { AddCustomersForm } from "./components/add-customers-form"
|
import { AddCustomersForm } from "./components/add-customers-form"
|
||||||
|
|
||||||
File renamed without changes.
+3
-2
@@ -10,8 +10,9 @@ import {
|
|||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
|
import { useCreateCustomerGroup } from "../../../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
const CreateCustomerGroupSchema = zod.object({
|
export const CreateCustomerGroupSchema = zod.object({
|
||||||
name: zod.string().min(1),
|
name: zod.string().min(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ export const CreateCustomerGroupForm = () => {
|
|||||||
resolver: zodResolver(CreateCustomerGroupSchema),
|
resolver: zodResolver(CreateCustomerGroupSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync, isLoading } = useAdminCreateCustomerGroup()
|
const { mutateAsync, isLoading } = useCreateCustomerGroup()
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (data) => {
|
const handleSubmit = form.handleSubmit(async (data) => {
|
||||||
await mutateAsync(
|
await mutateAsync(
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+47
-35
@@ -1,23 +1,30 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
|
||||||
import { Customer, CustomerGroup } from "@medusajs/medusa"
|
|
||||||
import { Button, Checkbox, Container, Heading, usePrompt } from "@medusajs/ui"
|
import { Button, Checkbox, Container, Heading, usePrompt } from "@medusajs/ui"
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { Customer } from "@medusajs/medusa"
|
||||||
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import {
|
import {
|
||||||
|
adminCustomerGroupKeys,
|
||||||
|
useAdminCustomPost,
|
||||||
useAdminCustomerGroupCustomers,
|
useAdminCustomerGroupCustomers,
|
||||||
useAdminRemoveCustomersFromCustomerGroup,
|
|
||||||
} from "medusa-react"
|
} from "medusa-react"
|
||||||
import { useMemo } from "react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { Link } from "react-router-dom"
|
|
||||||
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 { Link } from "react-router-dom"
|
||||||
|
import { RowSelectionState, createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useCustomerTableColumns } from "../../../../../hooks/table/columns/use-customer-table-columns"
|
import { useCustomerTableColumns } from "../../../../../hooks/table/columns/use-customer-table-columns"
|
||||||
import { useCustomerTableFilters } from "../../../../../hooks/table/filters/use-customer-table-filters"
|
import { useCustomerTableFilters } from "../../../../../hooks/table/filters/use-customer-table-filters"
|
||||||
import { useCustomerTableQuery } from "../../../../../hooks/table/query/use-customer-table-query"
|
import { useCustomerTableQuery } from "../../../../../hooks/table/query/use-customer-table-query"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
|
import { useMemo, useState } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { AdminCustomerGroupResponse, AdminCustomerResponse } from "@medusajs/types"
|
||||||
|
import { useCustomers } from "../../../../../hooks/api/customers"
|
||||||
|
import {
|
||||||
|
useRemoveCustomersFromGroup,
|
||||||
|
useUpdateCustomerGroup,
|
||||||
|
} from "../../../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
type CustomerGroupCustomerSectionProps = {
|
type CustomerGroupCustomerSectionProps = {
|
||||||
group: CustomerGroup
|
group: AdminCustomerGroupResponse["customer_group"]
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAGE_SIZE = 10
|
const PAGE_SIZE = 10
|
||||||
@@ -25,22 +32,18 @@ const PAGE_SIZE = 10
|
|||||||
export const CustomerGroupCustomerSection = ({
|
export const CustomerGroupCustomerSection = ({
|
||||||
group,
|
group,
|
||||||
}: CustomerGroupCustomerSectionProps) => {
|
}: CustomerGroupCustomerSectionProps) => {
|
||||||
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const prompt = usePrompt()
|
||||||
|
|
||||||
const { searchParams, raw } = useCustomerTableQuery({ pageSize: PAGE_SIZE })
|
const { searchParams, raw } = useCustomerTableQuery({ pageSize: PAGE_SIZE })
|
||||||
const { customers, count, isLoading, isError, error } =
|
const { customers, count, isLoading, isError, error } = useCustomers({
|
||||||
useAdminCustomerGroupCustomers(
|
|
||||||
group.id,
|
|
||||||
{
|
|
||||||
...searchParams,
|
...searchParams,
|
||||||
},
|
groups: group.id,
|
||||||
{
|
})
|
||||||
keepPreviousData: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const filters = useCustomerTableFilters(["groups"])
|
|
||||||
const columns = useColumns()
|
const columns = useColumns()
|
||||||
|
const filters = useCustomerTableFilters(["groups"])
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
data: customers ?? [],
|
data: customers ?? [],
|
||||||
@@ -50,21 +53,28 @@ export const CustomerGroupCustomerSection = ({
|
|||||||
enablePagination: true,
|
enablePagination: true,
|
||||||
enableRowSelection: true,
|
enableRowSelection: true,
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater: setRowSelection,
|
||||||
|
},
|
||||||
meta: {
|
meta: {
|
||||||
customerGroupId: group.id,
|
customerGroupId: group.id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync } = useAdminRemoveCustomersFromCustomerGroup(group.id)
|
if (isError) {
|
||||||
const prompt = usePrompt()
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
const handleRemoveCustomers = async (selection: Record<string, boolean>) => {
|
const { mutateAsync } = useRemoveCustomersFromGroup(group.id)
|
||||||
const selected = Object.keys(selection).filter((k) => selection[k])
|
|
||||||
|
const handleRemove = async () => {
|
||||||
|
const keys = Object.keys(rowSelection)
|
||||||
|
|
||||||
const res = await prompt({
|
const res = await prompt({
|
||||||
title: t("general.areYouSure"),
|
title: t("general.areYouSure"),
|
||||||
description: t("customerGroups.removeCustomersWarning", {
|
description: t("customerGroups.removeCustomersWarning", {
|
||||||
count: selected.length,
|
count: keys.length,
|
||||||
}),
|
}),
|
||||||
confirmText: t("actions.continue"),
|
confirmText: t("actions.continue"),
|
||||||
cancelText: t("actions.cancel"),
|
cancelText: t("actions.cancel"),
|
||||||
@@ -74,13 +84,16 @@ export const CustomerGroupCustomerSection = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await mutateAsync({
|
await mutateAsync(
|
||||||
customer_ids: selected.map((s) => ({ id: s })),
|
{
|
||||||
})
|
customer_ids: keys.map((k) => ({ id: k })),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
setRowSelection({})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
)
|
||||||
if (isError) {
|
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -111,14 +124,14 @@ export const CustomerGroupCustomerSection = ({
|
|||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
]}
|
]}
|
||||||
|
queryObject={raw}
|
||||||
commands={[
|
commands={[
|
||||||
{
|
{
|
||||||
action: handleRemoveCustomers,
|
action: handleRemove,
|
||||||
label: t("actions.remove"),
|
label: t("actions.remove"),
|
||||||
shortcut: "r",
|
shortcut: "r",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
queryObject={raw}
|
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
@@ -128,12 +141,11 @@ const CustomerActions = ({
|
|||||||
customer,
|
customer,
|
||||||
customerGroupId,
|
customerGroupId,
|
||||||
}: {
|
}: {
|
||||||
customer: Customer
|
customer: AdminCustomerResponse["customer"]
|
||||||
customerGroupId: string
|
customerGroupId: string
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { mutateAsync } =
|
const { mutateAsync } = useRemoveCustomersFromGroup(customerGroupId)
|
||||||
useAdminRemoveCustomersFromCustomerGroup(customerGroupId)
|
|
||||||
|
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
|
|
||||||
@@ -182,7 +194,7 @@ const CustomerActions = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Customer>()
|
const columnHelper = createColumnHelper<AdminCustomerResponse["customer"]>()
|
||||||
|
|
||||||
const useColumns = () => {
|
const useColumns = () => {
|
||||||
const columns = useCustomerTableColumns()
|
const columns = useCustomerTableColumns()
|
||||||
File renamed without changes.
+4
-4
@@ -1,13 +1,13 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import type { CustomerGroup } from "@medusajs/medusa"
|
|
||||||
import { Container, Heading } from "@medusajs/ui"
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
import { useAdminDeleteCustomerGroup } from "medusa-react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { useNavigate } from "react-router-dom"
|
import { useNavigate } from "react-router-dom"
|
||||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
|
import { useDeleteCustomerGroup } from "../../../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
type CustomerGroupGeneralSectionProps = {
|
type CustomerGroupGeneralSectionProps = {
|
||||||
group: CustomerGroup
|
group: AdminCustomerGroupResponse["customer_group"]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CustomerGroupGeneralSection = ({
|
export const CustomerGroupGeneralSection = ({
|
||||||
@@ -16,7 +16,7 @@ export const CustomerGroupGeneralSection = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const { mutateAsync } = useAdminDeleteCustomerGroup(group.id)
|
const { mutateAsync } = useDeleteCustomerGroup(group.id)
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
await mutateAsync(undefined, {
|
await mutateAsync(undefined, {
|
||||||
File renamed without changes.
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
import { useAdminCustomerGroup } from "medusa-react"
|
|
||||||
import { Outlet, json, useLoaderData, useParams } from "react-router-dom"
|
import { Outlet, json, useLoaderData, useParams } from "react-router-dom"
|
||||||
import { JsonViewSection } from "../../../components/common/json-view-section"
|
import { JsonViewSection } from "../../../components/common/json-view-section"
|
||||||
import { CustomerGroupCustomerSection } from "./components/customer-group-customer-section"
|
import { CustomerGroupCustomerSection } from "./components/customer-group-customer-section"
|
||||||
import { CustomerGroupGeneralSection } from "./components/customer-group-general-section"
|
import { CustomerGroupGeneralSection } from "./components/customer-group-general-section"
|
||||||
import { customerGroupLoader } from "./loader"
|
import { customerGroupLoader } from "./loader"
|
||||||
|
import { useCustomerGroup } from "../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
export const CustomerGroupDetail = () => {
|
export const CustomerGroupDetail = () => {
|
||||||
const initialData = useLoaderData() as Awaited<
|
const initialData = useLoaderData() as Awaited<
|
||||||
@@ -11,7 +11,7 @@ export const CustomerGroupDetail = () => {
|
|||||||
>
|
>
|
||||||
|
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { customer_group, isLoading, isError, error } = useAdminCustomerGroup(
|
const { customer_group, isLoading, isError, error } = useCustomerGroup(
|
||||||
id!,
|
id!,
|
||||||
undefined,
|
undefined,
|
||||||
{ initialData }
|
{ initialData }
|
||||||
File renamed without changes.
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
import { AdminCustomerGroupsRes } from "@medusajs/medusa"
|
|
||||||
import { Response } from "@medusajs/medusa-js"
|
import { Response } from "@medusajs/medusa-js"
|
||||||
import { adminProductKeys } from "medusa-react"
|
import { adminProductKeys } from "medusa-react"
|
||||||
import { LoaderFunctionArgs } from "react-router-dom"
|
import { LoaderFunctionArgs } from "react-router-dom"
|
||||||
|
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
import { medusa, queryClient } from "../../../lib/medusa"
|
import { medusa, queryClient } from "../../../lib/medusa"
|
||||||
|
|
||||||
const customerGroupDetailQuery = (id: string) => ({
|
const customerGroupDetailQuery = (id: string) => ({
|
||||||
@@ -15,7 +15,7 @@ export const customerGroupLoader = async ({ params }: LoaderFunctionArgs) => {
|
|||||||
const query = customerGroupDetailQuery(id!)
|
const query = customerGroupDetailQuery(id!)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
queryClient.getQueryData<Response<AdminCustomerGroupsRes>>(
|
queryClient.getQueryData<Response<AdminCustomerGroupResponse>>(
|
||||||
query.queryKey
|
query.queryKey
|
||||||
) ?? (await queryClient.fetchQuery(query))
|
) ?? (await queryClient.fetchQuery(query))
|
||||||
)
|
)
|
||||||
+5
-4
@@ -1,5 +1,4 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { CustomerGroup } from "@medusajs/medusa"
|
|
||||||
import { Button, Input } from "@medusajs/ui"
|
import { Button, Input } from "@medusajs/ui"
|
||||||
import { useAdminUpdateCustomerGroup } from "medusa-react"
|
import { useAdminUpdateCustomerGroup } from "medusa-react"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
@@ -10,12 +9,14 @@ import {
|
|||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
|
import { useUpdateCustomerGroup } from "../../../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
type EditCustomerGroupFormProps = {
|
type EditCustomerGroupFormProps = {
|
||||||
group: CustomerGroup
|
group: AdminCustomerGroupResponse["customer_group"]
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditCustomerGroupSchema = z.object({
|
export const EditCustomerGroupSchema = z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ export const EditCustomerGroupForm = ({
|
|||||||
resolver: zodResolver(EditCustomerGroupSchema),
|
resolver: zodResolver(EditCustomerGroupSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync, isLoading } = useAdminUpdateCustomerGroup(group.id)
|
const { mutateAsync, isLoading } = useUpdateCustomerGroup(group.id)
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (data) => {
|
const handleSubmit = form.handleSubmit(async (data) => {
|
||||||
await mutateAsync(data, {
|
await mutateAsync(data, {
|
||||||
File renamed without changes.
+2
-4
@@ -1,15 +1,13 @@
|
|||||||
import { Heading } from "@medusajs/ui"
|
import { Heading } from "@medusajs/ui"
|
||||||
import { useAdminCustomerGroup } from "medusa-react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
import { RouteDrawer } from "../../../components/route-modal"
|
import { RouteDrawer } from "../../../components/route-modal"
|
||||||
import { EditCustomerGroupForm } from "./components/edit-customer-group-form"
|
import { EditCustomerGroupForm } from "./components/edit-customer-group-form"
|
||||||
|
import { useCustomerGroup } from "../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
export const CustomerGroupEdit = () => {
|
export const CustomerGroupEdit = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { customer_group, isLoading, isError, error } = useAdminCustomerGroup(
|
const { customer_group, isLoading, isError, error } = useCustomerGroup(id!)
|
||||||
id!
|
|
||||||
)
|
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
File renamed without changes.
+4
-10
@@ -1,5 +1,4 @@
|
|||||||
import { Button, Container, Heading } from "@medusajs/ui"
|
import { Button, Container, Heading } from "@medusajs/ui"
|
||||||
import { useAdminCustomerGroups } from "medusa-react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
import { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
@@ -7,6 +6,7 @@ import { useDataTable } from "../../../../../hooks/use-data-table"
|
|||||||
import { useCustomerGroupTableColumns } from "./use-customer-group-table-columns"
|
import { useCustomerGroupTableColumns } from "./use-customer-group-table-columns"
|
||||||
import { useCustomerGroupTableFilters } from "./use-customer-group-table-filters"
|
import { useCustomerGroupTableFilters } from "./use-customer-group-table-filters"
|
||||||
import { useCustomerGroupTableQuery } from "./use-customer-group-table-query"
|
import { useCustomerGroupTableQuery } from "./use-customer-group-table-query"
|
||||||
|
import { useCustomerGroups } from "../../../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
const PAGE_SIZE = 20
|
const PAGE_SIZE = 20
|
||||||
|
|
||||||
@@ -17,16 +17,10 @@ export const CustomerGroupListTable = () => {
|
|||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
const { customer_groups, count, isLoading, isError, error } =
|
const { customer_groups, count, isLoading, isError, error } =
|
||||||
useAdminCustomerGroups(
|
useCustomerGroups({
|
||||||
{
|
|
||||||
...searchParams,
|
...searchParams,
|
||||||
expand: "customers",
|
fields: "id,name,customers.id",
|
||||||
fields: "id,name,customers,customers.id",
|
})
|
||||||
},
|
|
||||||
{
|
|
||||||
keepPreviousData: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const filters = useCustomerGroupTableFilters()
|
const filters = useCustomerGroupTableFilters()
|
||||||
const columns = useCustomerGroupTableColumns()
|
const columns = useCustomerGroupTableColumns()
|
||||||
File renamed without changes.
+10
-5
@@ -1,13 +1,14 @@
|
|||||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { CustomerGroup } from "@medusajs/medusa"
|
|
||||||
import { usePrompt } from "@medusajs/ui"
|
import { usePrompt } from "@medusajs/ui"
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useAdminDeleteCustomerGroup } from "medusa-react"
|
|
||||||
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 { ActionMenu } from "../../../../../components/common/action-menu"
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
|
import { useDeleteCustomerGroup } from "../../../../../hooks/api/customer-groups"
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<CustomerGroup>()
|
const columnHelper =
|
||||||
|
createColumnHelper<AdminCustomerGroupResponse["customer_group"]>()
|
||||||
|
|
||||||
export const useCustomerGroupTableColumns = () => {
|
export const useCustomerGroupTableColumns = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -35,11 +36,15 @@ export const useCustomerGroupTableColumns = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomerGroupActions = ({ group }: { group: CustomerGroup }) => {
|
const CustomerGroupActions = ({
|
||||||
|
group,
|
||||||
|
}: {
|
||||||
|
group: AdminCustomerGroupResponse["customer_group"]
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
|
|
||||||
const { mutateAsync } = useAdminDeleteCustomerGroup(group.id)
|
const { mutateAsync } = useDeleteCustomerGroup(group.id)
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
const res = await prompt({
|
const res = await prompt({
|
||||||
File renamed without changes.
+1
-2
@@ -1,4 +1,3 @@
|
|||||||
import { AdminGetCustomerGroupsParams } from "@medusajs/medusa"
|
|
||||||
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
||||||
|
|
||||||
type UseCustomerGroupTableQueryProps = {
|
type UseCustomerGroupTableQueryProps = {
|
||||||
@@ -17,7 +16,7 @@ export const useCustomerGroupTableQuery = ({
|
|||||||
|
|
||||||
const { offset, created_at, updated_at, q, order } = queryObject
|
const { offset, created_at, updated_at, q, order } = queryObject
|
||||||
|
|
||||||
const searchParams: AdminGetCustomerGroupsParams = {
|
const searchParams = {
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
offset: offset ? Number(offset) : 0,
|
offset: offset ? Number(offset) : 0,
|
||||||
order,
|
order,
|
||||||
File renamed without changes.
File renamed without changes.
-10
@@ -138,16 +138,6 @@ export const CreateCustomerForm = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-y-4">
|
|
||||||
<div>
|
|
||||||
<Text size="small" leading="compact" weight="plus">
|
|
||||||
{t("fields.password")}
|
|
||||||
</Text>
|
|
||||||
<Text size="small" className="text-ui-fg-subtle">
|
|
||||||
{t("customers.passwordHint")}
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</RouteFocusModal.Body>
|
</RouteFocusModal.Body>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+82
-46
@@ -1,54 +1,90 @@
|
|||||||
// TODO: Should be added with Customer Groups UI
|
import { Customer, CustomerGroup } from "@medusajs/medusa"
|
||||||
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
|
import { useMemo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { useCustomerGroups } from "../../../../../hooks/api/customer-groups"
|
||||||
|
import {
|
||||||
|
AdminCustomerGroupResponse,
|
||||||
|
AdminCustomerResponse,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
|
import { useCustomerGroupTableFilters } from "../../../../customer-groups/customer-group-list/components/customer-group-list-table/use-customer-group-table-filters"
|
||||||
|
import { t } from "i18next"
|
||||||
|
|
||||||
// import { Customer, CustomerGroup } from "@medusajs/medusa"
|
type CustomerGroupSectionProps = {
|
||||||
// import { Container, Heading } from "@medusajs/ui"
|
customer: AdminCustomerResponse["customer"]
|
||||||
// import { createColumnHelper } from "@tanstack/react-table"
|
}
|
||||||
// import { useAdminCustomerGroups } from "medusa-react"
|
|
||||||
// import { useMemo } from "react"
|
|
||||||
// import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
// // TODO: Continue working on this when there is a natural way to get customer groups related to a customer.
|
const PAGE_SIZE = 10
|
||||||
// type CustomerGroupSectionProps = {
|
|
||||||
// customer: Customer
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export const CustomerGroupSection = ({
|
export const CustomerGroupSection = ({
|
||||||
// customer,
|
customer,
|
||||||
// }: CustomerGroupSectionProps) => {
|
}: CustomerGroupSectionProps) => {
|
||||||
// const { customer_groups, isLoading, isError, error } = useAdminCustomerGroups(
|
const { customer_groups, count, isLoading, isError, error } =
|
||||||
// {
|
useCustomerGroups({
|
||||||
// id: customer.groups.map((g) => g.id).join(","),
|
customers: { id: customer.id },
|
||||||
// }
|
})
|
||||||
// )
|
|
||||||
|
|
||||||
// if (isError) {
|
const filters = useCustomerGroupTableFilters()
|
||||||
// throw error
|
const columns = useColumns()
|
||||||
// }
|
|
||||||
|
|
||||||
// return (
|
const { table } = useDataTable({
|
||||||
// <Container className="p-0 divide-y">
|
data: customer_groups ?? [],
|
||||||
// <div className="px-6 py-4">
|
columns,
|
||||||
// <Heading level="h2">Groups</Heading>
|
count,
|
||||||
// </div>
|
getRowId: (row) => row.id,
|
||||||
// </Container>
|
enablePagination: true,
|
||||||
// )
|
enableRowSelection: true,
|
||||||
// }
|
pageSize: PAGE_SIZE,
|
||||||
|
})
|
||||||
|
|
||||||
// const columnHelper = createColumnHelper<CustomerGroup>()
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
// const useColumns = () => {
|
if (isError) {
|
||||||
// const { t } = useTranslation()
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
// return useMemo(
|
return (
|
||||||
// () => [
|
<Container className="divide-y p-0">
|
||||||
// columnHelper.display({
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
// id: "select",
|
<Heading level="h2">{t("customerGroups.domain")}</Heading>
|
||||||
// }),
|
</div>
|
||||||
// columnHelper.accessor("name", {
|
<DataTable
|
||||||
// header: t("fields.name"),
|
table={table}
|
||||||
// cell: ({ getValue }) => getValue(),
|
columns={columns}
|
||||||
// }),
|
pageSize={PAGE_SIZE}
|
||||||
// ],
|
isLoading={isLoading}
|
||||||
// [t]
|
count={count}
|
||||||
// )
|
navigateTo={(row) => `/customer-groups/${row.id}`}
|
||||||
// }
|
filters={filters}
|
||||||
|
search
|
||||||
|
pagination
|
||||||
|
orderBy={["name", "created_at", "updated_at"]}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnHelper =
|
||||||
|
createColumnHelper<AdminCustomerGroupResponse["customer_group"]>()
|
||||||
|
|
||||||
|
const useColumns = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.display({
|
||||||
|
id: "select",
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("name", {
|
||||||
|
header: t("fields.name"),
|
||||||
|
cell: ({ getValue }) => getValue(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[t]
|
||||||
|
)
|
||||||
|
}
|
||||||
+2
-1
@@ -3,6 +3,7 @@ import { CustomerGeneralSection } from "./components/customer-general-section"
|
|||||||
import { JsonViewSection } from "../../../components/common/json-view-section"
|
import { JsonViewSection } from "../../../components/common/json-view-section"
|
||||||
import { customerLoader } from "./loader"
|
import { customerLoader } from "./loader"
|
||||||
import { useCustomer } from "../../../hooks/api/customers"
|
import { useCustomer } from "../../../hooks/api/customers"
|
||||||
|
import { CustomerGroupSection } from "./components/customer-group-section"
|
||||||
|
|
||||||
export const CustomerDetail = () => {
|
export const CustomerDetail = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
@@ -32,7 +33,7 @@ export const CustomerDetail = () => {
|
|||||||
{/* <CustomerOrderSection customer={customer} />
|
{/* <CustomerOrderSection customer={customer} />
|
||||||
// TODO: re-add when order endpoints are added to api-v2
|
// TODO: re-add when order endpoints are added to api-v2
|
||||||
*/}
|
*/}
|
||||||
{/* <CustomerGroupSection customer={customer} /> */}
|
<CustomerGroupSection customer={customer} />
|
||||||
<JsonViewSection data={customer} />
|
<JsonViewSection data={customer} />
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+20
-6
@@ -1,15 +1,19 @@
|
|||||||
|
import { createCustomerGroupCustomersWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../../../../types/routing"
|
} from "../../../../../../../types/routing"
|
||||||
|
import { AdminPostCustomerGroupsGroupCustomersBatchReq } from "../../../../validators"
|
||||||
import { AdminPostCustomerGroupsGroupCustomersBatchReq } from "../../../validators"
|
|
||||||
import { createCustomerGroupCustomersWorkflow } from "@medusajs/core-flows"
|
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
req: AuthenticatedMedusaRequest<AdminPostCustomerGroupsGroupCustomersBatchReq>,
|
req: AuthenticatedMedusaRequest<AdminPostCustomerGroupsGroupCustomersBatchReq>,
|
||||||
res: MedusaResponse
|
res: MedusaResponse<AdminCustomerGroupResponse>
|
||||||
) => {
|
) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const { customer_ids } = req.validatedBody
|
const { customer_ids } = req.validatedBody
|
||||||
@@ -30,5 +34,15 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ customer_group_customers: result })
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "customer_group",
|
||||||
|
variables: { id },
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [customer_group] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ customer_group })
|
||||||
}
|
}
|
||||||
+19
-8
@@ -1,15 +1,19 @@
|
|||||||
|
import { deleteCustomerGroupCustomersWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../../../../types/routing"
|
} from "../../../../../../../types/routing"
|
||||||
|
import { AdminPostCustomerGroupsGroupCustomersBatchReq } from "../../../../validators"
|
||||||
import { AdminPostCustomerGroupsGroupCustomersBatchReq } from "../../../validators"
|
|
||||||
import { deleteCustomerGroupCustomersWorkflow } from "@medusajs/core-flows"
|
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
req: AuthenticatedMedusaRequest<AdminPostCustomerGroupsGroupCustomersBatchReq>,
|
req: AuthenticatedMedusaRequest<AdminPostCustomerGroupsGroupCustomersBatchReq>,
|
||||||
res: MedusaResponse
|
res: MedusaResponse<AdminCustomerGroupResponse>
|
||||||
) => {
|
) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const { customer_ids } = req.validatedBody
|
const { customer_ids } = req.validatedBody
|
||||||
@@ -30,8 +34,15 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
object: "customer_group_customers",
|
|
||||||
deleted: true,
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "customer_group",
|
||||||
|
variables: { id },
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [customer_group] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ customer_group })
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|
||||||
|
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||||
import {
|
import {
|
||||||
AdminDeleteCustomerGroupsGroupCustomersBatchReq,
|
AdminDeleteCustomerGroupsGroupCustomersBatchReq,
|
||||||
AdminGetCustomerGroupsGroupCustomersParams,
|
AdminGetCustomerGroupsGroupCustomersParams,
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
AdminPostCustomerGroupsGroupReq,
|
AdminPostCustomerGroupsGroupReq,
|
||||||
AdminPostCustomerGroupsReq,
|
AdminPostCustomerGroupsReq,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||||
@@ -63,14 +63,24 @@ export const adminCustomerGroupRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/customer-groups/:id/customers/batch",
|
matcher: "/admin/customer-groups/:id/customers/batch/add",
|
||||||
middlewares: [transformBody(AdminPostCustomerGroupsGroupCustomersBatchReq)],
|
middlewares: [
|
||||||
|
transformBody(AdminPostCustomerGroupsGroupCustomersBatchReq),
|
||||||
|
transformQuery(
|
||||||
|
AdminGetCustomerGroupsGroupParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/customer-groups/:id/customers/remove",
|
matcher: "/admin/customer-groups/:id/customers/batch/remove",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
transformBody(AdminDeleteCustomerGroupsGroupCustomersBatchReq),
|
transformBody(AdminDeleteCustomerGroupsGroupCustomersBatchReq),
|
||||||
|
transformQuery(
|
||||||
|
AdminGetCustomerGroupsGroupParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import { OperatorMap } from "@medusajs/types"
|
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||||
import { Transform, Type } from "class-transformer"
|
|
||||||
import {
|
import {
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
import { Transform, Type } from "class-transformer"
|
||||||
import { OperatorMapValidator } from "../../../types/validators/operator-map"
|
|
||||||
import { IsType } from "../../../utils"
|
import { IsType } from "../../../utils"
|
||||||
|
import { OperatorMap } from "@medusajs/types"
|
||||||
|
import { OperatorMapValidator } from "../../../types/validators/operator-map"
|
||||||
|
|
||||||
export class AdminGetCustomerGroupsGroupParams extends FindParams {}
|
export class AdminGetCustomerGroupsGroupParams extends FindParams {}
|
||||||
|
|
||||||
@@ -61,6 +62,10 @@ export class AdminGetCustomerGroupsParams extends extendedFindParamsMixin({
|
|||||||
limit: 100,
|
limit: 100,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}) {
|
}) {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
q?: string
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString({ each: true })
|
@IsString({ each: true })
|
||||||
id?: string | string[]
|
id?: string | string[]
|
||||||
@@ -120,6 +125,10 @@ export class AdminGetCustomerGroupsGroupCustomersParams extends extendedFindPara
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
q?: string
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString({ each: true })
|
@IsString({ each: true })
|
||||||
id?: string | string[]
|
id?: string | string[]
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import {
|
|||||||
deleteCustomersWorkflow,
|
deleteCustomersWorkflow,
|
||||||
updateCustomersWorkflow,
|
updateCustomersWorkflow,
|
||||||
} from "@medusajs/core-flows"
|
} from "@medusajs/core-flows"
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { AdminCustomerResponse, CustomerUpdatableFields } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
AdminCustomerResponse,
|
ContainerRegistrationKeys,
|
||||||
CustomerUpdatableFields,
|
remoteQueryObjectFromString,
|
||||||
ICustomerModuleService,
|
} from "@medusajs/utils"
|
||||||
} from "@medusajs/types"
|
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
@@ -17,24 +16,26 @@ export const GET = async (
|
|||||||
req: AuthenticatedMedusaRequest,
|
req: AuthenticatedMedusaRequest,
|
||||||
res: MedusaResponse<AdminCustomerResponse>
|
res: MedusaResponse<AdminCustomerResponse>
|
||||||
) => {
|
) => {
|
||||||
const customerModuleService = req.scope.resolve<ICustomerModuleService>(
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
ModuleRegistrationName.CUSTOMER
|
|
||||||
)
|
|
||||||
|
|
||||||
const customer = await customerModuleService.retrieve(req.params.id, {
|
const variables = { id: req.params.id }
|
||||||
select: req.retrieveConfig.select,
|
|
||||||
relations: req.retrieveConfig.relations,
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "customer",
|
||||||
|
variables,
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
})
|
})
|
||||||
|
|
||||||
res.status(200).json({ customer: customer as AdminCustomerResponse["customer"] })
|
const [customer] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ customer })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
req: AuthenticatedMedusaRequest<CustomerUpdatableFields>,
|
req: AuthenticatedMedusaRequest<CustomerUpdatableFields>,
|
||||||
res: MedusaResponse<AdminCustomerResponse>
|
res: MedusaResponse<AdminCustomerResponse>
|
||||||
) => {
|
) => {
|
||||||
const updateCustomers = updateCustomersWorkflow(req.scope)
|
const { errors } = await updateCustomersWorkflow(req.scope).run({
|
||||||
const { result, errors } = await updateCustomers.run({
|
|
||||||
input: {
|
input: {
|
||||||
selector: { id: req.params.id },
|
selector: { id: req.params.id },
|
||||||
update: req.validatedBody,
|
update: req.validatedBody,
|
||||||
@@ -46,7 +47,19 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ customer: result[0] as AdminCustomerResponse["customer"] })
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "customer",
|
||||||
|
variables: {
|
||||||
|
filters: { id: req.params.id },
|
||||||
|
},
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [customer] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ customer })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DELETE = async (
|
export const DELETE = async (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const defaultAdminCustomerFields = [
|
|||||||
"first_name",
|
"first_name",
|
||||||
"last_name",
|
"last_name",
|
||||||
"email",
|
"email",
|
||||||
|
"phone",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
"deleted_at",
|
"deleted_at",
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ export const AdminCustomersParams = createFindParams({
|
|||||||
q: z.string().optional(),
|
q: z.string().optional(),
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
email: z.union([z.string(), z.array(z.string())]).optional(),
|
email: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
groups: z.union([
|
groups: z
|
||||||
|
.union([
|
||||||
AdminCustomerGroupInCustomerParams,
|
AdminCustomerGroupInCustomerParams,
|
||||||
z.string(),
|
z.string(),
|
||||||
z.array(z.string()),
|
z.array(z.string()),
|
||||||
]).optional(),
|
])
|
||||||
|
.optional(),
|
||||||
company_name: z.union([z.string(), z.array(z.string())]).optional(),
|
company_name: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
first_name: z.union([z.string(), z.array(z.string())]).optional(),
|
first_name: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
last_name: z.union([z.string(), z.array(z.string())]).optional(),
|
last_name: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { PaginatedResponse } from "../../../common"
|
|||||||
export interface CustomerGroupResponse {
|
export interface CustomerGroupResponse {
|
||||||
id: string
|
id: string
|
||||||
name: string | null
|
name: string | null
|
||||||
|
customers: CustomerResponse[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in new issue
Block a user