@open-mercato/core 0.6.1-develop.3260.1.dc719cac84 → 0.6.1-develop.3278.1.a4d26475e4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/domain_mapping/index.js +31 -0
- package/dist/generated/entities/domain_mapping/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +16 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +18 -17
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/customer_accounts/acl.js +2 -1
- package/dist/modules/customer_accounts/acl.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
- package/dist/modules/customer_accounts/api/domain-check.js +70 -0
- package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
- package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
- package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
- package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
- package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
- package/dist/modules/customer_accounts/api/login.js +14 -3
- package/dist/modules/customer_accounts/api/login.js.map +2 -2
- package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
- package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
- package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
- package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
- package/dist/modules/customer_accounts/api/signup.js +32 -5
- package/dist/modules/customer_accounts/api/signup.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
- package/dist/modules/customer_accounts/data/enrichers.js +78 -12
- package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
- package/dist/modules/customer_accounts/data/entities.js +85 -2
- package/dist/modules/customer_accounts/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/data/guards.js +130 -0
- package/dist/modules/customer_accounts/data/guards.js.map +7 -0
- package/dist/modules/customer_accounts/data/validators.js +19 -0
- package/dist/modules/customer_accounts/data/validators.js.map +2 -2
- package/dist/modules/customer_accounts/di.js +23 -1
- package/dist/modules/customer_accounts/di.js.map +2 -2
- package/dist/modules/customer_accounts/events.js +9 -2
- package/dist/modules/customer_accounts/events.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
- package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
- package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
- package/dist/modules/customer_accounts/lib/hostname.js +55 -0
- package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
- package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
- package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
- package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
- package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
- package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
- package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
- package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
- package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
- package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
- package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
- package/dist/modules/customer_accounts/notifications.client.js +76 -0
- package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
- package/dist/modules/customer_accounts/notifications.js +76 -0
- package/dist/modules/customer_accounts/notifications.js.map +2 -2
- package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
- package/dist/modules/customer_accounts/setup.js +48 -1
- package/dist/modules/customer_accounts/setup.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
- package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
- package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
- package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
- package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
- package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
- package/dist/modules/customers/api/companies/route.js +4 -9
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +4 -9
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +4 -9
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/api/utils.js +0 -12
- package/dist/modules/customers/api/utils.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +361 -284
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +378 -473
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +368 -286
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
- package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
- package/dist/modules/customers/widgets/injection-table.js +6 -0
- package/dist/modules/customers/widgets/injection-table.js.map +2 -2
- package/dist/modules/perspectives/services/perspectiveService.js +16 -2
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +1 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +31 -8
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/generated/entities/domain_mapping/index.ts +14 -0
- package/generated/entities.ids.generated.ts +2 -1
- package/generated/entity-fields-registry.ts +16 -0
- package/package.json +5 -5
- package/src/helpers/integration/salesUi.ts +27 -23
- package/src/modules/customer_accounts/acl.ts +1 -0
- package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
- package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
- package/src/modules/customer_accounts/api/domain-check.ts +83 -0
- package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
- package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
- package/src/modules/customer_accounts/api/login.ts +14 -3
- package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
- package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
- package/src/modules/customer_accounts/api/signup.ts +36 -5
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
- package/src/modules/customer_accounts/data/enrichers.ts +95 -12
- package/src/modules/customer_accounts/data/entities.ts +80 -1
- package/src/modules/customer_accounts/data/guards.ts +155 -0
- package/src/modules/customer_accounts/data/validators.ts +26 -0
- package/src/modules/customer_accounts/di.ts +35 -1
- package/src/modules/customer_accounts/events.ts +8 -1
- package/src/modules/customer_accounts/i18n/de.json +78 -0
- package/src/modules/customer_accounts/i18n/en.json +78 -0
- package/src/modules/customer_accounts/i18n/es.json +78 -0
- package/src/modules/customer_accounts/i18n/pl.json +78 -0
- package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
- package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
- package/src/modules/customer_accounts/lib/hostname.ts +76 -0
- package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
- package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
- package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
- package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
- package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
- package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
- package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
- package/src/modules/customer_accounts/notifications.client.ts +76 -0
- package/src/modules/customer_accounts/notifications.ts +76 -0
- package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
- package/src/modules/customer_accounts/setup.ts +81 -1
- package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
- package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
- package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
- package/src/modules/customers/api/companies/route.ts +4 -9
- package/src/modules/customers/api/deals/route.ts +4 -9
- package/src/modules/customers/api/people/route.ts +4 -9
- package/src/modules/customers/api/utils.ts +0 -14
- package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
- package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
- package/src/modules/customers/backend/customers/people/page.tsx +331 -240
- package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
- package/src/modules/customers/i18n/de.json +31 -0
- package/src/modules/customers/i18n/en.json +31 -0
- package/src/modules/customers/i18n/es.json +31 -0
- package/src/modules/customers/i18n/pl.json +31 -0
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
- package/src/modules/customers/widgets/injection-table.ts +6 -0
- package/src/modules/perspectives/services/perspectiveService.ts +30 -2
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +1 -0
- package/src/modules/query_index/lib/engine.ts +46 -8
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
|
-
import { useRouter } from "next/navigation";
|
|
5
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
6
6
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
7
|
import { DataTable, withDataTableNamespaces } from "@open-mercato/ui/backend/DataTable";
|
|
8
8
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
9
9
|
import { RowActions } from "@open-mercato/ui/backend/RowActions";
|
|
10
|
-
import { apiCall, apiCallOrThrow
|
|
10
|
+
import { apiCall, apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
11
11
|
import { buildCrudExportUrl } from "@open-mercato/ui/backend/utils/crud";
|
|
12
12
|
import { groupBulkDeleteFailures, runBulkDelete } from "@open-mercato/ui/backend/utils/bulkDelete";
|
|
13
13
|
import { coalesceLastOperations } from "@open-mercato/ui/backend/operations/store";
|
|
@@ -17,7 +17,9 @@ import { E } from "../../../../../generated/entities.ids.generated.js";
|
|
|
17
17
|
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
18
18
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
19
19
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
20
|
-
import {
|
|
20
|
+
import { createEmptyTree, makeRuleTree } from "@open-mercato/shared/lib/query/advanced-filter-tree";
|
|
21
|
+
import { deserializeAdvancedFilter, deserializeTree, flatToTree, mapDictionaryColorToTone, serializeTree } from "@open-mercato/shared/lib/query/advanced-filter";
|
|
22
|
+
import { useCurrentUserId } from "@open-mercato/ui/backend/utils/useCurrentUserId";
|
|
21
23
|
import {
|
|
22
24
|
DictionaryValue,
|
|
23
25
|
createEmptyCustomerDictionaryMaps,
|
|
@@ -32,9 +34,50 @@ import {
|
|
|
32
34
|
normalizeCustomFieldFilterOptions,
|
|
33
35
|
supportsCustomFieldColumn
|
|
34
36
|
} from "@open-mercato/ui/backend/utils/customFieldColumns";
|
|
37
|
+
import { useAutoDiscoveredFields } from "@open-mercato/ui/backend/utils/useAutoDiscoveredFields";
|
|
38
|
+
import { useAdvancedFilterTree } from "@open-mercato/ui/backend/hooks/useAdvancedFilter";
|
|
39
|
+
import { AdvancedFilterPanel } from "@open-mercato/ui/backend/filters/AdvancedFilterPanel";
|
|
40
|
+
import { ActiveFilterChips } from "@open-mercato/ui/backend/filters/ActiveFilterChips";
|
|
35
41
|
import { useQueryClient } from "@tanstack/react-query";
|
|
36
42
|
import { ensureCustomerDictionary } from "../../../components/detail/hooks/useCustomerDictionary.js";
|
|
43
|
+
import {
|
|
44
|
+
ensureCurrentUserFilterOption,
|
|
45
|
+
fetchAssignableStaffMembers,
|
|
46
|
+
mapAssignableStaffToFilterOptions
|
|
47
|
+
} from "../../../components/detail/assignableStaff.js";
|
|
37
48
|
import { CollectionPreviewCell, normalizeCollectionLabels } from "../../../components/list/CollectionPreviewCell.js";
|
|
49
|
+
function makePeoplePresets() {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
id: "recently-active",
|
|
53
|
+
labelKey: "customers.people.presets.recentlyActive",
|
|
54
|
+
iconName: "clock",
|
|
55
|
+
build: ({ now }) => {
|
|
56
|
+
const cutoff = new Date(now.getTime() - 7 * 24 * 3600 * 1e3).toISOString().slice(0, 10);
|
|
57
|
+
return makeRuleTree({ field: "next_interaction_at", operator: "is_after", value: cutoff });
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "my-contacts",
|
|
62
|
+
labelKey: "customers.people.presets.myContacts",
|
|
63
|
+
requiresUser: true,
|
|
64
|
+
build: ({ userId }) => makeRuleTree({ field: "owner_user_id", operator: "is", value: userId })
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "hot-leads",
|
|
68
|
+
labelKey: "customers.people.presets.hotLeads",
|
|
69
|
+
build: () => makeRuleTree({ field: "lifecycle_stage", operator: "is", value: "lead" })
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "stale-30",
|
|
73
|
+
labelKey: "customers.people.presets.stale30",
|
|
74
|
+
build: ({ now }) => {
|
|
75
|
+
const cutoff = new Date(now.getTime() - 30 * 24 * 3600 * 1e3).toISOString().slice(0, 10);
|
|
76
|
+
return makeRuleTree({ field: "next_interaction_at", operator: "is_before", value: cutoff });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
}
|
|
38
81
|
const NO_MATCH_TAG_SENTINEL = "__no_match__";
|
|
39
82
|
function formatDate(value, fallback) {
|
|
40
83
|
if (!value) return fallback;
|
|
@@ -109,13 +152,37 @@ function CustomersPeoplePage() {
|
|
|
109
152
|
const [total, setTotal] = React.useState(0);
|
|
110
153
|
const [totalPages, setTotalPages] = React.useState(1);
|
|
111
154
|
const [search, setSearch] = React.useState("");
|
|
112
|
-
const
|
|
113
|
-
const
|
|
155
|
+
const pathname = usePathname();
|
|
156
|
+
const searchParams = useSearchParams();
|
|
157
|
+
const initialFilterTree = React.useMemo(() => {
|
|
158
|
+
if (!searchParams) return createEmptyTree();
|
|
159
|
+
const record = {};
|
|
160
|
+
searchParams.forEach((value, key) => {
|
|
161
|
+
if (key.startsWith("filter[")) record[key] = value;
|
|
162
|
+
});
|
|
163
|
+
const v2 = deserializeTree(record);
|
|
164
|
+
if (v2) return v2;
|
|
165
|
+
const flat = deserializeAdvancedFilter(record);
|
|
166
|
+
if (flat) return flatToTree(flat);
|
|
167
|
+
return createEmptyTree();
|
|
168
|
+
}, []);
|
|
169
|
+
const [panelFields, setPanelFields] = React.useState([]);
|
|
170
|
+
const [filtersOpen, setFiltersOpen] = React.useState(false);
|
|
171
|
+
const filtersTriggerRef = React.useRef(null);
|
|
172
|
+
const filterPanel = useAdvancedFilterTree({
|
|
173
|
+
initial: initialFilterTree,
|
|
174
|
+
fields: panelFields,
|
|
175
|
+
onApply: () => setPage(1)
|
|
176
|
+
});
|
|
177
|
+
const advancedFilterState = filterPanel.appliedTree;
|
|
178
|
+
const handleAdvancedFilterClear = React.useCallback(() => {
|
|
179
|
+
filterPanel.clear();
|
|
180
|
+
setPage(1);
|
|
181
|
+
}, [filterPanel]);
|
|
114
182
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
115
183
|
const [reloadToken, setReloadToken] = React.useState(0);
|
|
116
184
|
const [cacheStatus, setCacheStatus] = React.useState(null);
|
|
117
185
|
const [dictionaryMaps, setDictionaryMaps] = React.useState(createEmptyCustomerDictionaryMaps());
|
|
118
|
-
const [tagIdToLabel, setTagIdToLabel] = React.useState({});
|
|
119
186
|
const scopeVersion = useOrganizationScopeVersion();
|
|
120
187
|
const queryClient = useQueryClient();
|
|
121
188
|
const t = useT();
|
|
@@ -146,65 +213,19 @@ function CustomersPeoplePage() {
|
|
|
146
213
|
return [];
|
|
147
214
|
}
|
|
148
215
|
}, [queryClient, scopeVersion]);
|
|
149
|
-
const loadDictionaryOptions = React.useCallback(async (kind) => {
|
|
150
|
-
const entries = await fetchDictionaryEntries(kind);
|
|
151
|
-
return entries.map((entry) => ({ value: entry.value, label: entry.label }));
|
|
152
|
-
}, [fetchDictionaryEntries]);
|
|
153
216
|
const dictionaryOptions = React.useMemo(() => {
|
|
154
|
-
const toOptions = (map) => Object.values(map ?? {}).map((entry) =>
|
|
217
|
+
const toOptions = (map) => Object.values(map ?? {}).map((entry) => {
|
|
218
|
+
const tone = mapDictionaryColorToTone(entry.color);
|
|
219
|
+
const option = { value: entry.value, label: entry.label };
|
|
220
|
+
if (tone) option.tone = tone;
|
|
221
|
+
return option;
|
|
222
|
+
}).sort((a, b) => a.label.localeCompare(b.label, void 0, { sensitivity: "base" }));
|
|
155
223
|
return {
|
|
156
224
|
statuses: toOptions(dictionaryMaps.statuses),
|
|
157
225
|
sources: toOptions(dictionaryMaps.sources),
|
|
158
226
|
lifecycleStages: toOptions(dictionaryMaps["lifecycle-stages"])
|
|
159
227
|
};
|
|
160
228
|
}, [dictionaryMaps]);
|
|
161
|
-
const loadTagOptions = React.useCallback(async (query) => {
|
|
162
|
-
try {
|
|
163
|
-
const params = new URLSearchParams({ pageSize: "100" });
|
|
164
|
-
const trimmedQuery = typeof query === "string" ? query.trim() : "";
|
|
165
|
-
if (trimmedQuery) params.set("search", trimmedQuery);
|
|
166
|
-
const payload = await readApiResultOrThrow(
|
|
167
|
-
`/api/customers/tags?${params.toString()}`,
|
|
168
|
-
void 0,
|
|
169
|
-
{ errorMessage: t("customers.people.detail.tags.loadError", "Failed to load tags.") }
|
|
170
|
-
);
|
|
171
|
-
const items = Array.isArray(payload?.items) ? payload.items : [];
|
|
172
|
-
const options = [];
|
|
173
|
-
for (const item of items) {
|
|
174
|
-
if (!item || typeof item !== "object") continue;
|
|
175
|
-
const raw = item;
|
|
176
|
-
const rawId = typeof raw.id === "string" ? raw.id : typeof raw.tagId === "string" ? raw.tagId : null;
|
|
177
|
-
if (!rawId) continue;
|
|
178
|
-
const label = typeof raw.label === "string" && raw.label.trim().length ? raw.label.trim() : typeof raw.slug === "string" && raw.slug.trim().length ? raw.slug.trim() : rawId;
|
|
179
|
-
options.push({ value: rawId, label });
|
|
180
|
-
}
|
|
181
|
-
if (options.length) {
|
|
182
|
-
setTagIdToLabel((prev) => {
|
|
183
|
-
let changed = false;
|
|
184
|
-
const next = { ...prev };
|
|
185
|
-
for (const option of options) {
|
|
186
|
-
if (next[option.value] !== option.label) {
|
|
187
|
-
next[option.value] = option.label;
|
|
188
|
-
changed = true;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return changed ? next : prev;
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
return options;
|
|
195
|
-
} catch (err) {
|
|
196
|
-
console.error("customers.people.list.loadTagOptions", err);
|
|
197
|
-
return [];
|
|
198
|
-
}
|
|
199
|
-
}, [setTagIdToLabel, t]);
|
|
200
|
-
const tagLabelToId = React.useMemo(() => {
|
|
201
|
-
const map = {};
|
|
202
|
-
for (const [id, label] of Object.entries(tagIdToLabel)) {
|
|
203
|
-
if (!label) continue;
|
|
204
|
-
map[label] = id;
|
|
205
|
-
}
|
|
206
|
-
return map;
|
|
207
|
-
}, [tagIdToLabel]);
|
|
208
229
|
React.useEffect(() => {
|
|
209
230
|
let cancelled = false;
|
|
210
231
|
async function loadAll() {
|
|
@@ -226,62 +247,33 @@ function CustomersPeoplePage() {
|
|
|
226
247
|
[E.customers.customer_entity, E.customers.customer_person_profile],
|
|
227
248
|
{ keyExtras: [scopeVersion, reloadToken] }
|
|
228
249
|
);
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
formatValue: (value) => tagIdToLabel[value] ?? value
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
id: "createdAt",
|
|
260
|
-
label: t("customers.people.list.filters.createdAt"),
|
|
261
|
-
type: "dateRange"
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
id: "emailContains",
|
|
265
|
-
label: t("customers.people.list.filters.emailContains"),
|
|
266
|
-
type: "text",
|
|
267
|
-
placeholder: t("customers.people.list.filters.emailContainsPlaceholder")
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
id: "hasEmail",
|
|
271
|
-
label: t("customers.people.list.filters.hasEmail"),
|
|
272
|
-
type: "checkbox"
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
id: "hasPhone",
|
|
276
|
-
label: t("customers.people.list.filters.hasPhone"),
|
|
277
|
-
type: "checkbox"
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
id: "hasNextInteraction",
|
|
281
|
-
label: t("customers.people.list.filters.hasNextInteraction"),
|
|
282
|
-
type: "checkbox"
|
|
283
|
-
}
|
|
284
|
-
], [dictionaryOptions.lifecycleStages, dictionaryOptions.sources, dictionaryOptions.statuses, loadDictionaryOptions, loadTagOptions, tagIdToLabel, t]);
|
|
250
|
+
const currentUserId = useCurrentUserId();
|
|
251
|
+
const [ownerFilterOptions, setOwnerFilterOptions] = React.useState([]);
|
|
252
|
+
React.useEffect(() => {
|
|
253
|
+
const controller = new AbortController();
|
|
254
|
+
let cancelled = false;
|
|
255
|
+
void fetchAssignableStaffMembers("", { pageSize: 100, signal: controller.signal }).then((items) => {
|
|
256
|
+
if (!cancelled) setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items));
|
|
257
|
+
}).catch(() => {
|
|
258
|
+
if (!cancelled) setOwnerFilterOptions([]);
|
|
259
|
+
});
|
|
260
|
+
return () => {
|
|
261
|
+
cancelled = true;
|
|
262
|
+
controller.abort();
|
|
263
|
+
};
|
|
264
|
+
}, [scopeVersion]);
|
|
265
|
+
const resolvedOwnerFilterOptions = React.useMemo(
|
|
266
|
+
() => ensureCurrentUserFilterOption(
|
|
267
|
+
ownerFilterOptions,
|
|
268
|
+
currentUserId,
|
|
269
|
+
t("customers.filters.currentUser", "Current user")
|
|
270
|
+
),
|
|
271
|
+
[currentUserId, ownerFilterOptions, t]
|
|
272
|
+
);
|
|
273
|
+
const loadOwnerFilterOptions = React.useCallback(async (query) => {
|
|
274
|
+
const items = await fetchAssignableStaffMembers(query ?? "", { pageSize: 100 });
|
|
275
|
+
return mapAssignableStaffToFilterOptions(items);
|
|
276
|
+
}, []);
|
|
285
277
|
const queryParams = React.useMemo(() => {
|
|
286
278
|
const params = new URLSearchParams();
|
|
287
279
|
params.set("page", String(page));
|
|
@@ -291,63 +283,32 @@ function CustomersPeoplePage() {
|
|
|
291
283
|
params.set("order", sorting[0].desc ? "desc" : "asc");
|
|
292
284
|
}
|
|
293
285
|
if (search.trim()) params.set("search", search.trim());
|
|
294
|
-
const
|
|
295
|
-
if (typeof status === "string" && status.trim()) params.set("status", status);
|
|
296
|
-
const source = filterValues.source;
|
|
297
|
-
if (typeof source === "string" && source.trim()) params.set("source", source);
|
|
298
|
-
const lifecycleStage = filterValues.lifecycleStage;
|
|
299
|
-
if (typeof lifecycleStage === "string" && lifecycleStage.trim()) params.set("lifecycleStage", lifecycleStage);
|
|
300
|
-
const createdAt = filterValues.createdAt;
|
|
301
|
-
if (createdAt && typeof createdAt === "object") {
|
|
302
|
-
if (createdAt.from) params.set("createdFrom", createdAt.from);
|
|
303
|
-
if (createdAt.to) params.set("createdTo", createdAt.to);
|
|
304
|
-
}
|
|
305
|
-
const emailContains = filterValues.emailContains;
|
|
306
|
-
if (typeof emailContains === "string" && emailContains.trim()) {
|
|
307
|
-
params.set("emailContains", emailContains.trim());
|
|
308
|
-
}
|
|
309
|
-
const booleanFilters = [
|
|
310
|
-
["hasEmail", "hasEmail"],
|
|
311
|
-
["hasPhone", "hasPhone"],
|
|
312
|
-
["hasNextInteraction", "hasNextInteraction"]
|
|
313
|
-
];
|
|
314
|
-
for (const [key, queryKey] of booleanFilters) {
|
|
315
|
-
const value = filterValues[key];
|
|
316
|
-
if (value === true) params.set(queryKey, "true");
|
|
317
|
-
if (value === false) params.set(queryKey, "false");
|
|
318
|
-
}
|
|
319
|
-
const tagValues = Array.isArray(filterValues.tagIds) ? filterValues.tagIds.map((value) => typeof value === "string" ? value.trim() : String(value || "").trim()).filter((value) => value.length > 0) : [];
|
|
320
|
-
if (tagValues.length > 0) {
|
|
321
|
-
const normalizedTagIds = tagValues.map((value) => typeof tagIdToLabel[value] === "string" ? value : tagLabelToId[value]).filter((id) => typeof id === "string" && id.length > 0);
|
|
322
|
-
if (normalizedTagIds.length === tagValues.length && normalizedTagIds.length > 0) {
|
|
323
|
-
params.set("tagIds", normalizedTagIds.join(","));
|
|
324
|
-
} else {
|
|
325
|
-
params.set("tagIdsEmpty", "true");
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
Object.entries(filterValues).forEach(([key, value]) => {
|
|
329
|
-
if (!key.startsWith("cf_") || value == null) return;
|
|
330
|
-
if (Array.isArray(value)) {
|
|
331
|
-
const normalized = value.map((item) => {
|
|
332
|
-
if (item == null) return "";
|
|
333
|
-
if (typeof item === "string") return item.trim();
|
|
334
|
-
return String(item).trim();
|
|
335
|
-
}).filter((item) => item.length > 0);
|
|
336
|
-
if (normalized.length) params.set(key, normalized.join(","));
|
|
337
|
-
} else if (typeof value === "object") {
|
|
338
|
-
return;
|
|
339
|
-
} else if (value !== "") {
|
|
340
|
-
const stringValue = typeof value === "string" ? value.trim() : String(value);
|
|
341
|
-
if (stringValue) params.set(key, stringValue);
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
const advancedParams = serializeAdvancedFilter(advancedFilterState);
|
|
286
|
+
const advancedParams = serializeTree(advancedFilterState);
|
|
345
287
|
for (const [key, val] of Object.entries(advancedParams)) {
|
|
346
288
|
params.set(key, val);
|
|
347
289
|
}
|
|
348
290
|
return params.toString();
|
|
349
|
-
}, [advancedFilterState,
|
|
291
|
+
}, [advancedFilterState, page, pageSize, search, sorting]);
|
|
350
292
|
const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams]);
|
|
293
|
+
const queryRef = React.useRef(searchParams?.toString() ?? "");
|
|
294
|
+
React.useEffect(() => {
|
|
295
|
+
if (!pathname) return;
|
|
296
|
+
const params = new URLSearchParams();
|
|
297
|
+
if (search.trim().length) params.set("search", search.trim());
|
|
298
|
+
if (page > 1) params.set("page", String(page));
|
|
299
|
+
if (sorting.length > 0) {
|
|
300
|
+
params.set("sort", sorting[0].id);
|
|
301
|
+
params.set("order", sorting[0].desc ? "desc" : "asc");
|
|
302
|
+
}
|
|
303
|
+
const advancedParams = serializeTree(advancedFilterState);
|
|
304
|
+
for (const [key, val] of Object.entries(advancedParams)) {
|
|
305
|
+
params.set(key, val);
|
|
306
|
+
}
|
|
307
|
+
const next = params.toString();
|
|
308
|
+
if (queryRef.current === next) return;
|
|
309
|
+
queryRef.current = next;
|
|
310
|
+
router.replace(next ? `${pathname}?${next}` : pathname, { scroll: false });
|
|
311
|
+
}, [pathname, router, page, search, sorting, advancedFilterState]);
|
|
351
312
|
const exportConfig = React.useMemo(() => ({
|
|
352
313
|
view: {
|
|
353
314
|
getUrl: (format) => buildCrudExportUrl("customers/people", { ...currentParams, exportScope: "view" }, format)
|
|
@@ -497,28 +458,6 @@ function CustomersPeoplePage() {
|
|
|
497
458
|
}
|
|
498
459
|
return succeeded.length > 0;
|
|
499
460
|
}, [bulkMutationContextId, confirm, retryBulkMutation, runBulkMutation, t]);
|
|
500
|
-
const handleFiltersApply = React.useCallback((values) => {
|
|
501
|
-
const next = {};
|
|
502
|
-
Object.entries(values).forEach(([key, value]) => {
|
|
503
|
-
if (value !== void 0) {
|
|
504
|
-
next[key] = value;
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
const rawTags = Array.isArray(values.tagIds) ? values.tagIds : [];
|
|
508
|
-
const sanitizedTags = rawTags.map((tag) => {
|
|
509
|
-
const normalized = typeof tag === "string" ? tag.trim() : "";
|
|
510
|
-
if (!normalized) return "";
|
|
511
|
-
return tagIdToLabel[normalized] ?? normalized;
|
|
512
|
-
}).filter((tag) => tag.length > 0);
|
|
513
|
-
if (sanitizedTags.length) next.tagIds = sanitizedTags;
|
|
514
|
-
else delete next.tagIds;
|
|
515
|
-
setFilterValues(next);
|
|
516
|
-
setPage(1);
|
|
517
|
-
}, [setFilterValues, setPage, tagIdToLabel]);
|
|
518
|
-
const handleFiltersClear = React.useCallback(() => {
|
|
519
|
-
setFilterValues({});
|
|
520
|
-
setPage(1);
|
|
521
|
-
}, [setFilterValues, setPage]);
|
|
522
461
|
const columns = React.useMemo(() => {
|
|
523
462
|
const noValue = /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: t("customers.people.list.noValue") });
|
|
524
463
|
const renderDictionaryCell = (kind, rawValue) => /* @__PURE__ */ jsx(
|
|
@@ -558,19 +497,36 @@ function CustomersPeoplePage() {
|
|
|
558
497
|
{
|
|
559
498
|
accessorKey: "name",
|
|
560
499
|
header: t("customers.people.list.columns.name"),
|
|
561
|
-
meta: {
|
|
500
|
+
meta: {
|
|
501
|
+
alwaysVisible: true,
|
|
502
|
+
columnChooserGroup: "Basic Info",
|
|
503
|
+
filterKey: "display_name",
|
|
504
|
+
filterGroup: "CRM",
|
|
505
|
+
maxWidth: "240px"
|
|
506
|
+
},
|
|
562
507
|
cell: ({ row }) => /* @__PURE__ */ jsx(Link, { href: `/backend/customers/people-v2/${row.original.id}`, className: "font-medium hover:underline", children: row.original.name })
|
|
563
508
|
},
|
|
564
509
|
{
|
|
565
510
|
accessorKey: "email",
|
|
566
511
|
header: t("customers.people.list.columns.email"),
|
|
567
|
-
meta: {
|
|
512
|
+
meta: {
|
|
513
|
+
columnChooserGroup: "Contact",
|
|
514
|
+
filterKey: "primary_email",
|
|
515
|
+
filterGroup: "Contact",
|
|
516
|
+
filterIconName: "mail",
|
|
517
|
+
maxWidth: "220px"
|
|
518
|
+
},
|
|
568
519
|
cell: ({ row }) => row.original.email || /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: t("customers.people.list.noValue") })
|
|
569
520
|
},
|
|
570
521
|
{
|
|
571
522
|
accessorKey: "status",
|
|
572
523
|
header: t("customers.people.list.columns.status"),
|
|
573
|
-
meta: {
|
|
524
|
+
meta: {
|
|
525
|
+
filterType: "select",
|
|
526
|
+
filterOptions: dictionaryOptions.statuses,
|
|
527
|
+
columnChooserGroup: "Basic Info",
|
|
528
|
+
filterGroup: "CRM"
|
|
529
|
+
},
|
|
574
530
|
cell: ({ row }) => renderDictionaryCell("statuses", row.original.status)
|
|
575
531
|
},
|
|
576
532
|
{
|
|
@@ -580,7 +536,8 @@ function CustomersPeoplePage() {
|
|
|
580
536
|
filterType: "select",
|
|
581
537
|
filterOptions: dictionaryOptions.lifecycleStages,
|
|
582
538
|
columnChooserGroup: "Basic Info",
|
|
583
|
-
filterKey: "lifecycle_stage"
|
|
539
|
+
filterKey: "lifecycle_stage",
|
|
540
|
+
filterGroup: "CRM"
|
|
584
541
|
},
|
|
585
542
|
cell: ({ row }) => renderDictionaryCell("lifecycle-stages", row.original.lifecycleStage)
|
|
586
543
|
},
|
|
@@ -590,6 +547,8 @@ function CustomersPeoplePage() {
|
|
|
590
547
|
meta: {
|
|
591
548
|
columnChooserGroup: "Dates",
|
|
592
549
|
filterKey: "next_interaction_at",
|
|
550
|
+
filterGroup: "Activity",
|
|
551
|
+
filterIconName: "calendar",
|
|
593
552
|
tooltipContent: (row) => {
|
|
594
553
|
if (!row.nextInteractionAt) return void 0;
|
|
595
554
|
const date = formatDate(row.nextInteractionAt, "");
|
|
@@ -609,67 +568,137 @@ function CustomersPeoplePage() {
|
|
|
609
568
|
{
|
|
610
569
|
accessorKey: "source",
|
|
611
570
|
header: t("customers.people.list.columns.source"),
|
|
612
|
-
meta: {
|
|
571
|
+
meta: {
|
|
572
|
+
filterType: "select",
|
|
573
|
+
filterOptions: dictionaryOptions.sources,
|
|
574
|
+
columnChooserGroup: "Basic Info",
|
|
575
|
+
filterGroup: "CRM"
|
|
576
|
+
},
|
|
613
577
|
cell: ({ row }) => renderDictionaryCell("sources", row.original.source)
|
|
614
578
|
},
|
|
579
|
+
{
|
|
580
|
+
accessorKey: "ownerUserId",
|
|
581
|
+
header: t("customers.people.list.columns.owner", "Owner"),
|
|
582
|
+
meta: {
|
|
583
|
+
columnChooserGroup: "CRM",
|
|
584
|
+
filterType: "select",
|
|
585
|
+
filterOptions: resolvedOwnerFilterOptions,
|
|
586
|
+
filterLoadOptions: loadOwnerFilterOptions,
|
|
587
|
+
filterGroup: "CRM",
|
|
588
|
+
filterIconName: "user-round",
|
|
589
|
+
filterKey: "owner_user_id",
|
|
590
|
+
hidden: true
|
|
591
|
+
},
|
|
592
|
+
cell: ({ row }) => row.original.ownerUserId ?? null
|
|
593
|
+
},
|
|
615
594
|
{
|
|
616
595
|
accessorKey: "firstName",
|
|
617
596
|
header: t("customers.people.form.firstName", "First name"),
|
|
618
|
-
meta: {
|
|
597
|
+
meta: {
|
|
598
|
+
columnChooserGroup: "Profile",
|
|
599
|
+
hidden: true,
|
|
600
|
+
filterKey: "person_profile.first_name",
|
|
601
|
+
filterGroup: "Profile"
|
|
602
|
+
},
|
|
619
603
|
cell: ({ row }) => row.original.firstName || noValue
|
|
620
604
|
},
|
|
621
605
|
{
|
|
622
606
|
accessorKey: "lastName",
|
|
623
607
|
header: t("customers.people.form.lastName", "Last name"),
|
|
624
|
-
meta: {
|
|
608
|
+
meta: {
|
|
609
|
+
columnChooserGroup: "Profile",
|
|
610
|
+
hidden: true,
|
|
611
|
+
filterKey: "person_profile.last_name",
|
|
612
|
+
filterGroup: "Profile"
|
|
613
|
+
},
|
|
625
614
|
cell: ({ row }) => row.original.lastName || noValue
|
|
626
615
|
},
|
|
627
616
|
{
|
|
628
617
|
accessorKey: "preferredName",
|
|
629
618
|
header: t("customers.people.form.preferredName", "Preferred name"),
|
|
630
|
-
meta: {
|
|
619
|
+
meta: {
|
|
620
|
+
columnChooserGroup: "Profile",
|
|
621
|
+
hidden: true,
|
|
622
|
+
filterKey: "person_profile.preferred_name",
|
|
623
|
+
filterGroup: "Profile"
|
|
624
|
+
},
|
|
631
625
|
cell: ({ row }) => row.original.preferredName || noValue
|
|
632
626
|
},
|
|
633
627
|
{
|
|
634
628
|
accessorKey: "jobTitle",
|
|
635
629
|
header: t("customers.people.form.jobTitle", "Job title"),
|
|
636
|
-
meta: {
|
|
630
|
+
meta: {
|
|
631
|
+
columnChooserGroup: "Profile",
|
|
632
|
+
hidden: true,
|
|
633
|
+
filterKey: "person_profile.job_title",
|
|
634
|
+
filterGroup: "Profile"
|
|
635
|
+
},
|
|
637
636
|
cell: ({ row }) => row.original.jobTitle || noValue
|
|
638
637
|
},
|
|
639
638
|
{
|
|
640
639
|
accessorKey: "department",
|
|
641
640
|
header: t("customers.people.detail.fields.department", "Department"),
|
|
642
|
-
meta: {
|
|
641
|
+
meta: {
|
|
642
|
+
columnChooserGroup: "Profile",
|
|
643
|
+
hidden: true,
|
|
644
|
+
filterKey: "person_profile.department",
|
|
645
|
+
filterGroup: "Profile"
|
|
646
|
+
},
|
|
643
647
|
cell: ({ row }) => row.original.department || noValue
|
|
644
648
|
},
|
|
645
649
|
{
|
|
646
650
|
accessorKey: "seniority",
|
|
647
651
|
header: t("customers.people.detail.fields.seniority", "Seniority"),
|
|
648
|
-
meta: {
|
|
652
|
+
meta: {
|
|
653
|
+
columnChooserGroup: "Profile",
|
|
654
|
+
hidden: true,
|
|
655
|
+
filterKey: "person_profile.seniority",
|
|
656
|
+
filterGroup: "Profile"
|
|
657
|
+
},
|
|
649
658
|
cell: ({ row }) => row.original.seniority || noValue
|
|
650
659
|
},
|
|
651
660
|
{
|
|
652
661
|
accessorKey: "timezone",
|
|
653
662
|
header: t("customers.people.detail.fields.timezone", "Timezone"),
|
|
654
|
-
meta: {
|
|
663
|
+
meta: {
|
|
664
|
+
columnChooserGroup: "Profile",
|
|
665
|
+
hidden: true,
|
|
666
|
+
filterKey: "person_profile.timezone",
|
|
667
|
+
filterGroup: "Profile"
|
|
668
|
+
},
|
|
655
669
|
cell: ({ row }) => row.original.timezone || noValue
|
|
656
670
|
},
|
|
657
671
|
{
|
|
658
672
|
accessorKey: "linkedInUrl",
|
|
659
673
|
header: t("customers.people.detail.fields.linkedIn", "LinkedIn"),
|
|
660
|
-
meta: {
|
|
674
|
+
meta: {
|
|
675
|
+
columnChooserGroup: "Socials",
|
|
676
|
+
hidden: true,
|
|
677
|
+
filterKey: "person_profile.linked_in_url",
|
|
678
|
+
filterGroup: "Socials"
|
|
679
|
+
},
|
|
661
680
|
cell: ({ row }) => row.original.linkedInUrl || noValue
|
|
662
681
|
},
|
|
663
682
|
{
|
|
664
683
|
accessorKey: "twitterUrl",
|
|
665
684
|
header: t("customers.people.detail.fields.twitter", "Twitter"),
|
|
666
|
-
meta: {
|
|
685
|
+
meta: {
|
|
686
|
+
columnChooserGroup: "Socials",
|
|
687
|
+
hidden: true,
|
|
688
|
+
filterKey: "person_profile.twitter_url",
|
|
689
|
+
filterGroup: "Socials"
|
|
690
|
+
},
|
|
667
691
|
cell: ({ row }) => row.original.twitterUrl || noValue
|
|
668
692
|
},
|
|
669
693
|
{
|
|
670
694
|
accessorKey: "description",
|
|
671
695
|
header: t("customers.people.form.description", "Description"),
|
|
672
|
-
meta: {
|
|
696
|
+
meta: {
|
|
697
|
+
columnChooserGroup: "Notes",
|
|
698
|
+
hidden: true,
|
|
699
|
+
filterKey: "description",
|
|
700
|
+
filterGroup: "Notes"
|
|
701
|
+
},
|
|
673
702
|
cell: ({ row }) => row.original.description || noValue
|
|
674
703
|
}
|
|
675
704
|
];
|
|
@@ -687,93 +716,146 @@ function CustomersPeoplePage() {
|
|
|
687
716
|
cell: ({ getValue }) => renderCustomFieldCell(getValue())
|
|
688
717
|
}));
|
|
689
718
|
return [...baseColumns, ...customColumns];
|
|
690
|
-
}, [customFieldDefs, dictionaryMaps, dictionaryOptions, t]);
|
|
719
|
+
}, [customFieldDefs, dictionaryMaps, dictionaryOptions, loadOwnerFilterOptions, resolvedOwnerFilterOptions, t]);
|
|
720
|
+
const { advancedFilterFields } = useAutoDiscoveredFields({ columns, customFieldDefs });
|
|
721
|
+
React.useEffect(() => {
|
|
722
|
+
setPanelFields((prev) => {
|
|
723
|
+
if (prev === advancedFilterFields) return prev;
|
|
724
|
+
if (prev.length === advancedFilterFields.length) {
|
|
725
|
+
let same = true;
|
|
726
|
+
for (let i = 0; i < prev.length; i++) {
|
|
727
|
+
if (prev[i].key !== advancedFilterFields[i].key) {
|
|
728
|
+
same = false;
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (same) return prev;
|
|
733
|
+
}
|
|
734
|
+
return advancedFilterFields;
|
|
735
|
+
});
|
|
736
|
+
}, [advancedFilterFields]);
|
|
737
|
+
const peoplePresets = React.useMemo(() => makePeoplePresets(), []);
|
|
691
738
|
return /* @__PURE__ */ jsxs(Page, { children: [
|
|
692
|
-
/* @__PURE__ */
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
739
|
+
/* @__PURE__ */ jsxs(PageBody, { children: [
|
|
740
|
+
/* @__PURE__ */ jsx(
|
|
741
|
+
DataTable,
|
|
742
|
+
{
|
|
743
|
+
stickyFirstColumn: true,
|
|
744
|
+
stickyActionsColumn: true,
|
|
745
|
+
title: t("customers.people.list.title"),
|
|
746
|
+
refreshButton: {
|
|
747
|
+
label: t("customers.people.list.actions.refresh"),
|
|
748
|
+
onRefresh: () => {
|
|
749
|
+
setSearch("");
|
|
750
|
+
setPage(1);
|
|
751
|
+
handleRefresh();
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
actions: /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/backend/customers/people/create", children: t("customers.people.list.actions.new") }) }),
|
|
755
|
+
columns,
|
|
756
|
+
columnChooser: { auto: true },
|
|
757
|
+
data: rows,
|
|
758
|
+
exporter: exportConfig,
|
|
759
|
+
searchValue: search,
|
|
760
|
+
onSearchChange: (value) => {
|
|
761
|
+
setSearch(value);
|
|
702
762
|
setPage(1);
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
id: "view",
|
|
741
|
-
label: t("customers.people.list.actions.view"),
|
|
742
|
-
onSelect: () => {
|
|
743
|
-
router.push(`/backend/customers/people-v2/${row.id}`);
|
|
763
|
+
},
|
|
764
|
+
searchPlaceholder: t("customers.people.list.searchPlaceholder"),
|
|
765
|
+
entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],
|
|
766
|
+
perspective: { tableId: "customers.people.list" },
|
|
767
|
+
onRowClick: (row) => router.push(`/backend/customers/people-v2/${row.id}`),
|
|
768
|
+
sortable: true,
|
|
769
|
+
sorting,
|
|
770
|
+
onSortingChange: setSorting,
|
|
771
|
+
bulkActions: [
|
|
772
|
+
{
|
|
773
|
+
id: "delete",
|
|
774
|
+
label: t("customers.people.list.bulkDelete.action", "Delete selected"),
|
|
775
|
+
destructive: true,
|
|
776
|
+
onExecute: handleBulkDelete
|
|
777
|
+
}
|
|
778
|
+
],
|
|
779
|
+
rowActions: (row) => /* @__PURE__ */ jsx(
|
|
780
|
+
RowActions,
|
|
781
|
+
{
|
|
782
|
+
items: [
|
|
783
|
+
{
|
|
784
|
+
id: "view",
|
|
785
|
+
label: t("customers.people.list.actions.view"),
|
|
786
|
+
onSelect: () => {
|
|
787
|
+
router.push(`/backend/customers/people-v2/${row.id}`);
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
id: "open-new-tab",
|
|
792
|
+
label: t("customers.people.list.actions.openInNewTab"),
|
|
793
|
+
onSelect: () => window.open(`/backend/customers/people-v2/${row.id}`, "_blank", "noopener")
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
id: "delete",
|
|
797
|
+
label: t("customers.people.list.actions.delete"),
|
|
798
|
+
destructive: true,
|
|
799
|
+
onSelect: () => handleDelete(row)
|
|
744
800
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
auto: true,
|
|
762
|
-
value: advancedFilterState,
|
|
763
|
-
onChange: setAdvancedFilterState,
|
|
764
|
-
onApply: () => {
|
|
765
|
-
setPage(1);
|
|
801
|
+
]
|
|
802
|
+
}
|
|
803
|
+
),
|
|
804
|
+
advancedFilter: {
|
|
805
|
+
auto: true,
|
|
806
|
+
value: filterPanel.tree,
|
|
807
|
+
onChange: filterPanel.setTree,
|
|
808
|
+
onApply: () => filterPanel.flush(),
|
|
809
|
+
onClear: handleAdvancedFilterClear,
|
|
810
|
+
triggerRef: filtersTriggerRef,
|
|
811
|
+
externalPopover: true,
|
|
812
|
+
onTriggerClick: () => setFiltersOpen((prev) => !prev),
|
|
813
|
+
onApplyTree: (tree) => {
|
|
814
|
+
filterPanel.replaceTree(tree);
|
|
815
|
+
setPage(1);
|
|
816
|
+
}
|
|
766
817
|
},
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
818
|
+
activeFilterChips: /* @__PURE__ */ jsx(
|
|
819
|
+
ActiveFilterChips,
|
|
820
|
+
{
|
|
821
|
+
tree: filterPanel.tree,
|
|
822
|
+
fields: advancedFilterFields,
|
|
823
|
+
popoverOpen: filtersOpen,
|
|
824
|
+
onRemoveNode: (id) => filterPanel.dispatch({ type: "removeNode", nodeId: id }),
|
|
825
|
+
onOpen: () => setFiltersOpen(true)
|
|
826
|
+
}
|
|
827
|
+
),
|
|
828
|
+
filterAwareEmptyState: {
|
|
829
|
+
active: advancedFilterState.root.children.length > 0,
|
|
830
|
+
entityNamePlural: t("customers.people.entityPlural", "people"),
|
|
831
|
+
canRemoveLast: filterPanel.tree.root.children.length > 0,
|
|
832
|
+
onClearAll: handleAdvancedFilterClear,
|
|
833
|
+
onRemoveLast: () => filterPanel.dispatch({ type: "removeLast" })
|
|
834
|
+
},
|
|
835
|
+
virtualized: true,
|
|
836
|
+
pagination: { page, pageSize, total, totalPages, onPageChange: setPage, cacheStatus, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange },
|
|
837
|
+
isLoading
|
|
838
|
+
}
|
|
839
|
+
),
|
|
840
|
+
/* @__PURE__ */ jsx(
|
|
841
|
+
AdvancedFilterPanel,
|
|
842
|
+
{
|
|
843
|
+
fields: advancedFilterFields,
|
|
844
|
+
value: filterPanel.tree,
|
|
845
|
+
onChange: filterPanel.setTree,
|
|
846
|
+
onApply: filterPanel.flush,
|
|
847
|
+
onClear: handleAdvancedFilterClear,
|
|
848
|
+
onFlush: filterPanel.flush,
|
|
849
|
+
pendingErrors: filterPanel.pendingErrors,
|
|
850
|
+
userId: currentUserId,
|
|
851
|
+
presets: peoplePresets,
|
|
852
|
+
open: filtersOpen,
|
|
853
|
+
onOpenChange: setFiltersOpen,
|
|
854
|
+
triggerRef: filtersTriggerRef,
|
|
855
|
+
savedFilterStorageKey: "customers.people.list"
|
|
856
|
+
}
|
|
857
|
+
)
|
|
858
|
+
] }),
|
|
777
859
|
ConfirmDialogElement
|
|
778
860
|
] });
|
|
779
861
|
}
|