feat(dashboard): manage groups from customer details (#7346)
This commit is contained in:
1 parent
05e42bc6fc
commit
ce75755ac6
11 files changed
+450
-20
No files matched your search
@@ -18,7 +18,9 @@ import { EditCustomerGroupSchema } from "../../v2-routes/customer-groups/custome
|
|||||||
import { customersQueryKeys } from "./customers"
|
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)
|
export const customerGroupsQueryKeys = queryKeysFactory(
|
||||||
|
CUSTOMER_GROUPS_QUERY_KEY
|
||||||
|
)
|
||||||
|
|
||||||
export const useCustomerGroup = (
|
export const useCustomerGroup = (
|
||||||
id: string,
|
id: string,
|
||||||
@@ -162,7 +164,7 @@ export const useRemoveCustomersFromGroup = (
|
|||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
AdminCustomerGroupResponse,
|
AdminCustomerGroupResponse,
|
||||||
Error,
|
Error,
|
||||||
{ customer_ids: { id: string }[] }
|
{ customer_ids: string[] }
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ export const useCustomerTableFilters = (
|
|||||||
if (customer_groups && !isGroupsExcluded) {
|
if (customer_groups && !isGroupsExcluded) {
|
||||||
const customerGroupFilter: Filter = {
|
const customerGroupFilter: Filter = {
|
||||||
key: "groups",
|
key: "groups",
|
||||||
label: t("customers.groups"),
|
label: t("customers.groups.label"),
|
||||||
type: "select",
|
type: "select",
|
||||||
multiple: true,
|
multiple: true,
|
||||||
options: customer_groups.map((s) => ({
|
options: customer_groups.map((s) => ({
|
||||||
|
|||||||
@@ -414,6 +414,12 @@
|
|||||||
"hint": "Create a new customer to manage their details.",
|
"hint": "Create a new customer to manage their details.",
|
||||||
"successToast": "Customer {{email}} was successfully created."
|
"successToast": "Customer {{email}} was successfully created."
|
||||||
},
|
},
|
||||||
|
"groups": {
|
||||||
|
"label": "Customer groups",
|
||||||
|
"remove": "Are you sure you want to remove the customer from \"{{name}}\" customer group?",
|
||||||
|
"removeMany": "Are you sure you want to customer from following customer groups: {{groups}}?",
|
||||||
|
"alreadyAddedTooltip": "The customer is already in this customer group."
|
||||||
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
"header": "Edit Customer",
|
"header": "Edit Customer",
|
||||||
"emailDisabledTooltip": "The email address cannot be changed for registered customers.",
|
"emailDisabledTooltip": "The email address cannot be changed for registered customers.",
|
||||||
@@ -428,7 +434,9 @@
|
|||||||
"guest": "Guest",
|
"guest": "Guest",
|
||||||
"registered": "Registered",
|
"registered": "Registered",
|
||||||
"groups": "Groups"
|
"groups": "Groups"
|
||||||
}
|
},
|
||||||
|
"registered": "Registered",
|
||||||
|
"guest": "Guest"
|
||||||
},
|
},
|
||||||
"customerGroups": {
|
"customerGroups": {
|
||||||
"domain": "Customer Groups",
|
"domain": "Customer Groups",
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ async function batchAddCustomers(
|
|||||||
|
|
||||||
async function batchRemoveCustomers(
|
async function batchRemoveCustomers(
|
||||||
id: string,
|
id: string,
|
||||||
payload: { customer_ids: { id: string }[] }
|
payload: { customer_ids: string[] }
|
||||||
) {
|
) {
|
||||||
return postRequest<AdminCustomerGroupResponse>(
|
return postRequest<AdminCustomerGroupResponse>(
|
||||||
`/admin/customer-groups/${id}/customers`,
|
`/admin/customer-groups/${id}/customers`,
|
||||||
|
|||||||
@@ -367,6 +367,13 @@ export const RouteMap: RouteObject[] = [
|
|||||||
lazy: () =>
|
lazy: () =>
|
||||||
import("../../v2-routes/customers/customer-edit"),
|
import("../../v2-routes/customers/customer-edit"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "add-customer-groups",
|
||||||
|
lazy: () =>
|
||||||
|
import(
|
||||||
|
"../../v2-routes/customers/customers-add-customer-group"
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
+149
-11
@@ -2,21 +2,35 @@ import {
|
|||||||
AdminCustomerGroupResponse,
|
AdminCustomerGroupResponse,
|
||||||
AdminCustomerResponse,
|
AdminCustomerResponse,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { Container, Heading } from "@medusajs/ui"
|
import {
|
||||||
import { createColumnHelper } from "@tanstack/react-table"
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Container,
|
||||||
|
Heading,
|
||||||
|
toast,
|
||||||
|
usePrompt,
|
||||||
|
} from "@medusajs/ui"
|
||||||
|
import { createColumnHelper, RowSelectionState } from "@tanstack/react-table"
|
||||||
import { t } from "i18next"
|
import { t } from "i18next"
|
||||||
import { useMemo } from "react"
|
import { useMemo, useState } from "react"
|
||||||
|
|
||||||
import { PencilSquare } from "@medusajs/icons"
|
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||||
import { keepPreviousData } from "@tanstack/react-query"
|
import { keepPreviousData } from "@tanstack/react-query"
|
||||||
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 { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
import { useCustomerGroups } from "../../../../../hooks/api/customer-groups"
|
import {
|
||||||
|
customerGroupsQueryKeys,
|
||||||
|
useCustomerGroups,
|
||||||
|
useRemoveCustomersFromGroup,
|
||||||
|
} from "../../../../../hooks/api/customer-groups"
|
||||||
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
|
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
|
||||||
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
|
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
|
||||||
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
|
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
|
||||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
|
import { Link } from "react-router-dom"
|
||||||
|
import { client } from "../../../../../lib/client"
|
||||||
|
import { queryClient } from "../../../../../lib/medusa.ts"
|
||||||
|
|
||||||
type CustomerGroupSectionProps = {
|
type CustomerGroupSectionProps = {
|
||||||
customer: AdminCustomerResponse["customer"]
|
customer: AdminCustomerResponse["customer"]
|
||||||
@@ -27,9 +41,13 @@ const PAGE_SIZE = 10
|
|||||||
export const CustomerGroupSection = ({
|
export const CustomerGroupSection = ({
|
||||||
customer,
|
customer,
|
||||||
}: CustomerGroupSectionProps) => {
|
}: CustomerGroupSectionProps) => {
|
||||||
|
const prompt = usePrompt()
|
||||||
|
|
||||||
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
const { raw, searchParams } = useCustomerGroupTableQuery({
|
const { raw, searchParams } = useCustomerGroupTableQuery({
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { customer_groups, count, isLoading, isError, error } =
|
const { customer_groups, count, isLoading, isError, error } =
|
||||||
useCustomerGroups(
|
useCustomerGroups(
|
||||||
{
|
{
|
||||||
@@ -43,7 +61,7 @@ export const CustomerGroupSection = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const filters = useCustomerGroupTableFilters()
|
const filters = useCustomerGroupTableFilters()
|
||||||
const columns = useColumns()
|
const columns = useColumns(customer.id)
|
||||||
|
|
||||||
const { table } = useDataTable({
|
const { table } = useDataTable({
|
||||||
data: customer_groups ?? [],
|
data: customer_groups ?? [],
|
||||||
@@ -53,10 +71,52 @@ export const CustomerGroupSection = ({
|
|||||||
enablePagination: true,
|
enablePagination: true,
|
||||||
enableRowSelection: true,
|
enableRowSelection: true,
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater: setRowSelection,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isError) {
|
const handleRemove = async () => {
|
||||||
throw error
|
const customerGroupIds = Object.keys(rowSelection)
|
||||||
|
|
||||||
|
const res = await prompt({
|
||||||
|
title: t("general.areYouSure"),
|
||||||
|
description: t("customers.groups.removeMany", {
|
||||||
|
groups: customer_groups
|
||||||
|
.filter((g) => customerGroupIds.includes(g.id))
|
||||||
|
.map((g) => g.name)
|
||||||
|
.join(","),
|
||||||
|
}),
|
||||||
|
confirmText: t("actions.remove"),
|
||||||
|
cancelText: t("actions.cancel"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* TODO: use this for now until add customer groups to customers batch is implemented
|
||||||
|
*/
|
||||||
|
const promises = customerGroupIds.map((id) =>
|
||||||
|
client.customerGroups.removeCustomers(id, {
|
||||||
|
customer_ids: [customer.id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("general.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
@@ -67,6 +127,11 @@ export const CustomerGroupSection = ({
|
|||||||
<Container className="divide-y p-0">
|
<Container className="divide-y p-0">
|
||||||
<div className="flex items-center justify-between px-6 py-4">
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
<Heading level="h2">{t("customerGroups.domain")}</Heading>
|
<Heading level="h2">{t("customerGroups.domain")}</Heading>
|
||||||
|
<Link to={`/customers/${customer.id}/add-customer-groups`}>
|
||||||
|
<Button variant="secondary" size="small">
|
||||||
|
{t("general.add")}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<DataTable
|
<DataTable
|
||||||
table={table}
|
table={table}
|
||||||
@@ -79,20 +144,55 @@ export const CustomerGroupSection = ({
|
|||||||
search
|
search
|
||||||
pagination
|
pagination
|
||||||
orderBy={["name", "created_at", "updated_at"]}
|
orderBy={["name", "created_at", "updated_at"]}
|
||||||
|
commands={[
|
||||||
|
{
|
||||||
|
action: handleRemove,
|
||||||
|
label: t("actions.remove"),
|
||||||
|
shortcut: "r",
|
||||||
|
},
|
||||||
|
]}
|
||||||
queryObject={raw}
|
queryObject={raw}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add remove association when /customer-groups/:id/batch has been created.
|
|
||||||
const CustomerGroupRowActions = ({
|
const CustomerGroupRowActions = ({
|
||||||
group,
|
group,
|
||||||
|
customerId,
|
||||||
}: {
|
}: {
|
||||||
group: AdminCustomerGroupResponse["customer_group"]
|
group: AdminCustomerGroupResponse["customer_group"]
|
||||||
|
customerId: string
|
||||||
}) => {
|
}) => {
|
||||||
|
const prompt = usePrompt()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const { mutateAsync } = useRemoveCustomersFromGroup(group.id)
|
||||||
|
|
||||||
|
const onRemove = async () => {
|
||||||
|
const res = await prompt({
|
||||||
|
title: t("general.areYouSure"),
|
||||||
|
description: t("customers.groups.remove", {
|
||||||
|
name: group.name,
|
||||||
|
}),
|
||||||
|
confirmText: t("actions.remove"),
|
||||||
|
cancelText: t("actions.cancel"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mutateAsync({ customer_ids: [customerId] })
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("general.close"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionMenu
|
<ActionMenu
|
||||||
groups={[
|
groups={[
|
||||||
@@ -103,6 +203,11 @@ const CustomerGroupRowActions = ({
|
|||||||
icon: <PencilSquare />,
|
icon: <PencilSquare />,
|
||||||
to: `/customer-groups/${group.id}/edit`,
|
to: `/customer-groups/${group.id}/edit`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t("actions.remove"),
|
||||||
|
onClick: onRemove,
|
||||||
|
icon: <Trash />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
@@ -113,15 +218,48 @@ const CustomerGroupRowActions = ({
|
|||||||
const columnHelper =
|
const columnHelper =
|
||||||
createColumnHelper<AdminCustomerGroupResponse["customer_group"]>()
|
createColumnHelper<AdminCustomerGroupResponse["customer_group"]>()
|
||||||
|
|
||||||
const useColumns = () => {
|
const useColumns = (customerId: string) => {
|
||||||
const columns = useCustomerGroupTableColumns()
|
const columns = useCustomerGroupTableColumns()
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
columnHelper.display({
|
||||||
|
id: "select",
|
||||||
|
header: ({ table }) => {
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
table.getIsSomePageRowsSelected()
|
||||||
|
? "indeterminate"
|
||||||
|
: table.getIsAllPageRowsSelected()
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
table.toggleAllPageRowsSelected(!!value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}),
|
||||||
...columns,
|
...columns,
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: "actions",
|
id: "actions",
|
||||||
cell: ({ row }) => <CustomerGroupRowActions group={row.original} />,
|
cell: ({ row }) => (
|
||||||
|
<CustomerGroupRowActions
|
||||||
|
group={row.original}
|
||||||
|
customerId={customerId}
|
||||||
|
/>
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[columns]
|
[columns]
|
||||||
|
|||||||
+254
@@ -0,0 +1,254 @@
|
|||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { Button, Checkbox, Hint, Tooltip, toast } from "@medusajs/ui"
|
||||||
|
import {
|
||||||
|
OnChangeFn,
|
||||||
|
RowSelectionState,
|
||||||
|
createColumnHelper,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
import { useEffect, useMemo, useState } from "react"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import * as zod from "zod"
|
||||||
|
|
||||||
|
import { AdminCustomerGroupResponse } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
RouteFocusModal,
|
||||||
|
useRouteModal,
|
||||||
|
} from "../../../../../components/route-modal"
|
||||||
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
|
import {
|
||||||
|
useCustomerGroups,
|
||||||
|
customerGroupsQueryKeys,
|
||||||
|
} from "../../../../../hooks/api/customer-groups"
|
||||||
|
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||||
|
import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns/use-customer-group-table-columns"
|
||||||
|
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
|
||||||
|
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
|
||||||
|
import { client } from "../../../../../lib/client"
|
||||||
|
import { queryClient } from "../../../../../lib/medusa"
|
||||||
|
|
||||||
|
type AddCustomerGroupsFormProps = {
|
||||||
|
customerId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddCustomerGroupsSchema = zod.object({
|
||||||
|
customer_group_ids: zod.array(zod.string()).min(1),
|
||||||
|
})
|
||||||
|
|
||||||
|
const PAGE_SIZE = 10
|
||||||
|
|
||||||
|
export const AddCustomerGroupsForm = ({
|
||||||
|
customerId,
|
||||||
|
}: AddCustomerGroupsFormProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { handleSuccess } = useRouteModal()
|
||||||
|
const [isPending, setIsPending] = useState(false)
|
||||||
|
|
||||||
|
const form = useForm<zod.infer<typeof AddCustomerGroupsSchema>>({
|
||||||
|
defaultValues: {
|
||||||
|
customer_group_ids: [],
|
||||||
|
},
|
||||||
|
resolver: zodResolver(AddCustomerGroupsSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { setValue } = form
|
||||||
|
|
||||||
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(
|
||||||
|
"customer_group_ids",
|
||||||
|
Object.keys(rowSelection).filter((k) => rowSelection[k]),
|
||||||
|
{
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}, [rowSelection, setValue])
|
||||||
|
|
||||||
|
const { searchParams, raw } = useCustomerGroupTableQuery({
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
})
|
||||||
|
const filters = useCustomerGroupTableFilters()
|
||||||
|
|
||||||
|
const {
|
||||||
|
customer_groups,
|
||||||
|
count,
|
||||||
|
isPending: isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useCustomerGroups({
|
||||||
|
fields: "*customers",
|
||||||
|
...searchParams,
|
||||||
|
})
|
||||||
|
|
||||||
|
const updater: OnChangeFn<RowSelectionState> = (fn) => {
|
||||||
|
const state = typeof fn === "function" ? fn(rowSelection) : fn
|
||||||
|
|
||||||
|
const ids = Object.keys(state)
|
||||||
|
|
||||||
|
setValue("customer_group_ids", ids, {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
setRowSelection(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = useColumns()
|
||||||
|
|
||||||
|
const { table } = useDataTable({
|
||||||
|
data: customer_groups ?? [],
|
||||||
|
columns,
|
||||||
|
count,
|
||||||
|
enablePagination: true,
|
||||||
|
enableRowSelection: (row) => {
|
||||||
|
return !row.original.customers?.map((c) => c.id).includes(customerId)
|
||||||
|
},
|
||||||
|
getRowId: (row) => row.id,
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
rowSelection: {
|
||||||
|
state: rowSelection,
|
||||||
|
updater,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSubmit = form.handleSubmit(async (data) => {
|
||||||
|
setIsPending(true)
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* TODO: use this for now until add customer groups to customers batch is implemented
|
||||||
|
*/
|
||||||
|
const promises = data.customer_group_ids.map((id) =>
|
||||||
|
client.customerGroups.addCustomers(id, { customer_ids: [customerId] })
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: customerGroupsQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
|
||||||
|
handleSuccess(`/customers/${customerId}`)
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("general.error"), {
|
||||||
|
description: e.message,
|
||||||
|
dismissLabel: t("actions.close"),
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setIsPending(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RouteFocusModal.Form form={form}>
|
||||||
|
<form
|
||||||
|
className="flex h-full flex-col overflow-hidden"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<RouteFocusModal.Header>
|
||||||
|
<div className="flex items-center justify-end gap-x-2">
|
||||||
|
{form.formState.errors.customer_group_ids && (
|
||||||
|
<Hint variant="error">
|
||||||
|
{form.formState.errors.customer_group_ids.message}
|
||||||
|
</Hint>
|
||||||
|
)}
|
||||||
|
<RouteFocusModal.Close asChild>
|
||||||
|
<Button variant="secondary" size="small">
|
||||||
|
{t("actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</RouteFocusModal.Close>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="primary"
|
||||||
|
size="small"
|
||||||
|
isLoading={isPending}
|
||||||
|
>
|
||||||
|
{t("actions.save")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</RouteFocusModal.Header>
|
||||||
|
<RouteFocusModal.Body className="size-full overflow-hidden">
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
count={count}
|
||||||
|
filters={filters}
|
||||||
|
orderBy={["name", "created_at", "updated_at"]}
|
||||||
|
isLoading={isLoading}
|
||||||
|
layout="fill"
|
||||||
|
search
|
||||||
|
queryObject={raw}
|
||||||
|
/>
|
||||||
|
</RouteFocusModal.Body>
|
||||||
|
</form>
|
||||||
|
</RouteFocusModal.Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnHelper =
|
||||||
|
createColumnHelper<AdminCustomerGroupResponse["customer_group"]>()
|
||||||
|
|
||||||
|
const useColumns = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const base = useCustomerGroupTableColumns()
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.display({
|
||||||
|
id: "select",
|
||||||
|
header: ({ table }) => {
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
table.getIsSomePageRowsSelected()
|
||||||
|
? "indeterminate"
|
||||||
|
: table.getIsAllPageRowsSelected()
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
table.toggleAllPageRowsSelected(!!value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isPreSelected = !row.getCanSelect()
|
||||||
|
const isSelected = row.getIsSelected() || isPreSelected
|
||||||
|
|
||||||
|
const Component = (
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
disabled={isPreSelected}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isPreSelected) {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content={t("customers.groups.alreadyAddedTooltip")}
|
||||||
|
side="right"
|
||||||
|
>
|
||||||
|
{Component}
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Component
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...base,
|
||||||
|
],
|
||||||
|
[t, base]
|
||||||
|
)
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./add-customer-groups-form"
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import { useParams } from "react-router-dom"
|
||||||
|
import { RouteFocusModal } from "../../../components/route-modal"
|
||||||
|
import { AddCustomerGroupsForm } from "./components/add-customers-form"
|
||||||
|
|
||||||
|
export const CustomerAddCustomerGroups = () => {
|
||||||
|
const { id } = useParams()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RouteFocusModal>
|
||||||
|
<AddCustomerGroupsForm customerId={id!} />
|
||||||
|
</RouteFocusModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export { CustomerAddCustomerGroups as Component } from "./customer-add-customer-groups"
|
||||||
@@ -11,19 +11,25 @@ const usePrompt = () => {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let open = true
|
let open = true
|
||||||
|
|
||||||
|
const mountRoot = createRoot(document.createElement("div"))
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
open = false
|
open = false
|
||||||
render()
|
mountRoot.unmount()
|
||||||
resolve(false)
|
resolve(false)
|
||||||
|
|
||||||
|
// TEMP FIX for Radix issue with dropdowns persisting pointer-events: none on body after closing
|
||||||
|
document.body.style.pointerEvents = "auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
open = false
|
open = false
|
||||||
resolve(true)
|
resolve(true)
|
||||||
render()
|
mountRoot.unmount()
|
||||||
}
|
|
||||||
|
|
||||||
const mountRoot = createRoot(document.createElement("div"))
|
// TEMP FIX for Radix issue with dropdowns persisting pointer-events: none on body after closing
|
||||||
|
document.body.style.pointerEvents = "auto"
|
||||||
|
}
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
mountRoot.render(
|
mountRoot.render(
|
||||||
|
|||||||
Reference in new issue
Block a user