@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,45 @@ 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 makeCompaniesPresets() {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
id: "my-accounts",
|
|
53
|
+
labelKey: "customers.companies.presets.myAccounts",
|
|
54
|
+
requiresUser: true,
|
|
55
|
+
build: ({ userId }) => makeRuleTree({ field: "owner_user_id", operator: "is", value: userId })
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "recently-created",
|
|
59
|
+
labelKey: "customers.companies.presets.recentlyCreated",
|
|
60
|
+
iconName: "clock",
|
|
61
|
+
build: ({ now }) => {
|
|
62
|
+
const cutoff = new Date(now.getTime() - 7 * 24 * 3600 * 1e3).toISOString().slice(0, 10);
|
|
63
|
+
return makeRuleTree({ field: "created_at", operator: "is_after", value: cutoff });
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "inactive-60",
|
|
68
|
+
labelKey: "customers.companies.presets.inactive60",
|
|
69
|
+
build: ({ now }) => {
|
|
70
|
+
const cutoff = new Date(now.getTime() - 60 * 24 * 3600 * 1e3).toISOString().slice(0, 10);
|
|
71
|
+
return makeRuleTree({ field: "next_interaction_at", operator: "is_before", value: cutoff });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
}
|
|
38
76
|
function formatDate(value, fallback) {
|
|
39
77
|
if (!value) return fallback;
|
|
40
78
|
const date = new Date(value);
|
|
@@ -63,6 +101,7 @@ function mapApiItem(item) {
|
|
|
63
101
|
const nextInteractionColor = typeof item.next_interaction_color === "string" ? item.next_interaction_color : null;
|
|
64
102
|
const organizationId = typeof item.organization_id === "string" ? item.organization_id : null;
|
|
65
103
|
const source = typeof item.source === "string" ? item.source : null;
|
|
104
|
+
const ownerUserId = typeof item.owner_user_id === "string" ? item.owner_user_id : null;
|
|
66
105
|
const customFields = {};
|
|
67
106
|
for (const [key, value] of Object.entries(item)) {
|
|
68
107
|
if (key.startsWith("cf_")) {
|
|
@@ -90,6 +129,7 @@ function mapApiItem(item) {
|
|
|
90
129
|
nextInteractionColor,
|
|
91
130
|
organizationId,
|
|
92
131
|
source,
|
|
132
|
+
ownerUserId,
|
|
93
133
|
...customFields
|
|
94
134
|
}, item);
|
|
95
135
|
}
|
|
@@ -102,13 +142,37 @@ function CustomersCompaniesPage() {
|
|
|
102
142
|
const [total, setTotal] = React.useState(0);
|
|
103
143
|
const [totalPages, setTotalPages] = React.useState(1);
|
|
104
144
|
const [search, setSearch] = React.useState("");
|
|
105
|
-
const
|
|
106
|
-
const
|
|
145
|
+
const pathname = usePathname();
|
|
146
|
+
const searchParams = useSearchParams();
|
|
147
|
+
const initialFilterTree = React.useMemo(() => {
|
|
148
|
+
if (!searchParams) return createEmptyTree();
|
|
149
|
+
const record = {};
|
|
150
|
+
searchParams.forEach((value, key) => {
|
|
151
|
+
if (key.startsWith("filter[")) record[key] = value;
|
|
152
|
+
});
|
|
153
|
+
const v2 = deserializeTree(record);
|
|
154
|
+
if (v2) return v2;
|
|
155
|
+
const flat = deserializeAdvancedFilter(record);
|
|
156
|
+
if (flat) return flatToTree(flat);
|
|
157
|
+
return createEmptyTree();
|
|
158
|
+
}, []);
|
|
159
|
+
const [panelFields, setPanelFields] = React.useState([]);
|
|
160
|
+
const [filtersOpen, setFiltersOpen] = React.useState(false);
|
|
161
|
+
const filtersTriggerRef = React.useRef(null);
|
|
162
|
+
const filterPanel = useAdvancedFilterTree({
|
|
163
|
+
initial: initialFilterTree,
|
|
164
|
+
fields: panelFields,
|
|
165
|
+
onApply: () => setPage(1)
|
|
166
|
+
});
|
|
167
|
+
const advancedFilterState = filterPanel.appliedTree;
|
|
168
|
+
const handleAdvancedFilterClear = React.useCallback(() => {
|
|
169
|
+
filterPanel.clear();
|
|
170
|
+
setPage(1);
|
|
171
|
+
}, [filterPanel]);
|
|
107
172
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
108
173
|
const [reloadToken, setReloadToken] = React.useState(0);
|
|
109
174
|
const [cacheStatus, setCacheStatus] = React.useState(null);
|
|
110
175
|
const [dictionaryMaps, setDictionaryMaps] = React.useState(createEmptyCustomerDictionaryMaps());
|
|
111
|
-
const [tagIdToLabel, setTagIdToLabel] = React.useState({});
|
|
112
176
|
const scopeVersion = useOrganizationScopeVersion();
|
|
113
177
|
const queryClient = useQueryClient();
|
|
114
178
|
const t = useT();
|
|
@@ -139,65 +203,19 @@ function CustomersCompaniesPage() {
|
|
|
139
203
|
return [];
|
|
140
204
|
}
|
|
141
205
|
}, [queryClient, scopeVersion]);
|
|
142
|
-
const loadDictionaryOptions = React.useCallback(async (kind) => {
|
|
143
|
-
const entries = await fetchDictionaryEntries(kind);
|
|
144
|
-
return entries.map((entry) => ({ value: entry.value, label: entry.label }));
|
|
145
|
-
}, [fetchDictionaryEntries]);
|
|
146
206
|
const dictionaryOptions = React.useMemo(() => {
|
|
147
|
-
const toOptions = (map) => Object.values(map ?? {}).map((entry) =>
|
|
207
|
+
const toOptions = (map) => Object.values(map ?? {}).map((entry) => {
|
|
208
|
+
const tone = mapDictionaryColorToTone(entry.color);
|
|
209
|
+
const option = { value: entry.value, label: entry.label };
|
|
210
|
+
if (tone) option.tone = tone;
|
|
211
|
+
return option;
|
|
212
|
+
}).sort((a, b) => a.label.localeCompare(b.label, void 0, { sensitivity: "base" }));
|
|
148
213
|
return {
|
|
149
214
|
statuses: toOptions(dictionaryMaps.statuses),
|
|
150
215
|
sources: toOptions(dictionaryMaps.sources),
|
|
151
216
|
lifecycleStages: toOptions(dictionaryMaps["lifecycle-stages"])
|
|
152
217
|
};
|
|
153
218
|
}, [dictionaryMaps]);
|
|
154
|
-
const loadTagOptions = React.useCallback(async (query) => {
|
|
155
|
-
try {
|
|
156
|
-
const params = new URLSearchParams({ pageSize: "100" });
|
|
157
|
-
const trimmedQuery = typeof query === "string" ? query.trim() : "";
|
|
158
|
-
if (trimmedQuery) params.set("search", trimmedQuery);
|
|
159
|
-
const payload = await readApiResultOrThrow(
|
|
160
|
-
`/api/customers/tags?${params.toString()}`,
|
|
161
|
-
void 0,
|
|
162
|
-
{ errorMessage: t("customers.companies.list.tags.loadError", "Failed to load tags.") }
|
|
163
|
-
);
|
|
164
|
-
const items = Array.isArray(payload?.items) ? payload.items : [];
|
|
165
|
-
const options = [];
|
|
166
|
-
for (const item of items) {
|
|
167
|
-
if (!item || typeof item !== "object") continue;
|
|
168
|
-
const raw = item;
|
|
169
|
-
const rawId = typeof raw.id === "string" ? raw.id : typeof raw.tagId === "string" ? raw.tagId : null;
|
|
170
|
-
if (!rawId) continue;
|
|
171
|
-
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;
|
|
172
|
-
options.push({ value: rawId, label });
|
|
173
|
-
}
|
|
174
|
-
if (options.length) {
|
|
175
|
-
setTagIdToLabel((prev) => {
|
|
176
|
-
let changed = false;
|
|
177
|
-
const next = { ...prev };
|
|
178
|
-
for (const option of options) {
|
|
179
|
-
if (next[option.value] !== option.label) {
|
|
180
|
-
next[option.value] = option.label;
|
|
181
|
-
changed = true;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return changed ? next : prev;
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
return options;
|
|
188
|
-
} catch (err) {
|
|
189
|
-
console.error("customers.companies.list.loadTagOptions", err);
|
|
190
|
-
return [];
|
|
191
|
-
}
|
|
192
|
-
}, [setTagIdToLabel, t]);
|
|
193
|
-
const tagLabelToId = React.useMemo(() => {
|
|
194
|
-
const map = {};
|
|
195
|
-
for (const [id, label] of Object.entries(tagIdToLabel)) {
|
|
196
|
-
if (!label) continue;
|
|
197
|
-
map[label] = id;
|
|
198
|
-
}
|
|
199
|
-
return map;
|
|
200
|
-
}, [tagIdToLabel]);
|
|
201
219
|
React.useEffect(() => {
|
|
202
220
|
let cancelled = false;
|
|
203
221
|
async function loadAll() {
|
|
@@ -219,62 +237,33 @@ function CustomersCompaniesPage() {
|
|
|
219
237
|
[E.customers.customer_entity, E.customers.customer_company_profile],
|
|
220
238
|
{ keyExtras: [scopeVersion, reloadToken] }
|
|
221
239
|
);
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
formatValue: (value) => tagIdToLabel[value] ?? value
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
id: "createdAt",
|
|
253
|
-
label: t("customers.companies.list.filters.createdAt"),
|
|
254
|
-
type: "dateRange"
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
id: "emailContains",
|
|
258
|
-
label: t("customers.companies.list.filters.emailContains"),
|
|
259
|
-
type: "text",
|
|
260
|
-
placeholder: t("customers.companies.list.filters.emailContainsPlaceholder")
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
id: "hasEmail",
|
|
264
|
-
label: t("customers.companies.list.filters.hasEmail"),
|
|
265
|
-
type: "checkbox"
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
id: "hasPhone",
|
|
269
|
-
label: t("customers.companies.list.filters.hasPhone"),
|
|
270
|
-
type: "checkbox"
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
id: "hasNextInteraction",
|
|
274
|
-
label: t("customers.companies.list.filters.hasNextInteraction"),
|
|
275
|
-
type: "checkbox"
|
|
276
|
-
}
|
|
277
|
-
], [dictionaryOptions.lifecycleStages, dictionaryOptions.sources, dictionaryOptions.statuses, loadDictionaryOptions, loadTagOptions, tagIdToLabel, t]);
|
|
240
|
+
const currentUserId = useCurrentUserId();
|
|
241
|
+
const [ownerFilterOptions, setOwnerFilterOptions] = React.useState([]);
|
|
242
|
+
React.useEffect(() => {
|
|
243
|
+
const controller = new AbortController();
|
|
244
|
+
let cancelled = false;
|
|
245
|
+
void fetchAssignableStaffMembers("", { pageSize: 100, signal: controller.signal }).then((items) => {
|
|
246
|
+
if (!cancelled) setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items));
|
|
247
|
+
}).catch(() => {
|
|
248
|
+
if (!cancelled) setOwnerFilterOptions([]);
|
|
249
|
+
});
|
|
250
|
+
return () => {
|
|
251
|
+
cancelled = true;
|
|
252
|
+
controller.abort();
|
|
253
|
+
};
|
|
254
|
+
}, [scopeVersion]);
|
|
255
|
+
const resolvedOwnerFilterOptions = React.useMemo(
|
|
256
|
+
() => ensureCurrentUserFilterOption(
|
|
257
|
+
ownerFilterOptions,
|
|
258
|
+
currentUserId,
|
|
259
|
+
t("customers.filters.currentUser", "Current user")
|
|
260
|
+
),
|
|
261
|
+
[currentUserId, ownerFilterOptions, t]
|
|
262
|
+
);
|
|
263
|
+
const loadOwnerFilterOptions = React.useCallback(async (query) => {
|
|
264
|
+
const items = await fetchAssignableStaffMembers(query ?? "", { pageSize: 100 });
|
|
265
|
+
return mapAssignableStaffToFilterOptions(items);
|
|
266
|
+
}, []);
|
|
278
267
|
const queryParams = React.useMemo(() => {
|
|
279
268
|
const params = new URLSearchParams();
|
|
280
269
|
params.set("page", String(page));
|
|
@@ -284,63 +273,32 @@ function CustomersCompaniesPage() {
|
|
|
284
273
|
params.set("order", sorting[0].desc ? "desc" : "asc");
|
|
285
274
|
}
|
|
286
275
|
if (search.trim()) params.set("search", search.trim());
|
|
287
|
-
const
|
|
288
|
-
if (typeof status === "string" && status.trim()) params.set("status", status);
|
|
289
|
-
const source = filterValues.source;
|
|
290
|
-
if (typeof source === "string" && source.trim()) params.set("source", source);
|
|
291
|
-
const lifecycleStage = filterValues.lifecycleStage;
|
|
292
|
-
if (typeof lifecycleStage === "string" && lifecycleStage.trim()) params.set("lifecycleStage", lifecycleStage);
|
|
293
|
-
const createdAt = filterValues.createdAt;
|
|
294
|
-
if (createdAt && typeof createdAt === "object") {
|
|
295
|
-
if (createdAt.from) params.set("createdFrom", createdAt.from);
|
|
296
|
-
if (createdAt.to) params.set("createdTo", createdAt.to);
|
|
297
|
-
}
|
|
298
|
-
const emailContains = filterValues.emailContains;
|
|
299
|
-
if (typeof emailContains === "string" && emailContains.trim()) {
|
|
300
|
-
params.set("emailContains", emailContains.trim());
|
|
301
|
-
}
|
|
302
|
-
const tagValues = Array.isArray(filterValues.tagIds) ? filterValues.tagIds.map((value) => typeof value === "string" ? value.trim() : String(value || "").trim()).filter((value) => value.length > 0) : [];
|
|
303
|
-
if (tagValues.length > 0) {
|
|
304
|
-
const normalizedTagIds = tagValues.map((value) => typeof tagIdToLabel[value] === "string" ? value : tagLabelToId[value]).filter((id) => typeof id === "string" && id.length > 0);
|
|
305
|
-
if (normalizedTagIds.length === tagValues.length && normalizedTagIds.length > 0) {
|
|
306
|
-
params.set("tagIds", normalizedTagIds.join(","));
|
|
307
|
-
} else {
|
|
308
|
-
params.set("tagIdsEmpty", "true");
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
const booleanFilters = [
|
|
312
|
-
["hasEmail", "hasEmail"],
|
|
313
|
-
["hasPhone", "hasPhone"],
|
|
314
|
-
["hasNextInteraction", "hasNextInteraction"]
|
|
315
|
-
];
|
|
316
|
-
for (const [key, queryKey] of booleanFilters) {
|
|
317
|
-
const value = filterValues[key];
|
|
318
|
-
if (value === true) params.set(queryKey, "true");
|
|
319
|
-
if (value === false) params.set(queryKey, "false");
|
|
320
|
-
}
|
|
321
|
-
Object.entries(filterValues).forEach(([key, value]) => {
|
|
322
|
-
if (!key.startsWith("cf_") || value == null) return;
|
|
323
|
-
if (Array.isArray(value)) {
|
|
324
|
-
const normalized = value.map((item) => {
|
|
325
|
-
if (item == null) return "";
|
|
326
|
-
if (typeof item === "string") return item.trim();
|
|
327
|
-
return String(item).trim();
|
|
328
|
-
}).filter((item) => item.length > 0);
|
|
329
|
-
if (normalized.length) params.set(key, normalized.join(","));
|
|
330
|
-
} else if (typeof value === "object") {
|
|
331
|
-
return;
|
|
332
|
-
} else if (value !== "") {
|
|
333
|
-
const stringValue = typeof value === "string" ? value.trim() : String(value);
|
|
334
|
-
if (stringValue) params.set(key, stringValue);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
const advancedParams = serializeAdvancedFilter(advancedFilterState);
|
|
276
|
+
const advancedParams = serializeTree(advancedFilterState);
|
|
338
277
|
for (const [key, val] of Object.entries(advancedParams)) {
|
|
339
278
|
params.set(key, val);
|
|
340
279
|
}
|
|
341
280
|
return params.toString();
|
|
342
|
-
}, [advancedFilterState,
|
|
281
|
+
}, [advancedFilterState, page, pageSize, search, sorting]);
|
|
343
282
|
const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams]);
|
|
283
|
+
const queryRef = React.useRef(searchParams?.toString() ?? "");
|
|
284
|
+
React.useEffect(() => {
|
|
285
|
+
if (!pathname) return;
|
|
286
|
+
const params = new URLSearchParams();
|
|
287
|
+
if (search.trim().length) params.set("search", search.trim());
|
|
288
|
+
if (page > 1) params.set("page", String(page));
|
|
289
|
+
if (sorting.length > 0) {
|
|
290
|
+
params.set("sort", sorting[0].id);
|
|
291
|
+
params.set("order", sorting[0].desc ? "desc" : "asc");
|
|
292
|
+
}
|
|
293
|
+
const advancedParams = serializeTree(advancedFilterState);
|
|
294
|
+
for (const [key, val] of Object.entries(advancedParams)) {
|
|
295
|
+
params.set(key, val);
|
|
296
|
+
}
|
|
297
|
+
const next = params.toString();
|
|
298
|
+
if (queryRef.current === next) return;
|
|
299
|
+
queryRef.current = next;
|
|
300
|
+
router.replace(next ? `${pathname}?${next}` : pathname, { scroll: false });
|
|
301
|
+
}, [pathname, router, page, search, sorting, advancedFilterState]);
|
|
344
302
|
const exportConfig = React.useMemo(() => ({
|
|
345
303
|
view: {
|
|
346
304
|
getUrl: (format) => buildCrudExportUrl("customers/companies", { ...currentParams, exportScope: "view" }, format)
|
|
@@ -489,26 +447,6 @@ function CustomersCompaniesPage() {
|
|
|
489
447
|
}
|
|
490
448
|
return succeeded.length > 0;
|
|
491
449
|
}, [bulkMutationContextId, confirm, retryBulkMutation, runBulkMutation, t]);
|
|
492
|
-
const handleFiltersApply = React.useCallback((values) => {
|
|
493
|
-
const next = {};
|
|
494
|
-
Object.entries(values).forEach(([key, value]) => {
|
|
495
|
-
if (value !== void 0) next[key] = value;
|
|
496
|
-
});
|
|
497
|
-
const rawTags = Array.isArray(values.tagIds) ? values.tagIds : [];
|
|
498
|
-
const sanitizedTags = rawTags.map((tag) => {
|
|
499
|
-
const normalized = typeof tag === "string" ? tag.trim() : "";
|
|
500
|
-
if (!normalized) return "";
|
|
501
|
-
return tagIdToLabel[normalized] ?? normalized;
|
|
502
|
-
}).filter((tag) => tag.length > 0);
|
|
503
|
-
if (sanitizedTags.length) next.tagIds = sanitizedTags;
|
|
504
|
-
else delete next.tagIds;
|
|
505
|
-
setFilterValues(next);
|
|
506
|
-
setPage(1);
|
|
507
|
-
}, [setFilterValues, setPage, tagIdToLabel]);
|
|
508
|
-
const handleFiltersClear = React.useCallback(() => {
|
|
509
|
-
setFilterValues({});
|
|
510
|
-
setPage(1);
|
|
511
|
-
}, [setFilterValues, setPage]);
|
|
512
450
|
const columns = React.useMemo(() => {
|
|
513
451
|
const noValue = /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: t("customers.companies.list.noValue") });
|
|
514
452
|
const renderDictionaryCell = (kind, rawValue) => /* @__PURE__ */ jsx(
|
|
@@ -548,25 +486,49 @@ function CustomersCompaniesPage() {
|
|
|
548
486
|
{
|
|
549
487
|
accessorKey: "name",
|
|
550
488
|
header: t("customers.companies.list.columns.name"),
|
|
551
|
-
meta: {
|
|
489
|
+
meta: {
|
|
490
|
+
alwaysVisible: true,
|
|
491
|
+
columnChooserGroup: "Basic Info",
|
|
492
|
+
filterKey: "display_name",
|
|
493
|
+
filterGroup: "CRM",
|
|
494
|
+
maxWidth: "260px"
|
|
495
|
+
},
|
|
552
496
|
cell: ({ row }) => /* @__PURE__ */ jsx(Link, { href: `/backend/customers/companies-v2/${row.original.id}`, className: "font-medium hover:underline", children: row.original.name })
|
|
553
497
|
},
|
|
554
498
|
{
|
|
555
499
|
accessorKey: "email",
|
|
556
500
|
header: t("customers.companies.list.columns.email"),
|
|
557
|
-
meta: {
|
|
501
|
+
meta: {
|
|
502
|
+
columnChooserGroup: "Contact",
|
|
503
|
+
filterKey: "primary_email",
|
|
504
|
+
filterGroup: "Contact",
|
|
505
|
+
filterIconName: "mail",
|
|
506
|
+
maxWidth: "220px"
|
|
507
|
+
},
|
|
558
508
|
cell: ({ row }) => row.original.email || noValue
|
|
559
509
|
},
|
|
560
510
|
{
|
|
561
511
|
accessorKey: "phone",
|
|
562
512
|
header: t("customers.companies.detail.highlights.primaryPhone", "Primary phone"),
|
|
563
|
-
meta: {
|
|
513
|
+
meta: {
|
|
514
|
+
columnChooserGroup: "Contact",
|
|
515
|
+
hidden: true,
|
|
516
|
+
filterKey: "primary_phone",
|
|
517
|
+
filterGroup: "Contact",
|
|
518
|
+
filterIconName: "phone",
|
|
519
|
+
maxWidth: "180px"
|
|
520
|
+
},
|
|
564
521
|
cell: ({ row }) => row.original.phone || noValue
|
|
565
522
|
},
|
|
566
523
|
{
|
|
567
524
|
accessorKey: "status",
|
|
568
525
|
header: t("customers.companies.list.columns.status"),
|
|
569
|
-
meta: {
|
|
526
|
+
meta: {
|
|
527
|
+
filterType: "select",
|
|
528
|
+
filterOptions: dictionaryOptions.statuses,
|
|
529
|
+
columnChooserGroup: "Basic Info",
|
|
530
|
+
filterGroup: "CRM"
|
|
531
|
+
},
|
|
570
532
|
cell: ({ row }) => renderDictionaryCell("statuses", row.original.status)
|
|
571
533
|
},
|
|
572
534
|
{
|
|
@@ -576,14 +538,20 @@ function CustomersCompaniesPage() {
|
|
|
576
538
|
filterType: "select",
|
|
577
539
|
filterOptions: dictionaryOptions.lifecycleStages,
|
|
578
540
|
columnChooserGroup: "Basic Info",
|
|
579
|
-
filterKey: "lifecycle_stage"
|
|
541
|
+
filterKey: "lifecycle_stage",
|
|
542
|
+
filterGroup: "CRM"
|
|
580
543
|
},
|
|
581
544
|
cell: ({ row }) => renderDictionaryCell("lifecycle-stages", row.original.lifecycleStage)
|
|
582
545
|
},
|
|
583
546
|
{
|
|
584
547
|
accessorKey: "nextInteractionAt",
|
|
585
548
|
header: t("customers.companies.list.columns.nextInteraction"),
|
|
586
|
-
meta: {
|
|
549
|
+
meta: {
|
|
550
|
+
columnChooserGroup: "Dates",
|
|
551
|
+
filterKey: "next_interaction_at",
|
|
552
|
+
filterGroup: "Activity",
|
|
553
|
+
filterIconName: "calendar"
|
|
554
|
+
},
|
|
587
555
|
cell: ({ row }) => row.original.nextInteractionAt ? /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-sm", children: [
|
|
588
556
|
row.original.nextInteractionIcon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card", children: renderDictionaryIcon(row.original.nextInteractionIcon, "h-4 w-4") }) : null,
|
|
589
557
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
@@ -596,43 +564,93 @@ function CustomersCompaniesPage() {
|
|
|
596
564
|
{
|
|
597
565
|
accessorKey: "source",
|
|
598
566
|
header: t("customers.companies.list.columns.source"),
|
|
599
|
-
meta: {
|
|
567
|
+
meta: {
|
|
568
|
+
filterType: "select",
|
|
569
|
+
filterOptions: dictionaryOptions.sources,
|
|
570
|
+
columnChooserGroup: "Basic Info",
|
|
571
|
+
filterGroup: "CRM"
|
|
572
|
+
},
|
|
600
573
|
cell: ({ row }) => renderDictionaryCell("sources", row.original.source)
|
|
601
574
|
},
|
|
575
|
+
{
|
|
576
|
+
accessorKey: "ownerUserId",
|
|
577
|
+
header: t("customers.companies.list.columns.owner", "Owner"),
|
|
578
|
+
meta: {
|
|
579
|
+
columnChooserGroup: "CRM",
|
|
580
|
+
filterType: "select",
|
|
581
|
+
filterOptions: resolvedOwnerFilterOptions,
|
|
582
|
+
filterLoadOptions: loadOwnerFilterOptions,
|
|
583
|
+
filterGroup: "CRM",
|
|
584
|
+
filterIconName: "user-round",
|
|
585
|
+
filterKey: "owner_user_id",
|
|
586
|
+
hidden: true
|
|
587
|
+
},
|
|
588
|
+
cell: ({ row }) => row.original.ownerUserId ?? null
|
|
589
|
+
},
|
|
602
590
|
{
|
|
603
591
|
accessorKey: "legalName",
|
|
604
592
|
header: t("customers.companies.detail.fields.legalName", "Legal name"),
|
|
605
|
-
meta: {
|
|
593
|
+
meta: {
|
|
594
|
+
columnChooserGroup: "Profile",
|
|
595
|
+
hidden: true,
|
|
596
|
+
filterKey: "company_profile.legal_name",
|
|
597
|
+
filterGroup: "Profile"
|
|
598
|
+
},
|
|
606
599
|
cell: ({ row }) => row.original.legalName || noValue
|
|
607
600
|
},
|
|
608
601
|
{
|
|
609
602
|
accessorKey: "brandName",
|
|
610
603
|
header: t("customers.companies.detail.fields.brandName", "Brand name"),
|
|
611
|
-
meta: {
|
|
604
|
+
meta: {
|
|
605
|
+
columnChooserGroup: "Profile",
|
|
606
|
+
hidden: true,
|
|
607
|
+
filterKey: "company_profile.brand_name",
|
|
608
|
+
filterGroup: "Profile"
|
|
609
|
+
},
|
|
612
610
|
cell: ({ row }) => row.original.brandName || noValue
|
|
613
611
|
},
|
|
614
612
|
{
|
|
615
613
|
accessorKey: "domain",
|
|
616
614
|
header: t("customers.companies.detail.fields.domain", "Domain"),
|
|
617
|
-
meta: {
|
|
615
|
+
meta: {
|
|
616
|
+
columnChooserGroup: "Profile",
|
|
617
|
+
hidden: true,
|
|
618
|
+
filterKey: "company_profile.domain",
|
|
619
|
+
filterGroup: "Profile"
|
|
620
|
+
},
|
|
618
621
|
cell: ({ row }) => row.original.domain || noValue
|
|
619
622
|
},
|
|
620
623
|
{
|
|
621
624
|
accessorKey: "websiteUrl",
|
|
622
625
|
header: t("customers.companies.detail.fields.website", "Website"),
|
|
623
|
-
meta: {
|
|
626
|
+
meta: {
|
|
627
|
+
columnChooserGroup: "Profile",
|
|
628
|
+
hidden: true,
|
|
629
|
+
filterKey: "company_profile.website_url",
|
|
630
|
+
filterGroup: "Profile"
|
|
631
|
+
},
|
|
624
632
|
cell: ({ row }) => row.original.websiteUrl || noValue
|
|
625
633
|
},
|
|
626
634
|
{
|
|
627
635
|
accessorKey: "industry",
|
|
628
636
|
header: t("customers.companies.detail.fields.industry", "Industry"),
|
|
629
|
-
meta: {
|
|
637
|
+
meta: {
|
|
638
|
+
columnChooserGroup: "Profile",
|
|
639
|
+
hidden: true,
|
|
640
|
+
filterKey: "company_profile.industry",
|
|
641
|
+
filterGroup: "Profile"
|
|
642
|
+
},
|
|
630
643
|
cell: ({ row }) => row.original.industry || noValue
|
|
631
644
|
},
|
|
632
645
|
{
|
|
633
646
|
accessorKey: "sizeBucket",
|
|
634
647
|
header: t("customers.companies.detail.fields.sizeBucket", "Company size"),
|
|
635
|
-
meta: {
|
|
648
|
+
meta: {
|
|
649
|
+
columnChooserGroup: "Profile",
|
|
650
|
+
hidden: true,
|
|
651
|
+
filterKey: "company_profile.size_bucket",
|
|
652
|
+
filterGroup: "Profile"
|
|
653
|
+
},
|
|
636
654
|
cell: ({ row }) => row.original.sizeBucket || noValue
|
|
637
655
|
},
|
|
638
656
|
{
|
|
@@ -642,14 +660,20 @@ function CustomersCompaniesPage() {
|
|
|
642
660
|
columnChooserGroup: "Profile",
|
|
643
661
|
hidden: true,
|
|
644
662
|
filterKey: "company_profile.annual_revenue",
|
|
645
|
-
filterType: "number"
|
|
663
|
+
filterType: "number",
|
|
664
|
+
filterGroup: "Profile"
|
|
646
665
|
},
|
|
647
666
|
cell: ({ row }) => row.original.annualRevenue || noValue
|
|
648
667
|
},
|
|
649
668
|
{
|
|
650
669
|
accessorKey: "description",
|
|
651
670
|
header: t("customers.companies.detail.fields.description", "Description"),
|
|
652
|
-
meta: {
|
|
671
|
+
meta: {
|
|
672
|
+
columnChooserGroup: "Notes",
|
|
673
|
+
hidden: true,
|
|
674
|
+
filterKey: "description",
|
|
675
|
+
filterGroup: "Notes"
|
|
676
|
+
},
|
|
653
677
|
cell: ({ row }) => row.original.description || noValue
|
|
654
678
|
}
|
|
655
679
|
];
|
|
@@ -667,93 +691,146 @@ function CustomersCompaniesPage() {
|
|
|
667
691
|
cell: ({ getValue }) => renderCustomFieldCell(getValue())
|
|
668
692
|
}));
|
|
669
693
|
return [...baseColumns, ...customColumns];
|
|
670
|
-
}, [customFieldDefs, dictionaryMaps, t]);
|
|
694
|
+
}, [customFieldDefs, dictionaryMaps, dictionaryOptions, loadOwnerFilterOptions, resolvedOwnerFilterOptions, t]);
|
|
695
|
+
const { advancedFilterFields } = useAutoDiscoveredFields({ columns, customFieldDefs });
|
|
696
|
+
React.useEffect(() => {
|
|
697
|
+
setPanelFields((prev) => {
|
|
698
|
+
if (prev === advancedFilterFields) return prev;
|
|
699
|
+
if (prev.length === advancedFilterFields.length) {
|
|
700
|
+
let same = true;
|
|
701
|
+
for (let i = 0; i < prev.length; i++) {
|
|
702
|
+
if (prev[i].key !== advancedFilterFields[i].key) {
|
|
703
|
+
same = false;
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (same) return prev;
|
|
708
|
+
}
|
|
709
|
+
return advancedFilterFields;
|
|
710
|
+
});
|
|
711
|
+
}, [advancedFilterFields]);
|
|
712
|
+
const companiesPresets = React.useMemo(() => makeCompaniesPresets(), []);
|
|
671
713
|
return /* @__PURE__ */ jsxs(Page, { children: [
|
|
672
|
-
/* @__PURE__ */
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
714
|
+
/* @__PURE__ */ jsxs(PageBody, { children: [
|
|
715
|
+
/* @__PURE__ */ jsx(
|
|
716
|
+
DataTable,
|
|
717
|
+
{
|
|
718
|
+
stickyFirstColumn: true,
|
|
719
|
+
stickyActionsColumn: true,
|
|
720
|
+
title: t("customers.companies.list.title"),
|
|
721
|
+
refreshButton: {
|
|
722
|
+
label: t("customers.companies.list.actions.refresh"),
|
|
723
|
+
onRefresh: () => {
|
|
724
|
+
setSearch("");
|
|
725
|
+
setPage(1);
|
|
726
|
+
handleRefresh();
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
actions: /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/backend/customers/companies/create", children: t("customers.companies.list.actions.new") }) }),
|
|
730
|
+
columns,
|
|
731
|
+
columnChooser: { auto: true },
|
|
732
|
+
data: rows,
|
|
733
|
+
exporter: exportConfig,
|
|
734
|
+
searchValue: search,
|
|
735
|
+
onSearchChange: (value) => {
|
|
736
|
+
setSearch(value);
|
|
682
737
|
setPage(1);
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
id: "view",
|
|
721
|
-
label: t("customers.companies.list.actions.view"),
|
|
722
|
-
onSelect: () => {
|
|
723
|
-
router.push(`/backend/customers/companies-v2/${row.id}`);
|
|
738
|
+
},
|
|
739
|
+
searchPlaceholder: t("customers.companies.list.searchPlaceholder"),
|
|
740
|
+
entityIds: [E.customers.customer_entity, E.customers.customer_company_profile],
|
|
741
|
+
onRowClick: (row) => router.push(`/backend/customers/companies-v2/${row.id}`),
|
|
742
|
+
perspective: { tableId: "customers.companies.list" },
|
|
743
|
+
sortable: true,
|
|
744
|
+
sorting,
|
|
745
|
+
onSortingChange: setSorting,
|
|
746
|
+
bulkActions: [
|
|
747
|
+
{
|
|
748
|
+
id: "delete",
|
|
749
|
+
label: t("customers.companies.list.actions.bulkDelete", "Delete selected"),
|
|
750
|
+
destructive: true,
|
|
751
|
+
onExecute: handleBulkDelete
|
|
752
|
+
}
|
|
753
|
+
],
|
|
754
|
+
rowActions: (row) => /* @__PURE__ */ jsx(
|
|
755
|
+
RowActions,
|
|
756
|
+
{
|
|
757
|
+
items: [
|
|
758
|
+
{
|
|
759
|
+
id: "view",
|
|
760
|
+
label: t("customers.companies.list.actions.view"),
|
|
761
|
+
onSelect: () => {
|
|
762
|
+
router.push(`/backend/customers/companies-v2/${row.id}`);
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
id: "open-new-tab",
|
|
767
|
+
label: t("customers.companies.list.actions.openInNewTab"),
|
|
768
|
+
onSelect: () => window.open(`/backend/customers/companies-v2/${row.id}`, "_blank", "noopener")
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
id: "delete",
|
|
772
|
+
label: t("customers.companies.list.actions.delete"),
|
|
773
|
+
destructive: true,
|
|
774
|
+
onSelect: () => handleDelete(row)
|
|
724
775
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
auto: true,
|
|
742
|
-
value: advancedFilterState,
|
|
743
|
-
onChange: setAdvancedFilterState,
|
|
744
|
-
onApply: () => {
|
|
745
|
-
setPage(1);
|
|
776
|
+
]
|
|
777
|
+
}
|
|
778
|
+
),
|
|
779
|
+
advancedFilter: {
|
|
780
|
+
auto: true,
|
|
781
|
+
value: filterPanel.tree,
|
|
782
|
+
onChange: filterPanel.setTree,
|
|
783
|
+
onApply: () => filterPanel.flush(),
|
|
784
|
+
onClear: handleAdvancedFilterClear,
|
|
785
|
+
triggerRef: filtersTriggerRef,
|
|
786
|
+
externalPopover: true,
|
|
787
|
+
onTriggerClick: () => setFiltersOpen((prev) => !prev),
|
|
788
|
+
onApplyTree: (tree) => {
|
|
789
|
+
filterPanel.replaceTree(tree);
|
|
790
|
+
setPage(1);
|
|
791
|
+
}
|
|
746
792
|
},
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
793
|
+
activeFilterChips: /* @__PURE__ */ jsx(
|
|
794
|
+
ActiveFilterChips,
|
|
795
|
+
{
|
|
796
|
+
tree: filterPanel.tree,
|
|
797
|
+
fields: advancedFilterFields,
|
|
798
|
+
popoverOpen: filtersOpen,
|
|
799
|
+
onRemoveNode: (id) => filterPanel.dispatch({ type: "removeNode", nodeId: id }),
|
|
800
|
+
onOpen: () => setFiltersOpen(true)
|
|
801
|
+
}
|
|
802
|
+
),
|
|
803
|
+
filterAwareEmptyState: {
|
|
804
|
+
active: advancedFilterState.root.children.length > 0,
|
|
805
|
+
entityNamePlural: t("customers.companies.entityPlural", "companies"),
|
|
806
|
+
canRemoveLast: filterPanel.tree.root.children.length > 0,
|
|
807
|
+
onClearAll: handleAdvancedFilterClear,
|
|
808
|
+
onRemoveLast: () => filterPanel.dispatch({ type: "removeLast" })
|
|
809
|
+
},
|
|
810
|
+
virtualized: true,
|
|
811
|
+
pagination: { page, pageSize, total, totalPages, onPageChange: setPage, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange, cacheStatus },
|
|
812
|
+
isLoading
|
|
813
|
+
}
|
|
814
|
+
),
|
|
815
|
+
/* @__PURE__ */ jsx(
|
|
816
|
+
AdvancedFilterPanel,
|
|
817
|
+
{
|
|
818
|
+
fields: advancedFilterFields,
|
|
819
|
+
value: filterPanel.tree,
|
|
820
|
+
onChange: filterPanel.setTree,
|
|
821
|
+
onApply: filterPanel.flush,
|
|
822
|
+
onClear: handleAdvancedFilterClear,
|
|
823
|
+
onFlush: filterPanel.flush,
|
|
824
|
+
pendingErrors: filterPanel.pendingErrors,
|
|
825
|
+
userId: currentUserId,
|
|
826
|
+
presets: companiesPresets,
|
|
827
|
+
open: filtersOpen,
|
|
828
|
+
onOpenChange: setFiltersOpen,
|
|
829
|
+
triggerRef: filtersTriggerRef,
|
|
830
|
+
savedFilterStorageKey: "customers.companies.list"
|
|
831
|
+
}
|
|
832
|
+
)
|
|
833
|
+
] }),
|
|
757
834
|
ConfirmDialogElement
|
|
758
835
|
] });
|
|
759
836
|
}
|