@open-mercato/core 0.6.1-develop.3276.1.1fcd145078 → 0.6.1-develop.3287.1.450f4ffb56

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.
Files changed (266) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/domain_mapping/index.js +31 -0
  3. package/dist/generated/entities/domain_mapping/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +2 -1
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +16 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/helpers/integration/auth.js +1 -1
  9. package/dist/helpers/integration/auth.js.map +2 -2
  10. package/dist/helpers/integration/authUi.js +1 -1
  11. package/dist/helpers/integration/authUi.js.map +2 -2
  12. package/dist/helpers/integration/salesUi.js +18 -17
  13. package/dist/helpers/integration/salesUi.js.map +2 -2
  14. package/dist/modules/auth/frontend/login.js +4 -4
  15. package/dist/modules/auth/frontend/login.js.map +2 -2
  16. package/dist/modules/auth/frontend/reset/[token]/page.js +5 -5
  17. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  18. package/dist/modules/auth/frontend/reset.js +2 -2
  19. package/dist/modules/auth/frontend/reset.js.map +2 -2
  20. package/dist/modules/customer_accounts/acl.js +2 -1
  21. package/dist/modules/customer_accounts/acl.js.map +2 -2
  22. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
  23. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
  24. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
  25. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
  26. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
  27. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
  28. package/dist/modules/customer_accounts/api/domain-check.js +70 -0
  29. package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
  30. package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
  31. package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
  32. package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
  33. package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
  34. package/dist/modules/customer_accounts/api/login.js +14 -3
  35. package/dist/modules/customer_accounts/api/login.js.map +2 -2
  36. package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
  37. package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
  38. package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
  39. package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
  40. package/dist/modules/customer_accounts/api/signup.js +32 -5
  41. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  42. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
  43. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
  44. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
  45. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
  46. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
  47. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
  48. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
  49. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
  50. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
  51. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
  52. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
  53. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
  54. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
  55. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
  56. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
  57. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
  58. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
  59. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
  60. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
  61. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
  62. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
  63. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
  64. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
  65. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
  66. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +4 -3
  67. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  68. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +6 -5
  69. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  70. package/dist/modules/customer_accounts/data/enrichers.js +78 -12
  71. package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
  72. package/dist/modules/customer_accounts/data/entities.js +85 -2
  73. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  74. package/dist/modules/customer_accounts/data/guards.js +130 -0
  75. package/dist/modules/customer_accounts/data/guards.js.map +7 -0
  76. package/dist/modules/customer_accounts/data/validators.js +19 -0
  77. package/dist/modules/customer_accounts/data/validators.js.map +2 -2
  78. package/dist/modules/customer_accounts/di.js +23 -1
  79. package/dist/modules/customer_accounts/di.js.map +2 -2
  80. package/dist/modules/customer_accounts/events.js +9 -2
  81. package/dist/modules/customer_accounts/events.js.map +2 -2
  82. package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
  83. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
  84. package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
  85. package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
  86. package/dist/modules/customer_accounts/lib/hostname.js +55 -0
  87. package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
  88. package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
  89. package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
  90. package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
  91. package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
  92. package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
  93. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
  94. package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
  95. package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
  96. package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
  97. package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
  98. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
  99. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
  100. package/dist/modules/customer_accounts/notifications.client.js +76 -0
  101. package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
  102. package/dist/modules/customer_accounts/notifications.js +76 -0
  103. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  104. package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
  105. package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
  106. package/dist/modules/customer_accounts/setup.js +48 -1
  107. package/dist/modules/customer_accounts/setup.js.map +2 -2
  108. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
  109. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
  110. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
  111. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
  112. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
  113. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
  114. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
  115. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
  116. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
  117. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
  118. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
  119. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  120. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
  121. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
  122. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
  123. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
  124. package/dist/modules/customers/api/companies/route.js +4 -9
  125. package/dist/modules/customers/api/companies/route.js.map +2 -2
  126. package/dist/modules/customers/api/deals/route.js +4 -9
  127. package/dist/modules/customers/api/deals/route.js.map +2 -2
  128. package/dist/modules/customers/api/people/route.js +4 -9
  129. package/dist/modules/customers/api/people/route.js.map +2 -2
  130. package/dist/modules/customers/api/utils.js +0 -12
  131. package/dist/modules/customers/api/utils.js.map +2 -2
  132. package/dist/modules/customers/backend/customers/companies/page.js +361 -284
  133. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/deals/page.js +378 -473
  135. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  136. package/dist/modules/customers/backend/customers/people/page.js +368 -286
  137. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  138. package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
  139. package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
  140. package/dist/modules/customers/components/formConfig.js +2 -2
  141. package/dist/modules/customers/components/formConfig.js.map +2 -2
  142. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
  143. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  144. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
  145. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
  146. package/dist/modules/customers/widgets/injection-table.js +6 -0
  147. package/dist/modules/customers/widgets/injection-table.js.map +2 -2
  148. package/dist/modules/integrations/backend/integrations/[id]/page.js +2 -3
  149. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  150. package/dist/modules/perspectives/services/perspectiveService.js +16 -2
  151. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  152. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +5 -3
  153. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  154. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +3 -3
  155. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +2 -2
  156. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +4 -2
  157. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
  158. package/dist/modules/query_index/lib/engine.js +31 -8
  159. package/dist/modules/query_index/lib/engine.js.map +2 -2
  160. package/dist/modules/sales/backend/sales/documents/[id]/page.js +3 -5
  161. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  162. package/dist/modules/sales/components/ProviderFieldInput.js +2 -2
  163. package/dist/modules/sales/components/ProviderFieldInput.js.map +2 -2
  164. package/dist/modules/sales/components/documents/SalesDocumentForm.js +3 -4
  165. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  166. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js +3 -4
  167. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js.map +2 -2
  168. package/dist/modules/workflows/backend/tasks/[id]/page.js +2 -2
  169. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  170. package/dist/modules/workflows/frontend/checkout-demo/page.js +2 -2
  171. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  172. package/generated/entities/domain_mapping/index.ts +14 -0
  173. package/generated/entities.ids.generated.ts +2 -1
  174. package/generated/entity-fields-registry.ts +16 -0
  175. package/package.json +5 -5
  176. package/src/helpers/integration/auth.ts +1 -1
  177. package/src/helpers/integration/authUi.ts +1 -1
  178. package/src/helpers/integration/salesUi.ts +27 -23
  179. package/src/modules/auth/frontend/login.tsx +4 -3
  180. package/src/modules/auth/frontend/reset/[token]/page.tsx +4 -4
  181. package/src/modules/auth/frontend/reset.tsx +2 -2
  182. package/src/modules/customer_accounts/acl.ts +1 -0
  183. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
  184. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
  185. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
  186. package/src/modules/customer_accounts/api/domain-check.ts +83 -0
  187. package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
  188. package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
  189. package/src/modules/customer_accounts/api/login.ts +14 -3
  190. package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
  191. package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
  192. package/src/modules/customer_accounts/api/signup.ts +36 -5
  193. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
  194. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
  195. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
  196. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
  197. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
  198. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
  199. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
  200. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
  201. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
  202. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
  203. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
  204. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
  205. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +3 -2
  206. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +5 -4
  207. package/src/modules/customer_accounts/data/enrichers.ts +95 -12
  208. package/src/modules/customer_accounts/data/entities.ts +80 -1
  209. package/src/modules/customer_accounts/data/guards.ts +155 -0
  210. package/src/modules/customer_accounts/data/validators.ts +26 -0
  211. package/src/modules/customer_accounts/di.ts +35 -1
  212. package/src/modules/customer_accounts/events.ts +8 -1
  213. package/src/modules/customer_accounts/i18n/de.json +78 -0
  214. package/src/modules/customer_accounts/i18n/en.json +78 -0
  215. package/src/modules/customer_accounts/i18n/es.json +78 -0
  216. package/src/modules/customer_accounts/i18n/pl.json +78 -0
  217. package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
  218. package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
  219. package/src/modules/customer_accounts/lib/hostname.ts +76 -0
  220. package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
  221. package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
  222. package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
  223. package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
  224. package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
  225. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
  226. package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
  227. package/src/modules/customer_accounts/notifications.client.ts +76 -0
  228. package/src/modules/customer_accounts/notifications.ts +76 -0
  229. package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
  230. package/src/modules/customer_accounts/setup.ts +81 -1
  231. package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
  232. package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
  233. package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
  234. package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
  235. package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
  236. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
  237. package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
  238. package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
  239. package/src/modules/customers/api/companies/route.ts +4 -9
  240. package/src/modules/customers/api/deals/route.ts +4 -9
  241. package/src/modules/customers/api/people/route.ts +4 -9
  242. package/src/modules/customers/api/utils.ts +0 -14
  243. package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
  244. package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
  245. package/src/modules/customers/backend/customers/people/page.tsx +331 -240
  246. package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
  247. package/src/modules/customers/components/formConfig.tsx +2 -2
  248. package/src/modules/customers/i18n/de.json +31 -0
  249. package/src/modules/customers/i18n/en.json +31 -0
  250. package/src/modules/customers/i18n/es.json +31 -0
  251. package/src/modules/customers/i18n/pl.json +31 -0
  252. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
  253. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
  254. package/src/modules/customers/widgets/injection-table.ts +6 -0
  255. package/src/modules/integrations/backend/integrations/[id]/page.tsx +2 -2
  256. package/src/modules/perspectives/services/perspectiveService.ts +30 -2
  257. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +5 -3
  258. package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +3 -3
  259. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +4 -2
  260. package/src/modules/query_index/lib/engine.ts +46 -8
  261. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +3 -5
  262. package/src/modules/sales/components/ProviderFieldInput.tsx +2 -2
  263. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +2 -3
  264. package/src/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.tsx +3 -4
  265. package/src/modules/workflows/backend/tasks/[id]/page.tsx +2 -2
  266. package/src/modules/workflows/frontend/checkout-demo/page.tsx +2 -2
@@ -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, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
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 { serializeAdvancedFilter } from "@open-mercato/shared/lib/query/advanced-filter";
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 [filterValues, setFilterValues] = React.useState({});
113
- const [advancedFilterState, setAdvancedFilterState] = React.useState({ logic: "and", conditions: [] });
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) => ({ value: entry.value, label: entry.label })).sort((a, b) => a.label.localeCompare(b.label, void 0, { sensitivity: "base" }));
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 filters = React.useMemo(() => [
230
- {
231
- id: "status",
232
- label: t("customers.people.list.filters.status"),
233
- type: "select",
234
- options: dictionaryOptions.statuses,
235
- loadOptions: () => loadDictionaryOptions("statuses")
236
- },
237
- {
238
- id: "source",
239
- label: t("customers.people.list.filters.source"),
240
- type: "select",
241
- options: dictionaryOptions.sources,
242
- loadOptions: () => loadDictionaryOptions("sources")
243
- },
244
- {
245
- id: "lifecycleStage",
246
- label: t("customers.people.list.filters.lifecycleStage"),
247
- type: "select",
248
- options: dictionaryOptions.lifecycleStages,
249
- loadOptions: () => loadDictionaryOptions("lifecycle-stages")
250
- },
251
- {
252
- id: "tagIds",
253
- label: t("customers.people.list.filters.tags"),
254
- type: "tags",
255
- loadOptions: loadTagOptions,
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 status = filterValues.status;
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, filterValues, page, pageSize, search, sorting, tagIdToLabel, tagLabelToId]);
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: { alwaysVisible: true, columnChooserGroup: "Basic Info", filterKey: "display_name", maxWidth: "240px" },
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: { columnChooserGroup: "Contact", filterKey: "primary_email", maxWidth: "220px" },
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: { filterType: "select", filterOptions: dictionaryOptions.statuses, columnChooserGroup: "Basic Info" },
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: { filterType: "select", filterOptions: dictionaryOptions.sources, columnChooserGroup: "Basic Info" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.first_name" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.last_name" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.preferred_name" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.job_title" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.department" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.seniority" },
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: { columnChooserGroup: "Profile", hidden: true, filterKey: "person_profile.timezone" },
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: { columnChooserGroup: "Socials", hidden: true, filterKey: "person_profile.linked_in_url" },
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: { columnChooserGroup: "Socials", hidden: true, filterKey: "person_profile.twitter_url" },
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: { columnChooserGroup: "Notes", hidden: true, filterKey: "description" },
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__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
693
- DataTable,
694
- {
695
- stickyFirstColumn: true,
696
- stickyActionsColumn: true,
697
- title: t("customers.people.list.title"),
698
- refreshButton: {
699
- label: t("customers.people.list.actions.refresh"),
700
- onRefresh: () => {
701
- setSearch("");
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
- handleRefresh();
704
- }
705
- },
706
- actions: /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/backend/customers/people/create", children: t("customers.people.list.actions.new") }) }),
707
- columns,
708
- columnChooser: { auto: true },
709
- data: rows,
710
- exporter: exportConfig,
711
- searchValue: search,
712
- onSearchChange: (value) => {
713
- setSearch(value);
714
- setPage(1);
715
- },
716
- searchPlaceholder: t("customers.people.list.searchPlaceholder"),
717
- filters,
718
- filterValues,
719
- onFiltersApply: handleFiltersApply,
720
- onFiltersClear: handleFiltersClear,
721
- entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],
722
- perspective: { tableId: "customers.people.list" },
723
- onRowClick: (row) => router.push(`/backend/customers/people-v2/${row.id}`),
724
- sortable: true,
725
- sorting,
726
- onSortingChange: setSorting,
727
- bulkActions: [
728
- {
729
- id: "delete",
730
- label: t("customers.people.list.bulkDelete.action", "Delete selected"),
731
- destructive: true,
732
- onExecute: handleBulkDelete
733
- }
734
- ],
735
- rowActions: (row) => /* @__PURE__ */ jsx(
736
- RowActions,
737
- {
738
- items: [
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
- id: "open-new-tab",
748
- label: t("customers.people.list.actions.openInNewTab"),
749
- onSelect: () => window.open(`/backend/customers/people-v2/${row.id}`, "_blank", "noopener")
750
- },
751
- {
752
- id: "delete",
753
- label: t("customers.people.list.actions.delete"),
754
- destructive: true,
755
- onSelect: () => handleDelete(row)
756
- }
757
- ]
758
- }
759
- ),
760
- advancedFilter: {
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
- onClear: () => {
768
- setAdvancedFilterState({ logic: "and", conditions: [] });
769
- setPage(1);
770
- }
771
- },
772
- virtualized: true,
773
- pagination: { page, pageSize, total, totalPages, onPageChange: setPage, cacheStatus, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange },
774
- isLoading
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
  }