@aglyn/plugins-crm 1.0.0-beta.200 → 1.0.0-beta.202

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 (78) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/activity-list.d.ts +36 -5
  3. package/src/lib/components/activity-list.js +159 -137
  4. package/src/lib/components/activity-list.js.map +1 -1
  5. package/src/lib/components/activity-timeline.d.ts +84 -0
  6. package/src/lib/components/activity-timeline.js +224 -0
  7. package/src/lib/components/activity-timeline.js.map +1 -0
  8. package/src/lib/components/company-properties-card.js +53 -36
  9. package/src/lib/components/company-properties-card.js.map +1 -1
  10. package/src/lib/components/contact-associations-card.d.ts +3 -2
  11. package/src/lib/components/contact-associations-card.js +12 -12
  12. package/src/lib/components/contact-associations-card.js.map +1 -1
  13. package/src/lib/components/contact-detail-page.d.ts +4 -3
  14. package/src/lib/components/contact-detail-page.js +4 -10
  15. package/src/lib/components/contact-detail-page.js.map +1 -1
  16. package/src/lib/components/contact-properties-card.d.ts +21 -4
  17. package/src/lib/components/contact-properties-card.js +129 -29
  18. package/src/lib/components/contact-properties-card.js.map +1 -1
  19. package/src/lib/components/contact-timeline-card.d.ts +12 -6
  20. package/src/lib/components/contact-timeline-card.js +132 -174
  21. package/src/lib/components/contact-timeline-card.js.map +1 -1
  22. package/src/lib/components/contacts-section.js +197 -198
  23. package/src/lib/components/contacts-section.js.map +1 -1
  24. package/src/lib/components/crm-email-state-chip.js +4 -1
  25. package/src/lib/components/crm-email-state-chip.js.map +1 -1
  26. package/src/lib/components/deal-products-card.js +15 -13
  27. package/src/lib/components/deal-products-card.js.map +1 -1
  28. package/src/lib/components/deal-properties-card.js +7 -0
  29. package/src/lib/components/deal-properties-card.js.map +1 -1
  30. package/src/lib/components/deal-stage-card.js +31 -29
  31. package/src/lib/components/deal-stage-card.js.map +1 -1
  32. package/src/lib/components/deals-section.js +9 -14
  33. package/src/lib/components/deals-section.js.map +1 -1
  34. package/src/lib/components/fields-section.d.ts +4 -0
  35. package/src/lib/components/fields-section.js +368 -154
  36. package/src/lib/components/fields-section.js.map +1 -1
  37. package/src/lib/components/form-contact-fields-card.js +24 -20
  38. package/src/lib/components/form-contact-fields-card.js.map +1 -1
  39. package/src/lib/components/lead-campaigns-card.d.ts +1 -1
  40. package/src/lib/components/lead-campaigns-card.js +32 -35
  41. package/src/lib/components/lead-campaigns-card.js.map +1 -1
  42. package/src/lib/components/lead-properties-card.d.ts +14 -6
  43. package/src/lib/components/lead-properties-card.js +317 -313
  44. package/src/lib/components/lead-properties-card.js.map +1 -1
  45. package/src/lib/components/lead-surfaces-note.js +13 -2
  46. package/src/lib/components/lead-surfaces-note.js.map +1 -1
  47. package/src/lib/components/linked-deals-card.js +12 -10
  48. package/src/lib/components/linked-deals-card.js.map +1 -1
  49. package/src/lib/components/recent-activity-feed.d.ts +12 -8
  50. package/src/lib/components/recent-activity-feed.js +54 -29
  51. package/src/lib/components/recent-activity-feed.js.map +1 -1
  52. package/src/lib/components/record-activity-card.d.ts +4 -2
  53. package/src/lib/components/record-activity-card.js +29 -11
  54. package/src/lib/components/record-activity-card.js.map +1 -1
  55. package/src/lib/components/reports/activity-card.js +18 -12
  56. package/src/lib/components/reports/activity-card.js.map +1 -1
  57. package/src/lib/components/reports/lead-funnel-card.js +47 -37
  58. package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
  59. package/src/lib/components/reports/pipeline-card.js +22 -16
  60. package/src/lib/components/reports/pipeline-card.js.map +1 -1
  61. package/src/lib/components/reports/report-export.d.ts +4 -11
  62. package/src/lib/components/reports/report-export.js +16 -33
  63. package/src/lib/components/reports/report-export.js.map +1 -1
  64. package/src/lib/components/reports/source-conversion-card.js +18 -12
  65. package/src/lib/components/reports/source-conversion-card.js.map +1 -1
  66. package/src/lib/components/reports/tasks-card.js +35 -25
  67. package/src/lib/components/reports/tasks-card.js.map +1 -1
  68. package/src/lib/components/tasks-section.js +23 -23
  69. package/src/lib/components/tasks-section.js.map +1 -1
  70. package/src/lib/constants/field-list-query.d.ts +23 -0
  71. package/src/lib/constants/field-list-query.js +80 -0
  72. package/src/lib/constants/field-list-query.js.map +1 -0
  73. package/src/lib/model/lead-filters.d.ts +1 -1
  74. package/src/lib/server/capture-contact.js +7 -5
  75. package/src/lib/server/capture-contact.js.map +1 -1
  76. package/src/lib/components/contact-custom-fields-card.d.ts +0 -39
  77. package/src/lib/components/contact-custom-fields-card.js +0 -204
  78. package/src/lib/components/contact-custom-fields-card.js.map +0 -1
@@ -35,6 +35,7 @@ import { useCrmRecordsQuota } from "../hooks/use-crm-records-quota.js";
35
35
  import { CardDisplay } from "@aglyn/shared-ui-jsx";
36
36
  import EmptyStateComponent from "@aglyn/shared-ui-jsx/components/empty-state.component";
37
37
  import ContactsBulkBar from "./contacts-bulk-bar.js";
38
+ import { CrmListActions } from "./crm-list-toolbar.js";
38
39
  import { ContactImportButton } from "./contact-import-drawer.js";
39
40
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
40
41
  import { useFirestore, useFirestoreCollection, useFirestoreDoc, useUser } from "@aglyn/tenant-feature-instance";
@@ -730,144 +731,33 @@ import { CrmSuiteLockedButton, CrmSuiteNotice, crmSuiteIncluded } from "./crm-su
730
731
  ]);
731
732
  return /*#__PURE__*/ _jsxs(_Fragment, {
732
733
  children: [
733
- /*#__PURE__*/ _jsx(CardDisplay, {
734
- header: 'Contacts',
735
- help: pluginDocsHelp('contacts', {
736
- anchor: '#the-contacts-page'
737
- }),
738
- contentGutterX: true,
739
- contentGutterY: true,
740
- children: /*#__PURE__*/ _jsxs(Stack, {
741
- spacing: 2,
742
- children: [
743
- /*#__PURE__*/ _jsxs(Stack, {
744
- direction: "row",
745
- spacing: 1,
746
- sx: {
747
- alignItems: 'center',
748
- flexWrap: 'wrap',
749
- rowGap: 1
750
- },
751
- children: [
752
- /*#__PURE__*/ _jsx(Typography, {
753
- variant: "body2",
754
- color: "text.secondary",
755
- sx: {
756
- flex: 1
757
- },
758
- children: `${contactCount.toLocaleString()} contacts · ${Number.isFinite(quota.included) ? `${quota.used.toLocaleString()} of ${quota.included.toLocaleString()} CRM records` : `${quota.used.toLocaleString()} CRM records`}`
759
- }),
760
- suiteIncluded ? /*#__PURE__*/ _jsx(ContactImportButton, {
761
- hostId: hostId,
762
- org: org
763
- }) : /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
764
- children: 'Import CSV'
765
- }),
766
- /*#__PURE__*/ _jsx(Button, {
767
- size: "small",
768
- onClick: handleExport,
769
- disabled: !contacts.length,
770
- children: 'Export CSV'
771
- }),
772
- suiteIncluded ? /*#__PURE__*/ _jsx(Button, {
773
- size: "small",
774
- variant: "contained",
775
- color: "primary",
776
- disabled: !dataScope,
777
- onClick: ()=>{
778
- setCreateError(null);
779
- setCreateOpen(true);
780
- },
781
- children: 'New contact'
782
- }) : /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
783
- variant: "contained",
784
- color: "primary",
785
- children: 'New contact'
786
- })
787
- ]
788
- }),
789
- suiteIncluded ? null : /*#__PURE__*/ _jsx(CrmSuiteNotice, {
790
- children: 'Contacts arrive here on their own from your forms, sign-ups, ' + 'orders and bookings. Adding one by hand, importing a CSV, ' + "saved views, and a contact's owner, stage and company are " + 'part of the CRM.'
791
- }),
792
- /*#__PURE__*/ _jsxs(Stack, {
793
- spacing: 1,
794
- children: [
795
- /*#__PURE__*/ _jsx(CrmViewsControl, {
796
- controller: views,
797
- allLabel: "All contacts",
798
- suiteLocked: !suiteIncluded,
799
- presets: segmentPresets,
800
- onSaveAsSegment: segmentFilters && dataScope ? ()=>setSegmentName('') : null
801
- }),
802
- /*#__PURE__*/ _jsx(ListFilterChips, {
803
- fields: filterFields,
804
- headers: filterHeaders,
805
- clauses: views.state.filters,
806
- onChange: views.setFilters,
807
- options: filterOptions,
808
- marksServed: false
809
- })
810
- ]
811
- }),
812
- !quota.allowed ? /*#__PURE__*/ _jsx(Alert, {
813
- severity: "warning",
814
- children: 'CRM records limit reached — new visitors are no longer ' + 'captured' + (droppedTotal > 0 ? ` (${droppedTotal.toLocaleString()} missed so far)` : '') + '. Upgrade in Billing to keep collecting.'
815
- }) : quota.overageRecords > 0 && quota.overageRateUsd != null && // No claim about money until the verdict that decides it has
816
- // settled (AGL-1662). Before Remote Config activation the flag
817
- // reads its registry default, which is no org's verdict, so an
818
- // ungated alert could assert the wrong wording for one paint.
819
- releaseFlagsReady ? /*#__PURE__*/ _jsx(Alert, {
820
- severity: "info",
821
- children: contactsBilled ? // basis (AGL-2399): the count here is LIVE, the invoice
822
- // charges the last reading before the month closes, so the
823
- // dollar figure is a projection until the month ends. Staff
824
- // and customer must not read different sentences about the
825
- // same org's money — that applies to WHEN it is measured as
826
- // much as to how much.
827
- `${quota.overageRecords.toLocaleString()} CRM records over ` + `your plan's included ${quota.included.toLocaleString()} — ` + `metered at $${quota.overageRateUsd}/1,000 per month ` + `(≈$${quota.overageMonthlyUsd.toFixed(2)} if your list ends ` + 'the month at this size). ' + 'Upgrade in Billing for a larger included audience.' : // itself the wording `1a2aed5cb` published to
828
- // `billing-and-plans/overview.md` (AGL-1601/1603). Staff and
829
- // customer must not read different sentences about the same
830
- // org's money.
831
- //
832
- // THE COUNT STAYS, THE TOTAL GOES: the head-count is real —
833
- // ingestion captured those records — and is not a claim
834
- // about money. The upgrade nudge goes with the total, since
835
- // it prompts a purchase premised on a charge that is not
836
- // happening.
837
- `${quota.overageRecords.toLocaleString()} CRM records over ` + `your plan's included ${quota.included.toLocaleString()} — ` + 'not billed while the CRM is unavailable. ' + `The $${quota.overageRateUsd}/1,000 rate applies once ` + 'Contacts opens.'
838
- }) : droppedTotal > 0 ? /*#__PURE__*/ _jsx(Alert, {
839
- severity: "info",
840
- children: `${droppedTotal.toLocaleString()} earlier visitor${droppedTotal === 1 ? ' was' : 's were'} not captured while your CRM records band was full.`
841
- }) : null,
842
- unmatchedRefundTotal > 0 ? /*#__PURE__*/ _jsx(Alert, {
843
- severity: "warning",
844
- children: `${unmatchedRefundTotal.toLocaleString()} refund${unmatchedRefundTotal === 1 ? '' : 's'} could not be recorded against a contact${UNMATCHED_REFUND_REASON[unmatchedRefundReason] ? ` — most recently because ${UNMATCHED_REFUND_REASON[unmatchedRefundReason]}` : ''}. The money moved; the customer's timeline does not show it.`
845
- }) : null,
846
- /*#__PURE__*/ _jsx(ListQueryNotices, {
847
- refused: refused,
848
- notices: paged.plan.notices
734
+ /*#__PURE__*/ _jsxs(Stack, {
735
+ spacing: 3,
736
+ children: [
737
+ /*#__PURE__*/ _jsx(CardDisplay, {
738
+ header: 'Contacts',
739
+ help: pluginDocsHelp('contacts', {
740
+ anchor: '#the-contacts-page'
849
741
  }),
850
- contacts.length === 0 && !views.state.filters.length && !searchWords.some((word)=>word.trim()) ? /*#__PURE__*/ _jsx(EmptyStateComponent, {
851
- label: contactsStatus === 'loading' ? 'Loading contacts…' : 'No contacts yet',
852
- description: contactsStatus === 'loading' ? undefined : suiteIncluded ? 'Form submissions, member sign-ups, orders and bookings become contacts on their own; add one by hand or bring a list in from a CSV.' : 'Form submissions, member sign-ups, orders and bookings become contacts on their own.',
853
- action: contactsStatus === 'loading' ? undefined : !suiteIncluded ? /*#__PURE__*/ _jsxs(Stack, {
854
- direction: "row",
855
- spacing: 1,
742
+ contentGutterX: true,
743
+ contentGutterY: true,
744
+ HeaderProps: {
745
+ // The record actions, top right and never clipped (AGL-3311).
746
+ action: /*#__PURE__*/ _jsxs(CrmListActions, {
856
747
  children: [
857
- /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
858
- variant: "contained",
859
- color: "primary",
860
- children: 'New contact'
861
- }),
862
- /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
748
+ suiteIncluded ? /*#__PURE__*/ _jsx(ContactImportButton, {
749
+ hostId: hostId,
750
+ org: org
751
+ }) : /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
863
752
  children: 'Import CSV'
864
- })
865
- ]
866
- }) : /*#__PURE__*/ _jsxs(Stack, {
867
- direction: "row",
868
- spacing: 1,
869
- children: [
753
+ }),
870
754
  /*#__PURE__*/ _jsx(Button, {
755
+ size: "small",
756
+ onClick: handleExport,
757
+ disabled: !contacts.length,
758
+ children: 'Export CSV'
759
+ }),
760
+ suiteIncluded ? /*#__PURE__*/ _jsx(Button, {
871
761
  size: "small",
872
762
  variant: "contained",
873
763
  color: "primary",
@@ -877,79 +767,188 @@ import { CrmSuiteLockedButton, CrmSuiteNotice, crmSuiteIncluded } from "./crm-su
877
767
  setCreateOpen(true);
878
768
  },
879
769
  children: 'New contact'
880
- }),
881
- /*#__PURE__*/ _jsx(ContactImportButton, {
882
- hostId: hostId,
883
- org: org
770
+ }) : /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
771
+ variant: "contained",
772
+ color: "primary",
773
+ children: 'New contact'
884
774
  })
885
775
  ]
886
776
  })
887
- }) : /*#__PURE__*/ _jsxs(_Fragment, {
777
+ },
778
+ children: /*#__PURE__*/ _jsxs(Stack, {
779
+ spacing: 2,
888
780
  children: [
889
- /*#__PURE__*/ _jsx(ContactsBulkBar, {
890
- hostId: hostId,
891
- org: org,
892
- scope: dataScope,
893
- consentGroup: consentGroup,
894
- rows: contacts,
895
- selected: selectedIds,
896
- onSelectedChange: setSelectedIds,
897
- csv: csvOptions,
898
- suiteLocked: !suiteIncluded
781
+ /*#__PURE__*/ _jsx(Typography, {
782
+ variant: "body2",
783
+ color: "text.secondary",
784
+ children: `${contactCount.toLocaleString()} contacts · ${Number.isFinite(quota.included) ? `${quota.used.toLocaleString()} of ${quota.included.toLocaleString()} CRM records` : `${quota.used.toLocaleString()} CRM records`}`
899
785
  }),
900
- /*#__PURE__*/ _jsx(CrmColumnOrderProvider, {
901
- value: grid.columnOrder,
902
- children: /*#__PURE__*/ _jsx(ListTable, {
903
- rows: contacts,
904
- columns: grid.columns,
905
- slots: CRM_LIST_SLOTS,
906
- selectable: {
907
- selected: selectedIds,
908
- onChange: setSelectedIds
909
- },
910
- onOpen: (id)=>router.push(routes.contact(id)),
911
- /*
912
- * The grid's Filters panel and quick search edit the view's
913
- * clauses; the list's query answers them (AGL-3321), so the
914
- * grid must not filter again itself. Opening the panel is
915
- * what reads the roster and the companies its pickers name.
916
- * Columns are the view's, controlled so a saved arrangement
917
- * is what the grid shows; the query orders the rows —
918
- * newest change first — so the grid sorts nothing itself.
919
- */ filterMode: "server",
920
- filterModel: gridFilter.filterModel,
921
- onFilterModelChange: gridFilter.onFilterModelChange,
922
- quickFilter: true,
923
- onPreferencePanelOpen: ()=>setFilterOpened(true),
924
- // A filter that matched nothing is said inside the grid,
925
- // so the toolbar that set it stays to change it.
926
- noRowsLabel: contactsStatus === 'loading' ? 'Loading…' : 'No contacts match these filters',
927
- columnVisibilityModel: grid.columnVisibilityModel,
928
- onColumnVisibilityModelChange: grid.onColumnVisibilityModelChange,
929
- sortingMode: "server",
930
- disableColumnSorting: true,
931
- loading: contactsStatus === 'loading',
932
- // Paged by the footer below, so the grid must not also slice.
933
- hideFooter: true
934
- })
786
+ suiteIncluded ? null : /*#__PURE__*/ _jsx(CrmSuiteNotice, {
787
+ children: 'Contacts arrive here on their own from your forms, sign-ups, ' + 'orders and bookings. Adding one by hand, importing a CSV, ' + "saved views, and a contact's owner, stage and company are " + 'part of the CRM.'
788
+ }),
789
+ /*#__PURE__*/ _jsxs(Stack, {
790
+ spacing: 1,
791
+ children: [
792
+ /*#__PURE__*/ _jsx(CrmViewsControl, {
793
+ controller: views,
794
+ allLabel: "All contacts",
795
+ suiteLocked: !suiteIncluded,
796
+ presets: segmentPresets,
797
+ onSaveAsSegment: segmentFilters && dataScope ? ()=>setSegmentName('') : null
798
+ }),
799
+ /*#__PURE__*/ _jsx(ListFilterChips, {
800
+ fields: filterFields,
801
+ headers: filterHeaders,
802
+ clauses: views.state.filters,
803
+ onChange: views.setFilters,
804
+ options: filterOptions,
805
+ marksServed: false
806
+ })
807
+ ]
935
808
  }),
936
- /*#__PURE__*/ _jsx(ListPagination, {
937
- page: paged.page,
938
- pageSize: paged.pageSize,
939
- rowCount: contacts.length,
940
- hasMore: paged.hasMore,
941
- onPageChange: paged.setPage,
942
- onPageSizeChange: paged.setPageSize
809
+ !quota.allowed ? /*#__PURE__*/ _jsx(Alert, {
810
+ severity: "warning",
811
+ children: 'CRM records limit reached — new visitors are no longer ' + 'captured' + (droppedTotal > 0 ? ` (${droppedTotal.toLocaleString()} missed so far)` : '') + '. Upgrade in Billing to keep collecting.'
812
+ }) : quota.overageRecords > 0 && quota.overageRateUsd != null && // No claim about money until the verdict that decides it has
813
+ // settled (AGL-1662). Before Remote Config activation the flag
814
+ // reads its registry default, which is no org's verdict, so an
815
+ // ungated alert could assert the wrong wording for one paint.
816
+ releaseFlagsReady ? /*#__PURE__*/ _jsx(Alert, {
817
+ severity: "info",
818
+ children: contactsBilled ? // basis (AGL-2399): the count here is LIVE, the invoice
819
+ // charges the last reading before the month closes, so the
820
+ // dollar figure is a projection until the month ends. Staff
821
+ // and customer must not read different sentences about the
822
+ // same org's money — that applies to WHEN it is measured as
823
+ // much as to how much.
824
+ `${quota.overageRecords.toLocaleString()} CRM records over ` + `your plan's included ${quota.included.toLocaleString()} — ` + `metered at $${quota.overageRateUsd}/1,000 per month ` + `(≈$${quota.overageMonthlyUsd.toFixed(2)} if your list ends ` + 'the month at this size). ' + 'Upgrade in Billing for a larger included audience.' : // itself the wording `1a2aed5cb` published to
825
+ // `billing-and-plans/overview.md` (AGL-1601/1603). Staff and
826
+ // customer must not read different sentences about the same
827
+ // org's money.
828
+ //
829
+ // THE COUNT STAYS, THE TOTAL GOES: the head-count is real —
830
+ // ingestion captured those records — and is not a claim
831
+ // about money. The upgrade nudge goes with the total, since
832
+ // it prompts a purchase premised on a charge that is not
833
+ // happening.
834
+ `${quota.overageRecords.toLocaleString()} CRM records over ` + `your plan's included ${quota.included.toLocaleString()} — ` + 'not billed while the CRM is unavailable. ' + `The $${quota.overageRateUsd}/1,000 rate applies once ` + 'Contacts opens.'
835
+ }) : droppedTotal > 0 ? /*#__PURE__*/ _jsx(Alert, {
836
+ severity: "info",
837
+ children: `${droppedTotal.toLocaleString()} earlier visitor${droppedTotal === 1 ? ' was' : 's were'} not captured while your CRM records band was full.`
838
+ }) : null,
839
+ unmatchedRefundTotal > 0 ? /*#__PURE__*/ _jsx(Alert, {
840
+ severity: "warning",
841
+ children: `${unmatchedRefundTotal.toLocaleString()} refund${unmatchedRefundTotal === 1 ? '' : 's'} could not be recorded against a contact${UNMATCHED_REFUND_REASON[unmatchedRefundReason] ? ` — most recently because ${UNMATCHED_REFUND_REASON[unmatchedRefundReason]}` : ''}. The money moved; the customer's timeline does not show it.`
842
+ }) : null,
843
+ /*#__PURE__*/ _jsx(ListQueryNotices, {
844
+ refused: refused,
845
+ notices: paged.plan.notices
846
+ }),
847
+ contacts.length === 0 && !views.state.filters.length && !searchWords.some((word)=>word.trim()) ? /*#__PURE__*/ _jsx(EmptyStateComponent, {
848
+ label: contactsStatus === 'loading' ? 'Loading contacts…' : 'No contacts yet',
849
+ description: contactsStatus === 'loading' ? undefined : suiteIncluded ? 'Form submissions, member sign-ups, orders and bookings become contacts on their own; add one by hand or bring a list in from a CSV.' : 'Form submissions, member sign-ups, orders and bookings become contacts on their own.',
850
+ action: contactsStatus === 'loading' ? undefined : !suiteIncluded ? /*#__PURE__*/ _jsxs(Stack, {
851
+ direction: "row",
852
+ spacing: 1,
853
+ children: [
854
+ /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
855
+ variant: "contained",
856
+ color: "primary",
857
+ children: 'New contact'
858
+ }),
859
+ /*#__PURE__*/ _jsx(CrmSuiteLockedButton, {
860
+ children: 'Import CSV'
861
+ })
862
+ ]
863
+ }) : /*#__PURE__*/ _jsxs(Stack, {
864
+ direction: "row",
865
+ spacing: 1,
866
+ children: [
867
+ /*#__PURE__*/ _jsx(Button, {
868
+ size: "small",
869
+ variant: "contained",
870
+ color: "primary",
871
+ disabled: !dataScope,
872
+ onClick: ()=>{
873
+ setCreateError(null);
874
+ setCreateOpen(true);
875
+ },
876
+ children: 'New contact'
877
+ }),
878
+ /*#__PURE__*/ _jsx(ContactImportButton, {
879
+ hostId: hostId,
880
+ org: org
881
+ })
882
+ ]
883
+ })
884
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
885
+ children: [
886
+ /*#__PURE__*/ _jsx(ContactsBulkBar, {
887
+ hostId: hostId,
888
+ org: org,
889
+ scope: dataScope,
890
+ consentGroup: consentGroup,
891
+ rows: contacts,
892
+ selected: selectedIds,
893
+ onSelectedChange: setSelectedIds,
894
+ csv: csvOptions,
895
+ suiteLocked: !suiteIncluded
896
+ }),
897
+ /*#__PURE__*/ _jsx(CrmColumnOrderProvider, {
898
+ value: grid.columnOrder,
899
+ children: /*#__PURE__*/ _jsx(ListTable, {
900
+ rows: contacts,
901
+ columns: grid.columns,
902
+ slots: CRM_LIST_SLOTS,
903
+ selectable: {
904
+ selected: selectedIds,
905
+ onChange: setSelectedIds
906
+ },
907
+ onOpen: (id)=>router.push(routes.contact(id)),
908
+ /*
909
+ * The grid's Filters panel and quick search edit the view's
910
+ * clauses; the list's query answers them (AGL-3321), so the
911
+ * grid must not filter again itself. Opening the panel is
912
+ * what reads the roster and the companies its pickers name.
913
+ * Columns are the view's, controlled so a saved arrangement
914
+ * is what the grid shows; the query orders the rows —
915
+ * newest change first — so the grid sorts nothing itself.
916
+ */ filterMode: "server",
917
+ filterModel: gridFilter.filterModel,
918
+ onFilterModelChange: gridFilter.onFilterModelChange,
919
+ quickFilter: true,
920
+ onPreferencePanelOpen: ()=>setFilterOpened(true),
921
+ // A filter that matched nothing is said inside the grid,
922
+ // so the toolbar that set it stays to change it.
923
+ noRowsLabel: contactsStatus === 'loading' ? 'Loading…' : 'No contacts match these filters',
924
+ columnVisibilityModel: grid.columnVisibilityModel,
925
+ onColumnVisibilityModelChange: grid.onColumnVisibilityModelChange,
926
+ sortingMode: "server",
927
+ disableColumnSorting: true,
928
+ loading: contactsStatus === 'loading',
929
+ // Paged by the footer below, so the grid must not also slice.
930
+ hideFooter: true
931
+ })
932
+ }),
933
+ /*#__PURE__*/ _jsx(ListPagination, {
934
+ page: paged.page,
935
+ pageSize: paged.pageSize,
936
+ rowCount: contacts.length,
937
+ hasMore: paged.hasMore,
938
+ onPageChange: paged.setPage,
939
+ onPageSizeChange: paged.setPageSize
940
+ })
941
+ ]
943
942
  })
944
943
  ]
945
- }),
946
- /*#__PURE__*/ _jsx(RecentActivityFeed, {
947
- hostId: hostId,
948
- org: org,
949
- basePath: props.basePath
950
944
  })
951
- ]
952
- })
945
+ }),
946
+ /*#__PURE__*/ _jsx(RecentActivityFeed, {
947
+ hostId: hostId,
948
+ org: org,
949
+ basePath: props.basePath
950
+ })
951
+ ]
953
952
  }),
954
953
  /*#__PURE__*/ _jsxs(Dialog, {
955
954
  open: segmentName !== null,