@kelpie/ui 0.7.0 → 0.9.0
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/dist/api/resource.d.ts +59 -6
- package/dist/api/resource.d.ts.map +1 -1
- package/dist/api/resource.js +106 -24
- package/dist/api/resource.js.map +1 -1
- package/dist/api/resources/companies.d.ts +2 -0
- package/dist/api/resources/companies.d.ts.map +1 -1
- package/dist/api/resources/companies.js +5 -1
- package/dist/api/resources/companies.js.map +1 -1
- package/dist/api/resources/consentPurposes.d.ts +13 -0
- package/dist/api/resources/consentPurposes.d.ts.map +1 -0
- package/dist/api/resources/consentPurposes.js +39 -0
- package/dist/api/resources/consentPurposes.js.map +1 -0
- package/dist/api/resources/customFields.d.ts +15 -0
- package/dist/api/resources/customFields.d.ts.map +1 -0
- package/dist/api/resources/customFields.js +41 -0
- package/dist/api/resources/customFields.js.map +1 -0
- package/dist/api/resources/decisions.d.ts +2 -0
- package/dist/api/resources/decisions.d.ts.map +1 -1
- package/dist/api/resources/decisions.js +1 -0
- package/dist/api/resources/decisions.js.map +1 -1
- package/dist/api/resources/emailDomainLinker.d.ts +16 -0
- package/dist/api/resources/emailDomainLinker.d.ts.map +1 -0
- package/dist/api/resources/emailDomainLinker.js +16 -0
- package/dist/api/resources/emailDomainLinker.js.map +1 -0
- package/dist/api/resources/enquiries.d.ts +39 -0
- package/dist/api/resources/enquiries.d.ts.map +1 -0
- package/dist/api/resources/enquiries.js +68 -0
- package/dist/api/resources/enquiries.js.map +1 -0
- package/dist/api/resources/forms.d.ts +21 -1
- package/dist/api/resources/forms.d.ts.map +1 -1
- package/dist/api/resources/forms.js +44 -17
- package/dist/api/resources/forms.js.map +1 -1
- package/dist/api/resources/handbookPages.d.ts +1 -1
- package/dist/api/resources/handbookPages.js +1 -1
- package/dist/api/resources/importJobs.d.ts +5 -0
- package/dist/api/resources/importJobs.d.ts.map +1 -1
- package/dist/api/resources/importJobs.js +3 -0
- package/dist/api/resources/importJobs.js.map +1 -1
- package/dist/api/resources/listMembers.d.ts +27 -0
- package/dist/api/resources/listMembers.d.ts.map +1 -0
- package/dist/api/resources/listMembers.js +108 -0
- package/dist/api/resources/listMembers.js.map +1 -0
- package/dist/api/resources/lists.d.ts +14 -0
- package/dist/api/resources/lists.d.ts.map +1 -0
- package/dist/api/resources/lists.js +34 -0
- package/dist/api/resources/lists.js.map +1 -0
- package/dist/api/resources/opportunities.d.ts +2 -0
- package/dist/api/resources/opportunities.d.ts.map +1 -1
- package/dist/api/resources/opportunities.js +1 -0
- package/dist/api/resources/opportunities.js.map +1 -1
- package/dist/api/resources/people.d.ts.map +1 -1
- package/dist/api/resources/people.js +4 -1
- package/dist/api/resources/people.js.map +1 -1
- package/dist/api/resources/roles.d.ts +2 -0
- package/dist/api/resources/roles.d.ts.map +1 -1
- package/dist/api/resources/roles.js +1 -1
- package/dist/api/resources/roles.js.map +1 -1
- package/dist/api/resources/sampleData.d.ts +17 -0
- package/dist/api/resources/sampleData.d.ts.map +1 -0
- package/dist/api/resources/sampleData.js +31 -0
- package/dist/api/resources/sampleData.js.map +1 -0
- package/dist/api/resources/webhooks.d.ts.map +1 -1
- package/dist/api/resources/webhooks.js +7 -22
- package/dist/api/resources/webhooks.js.map +1 -1
- package/dist/app/KelpieApp.d.ts.map +1 -1
- package/dist/app/KelpieApp.js +9 -1
- package/dist/app/KelpieApp.js.map +1 -1
- package/dist/components/ActivitiesPanel.d.ts.map +1 -1
- package/dist/components/ActivitiesPanel.js +3 -1
- package/dist/components/ActivitiesPanel.js.map +1 -1
- package/dist/components/ColumnPicker.d.ts +24 -0
- package/dist/components/ColumnPicker.d.ts.map +1 -0
- package/dist/components/ColumnPicker.js +56 -0
- package/dist/components/ColumnPicker.js.map +1 -0
- package/dist/components/CustomFieldsPanel.d.ts +23 -0
- package/dist/components/CustomFieldsPanel.d.ts.map +1 -0
- package/dist/components/CustomFieldsPanel.js +116 -0
- package/dist/components/CustomFieldsPanel.js.map +1 -0
- package/dist/components/DataTable.d.ts +16 -1
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/DataTable.js +51 -10
- package/dist/components/DataTable.js.map +1 -1
- package/dist/components/DecisionsPanel.d.ts.map +1 -1
- package/dist/components/DecisionsPanel.js +4 -3
- package/dist/components/DecisionsPanel.js.map +1 -1
- package/dist/components/EntitySearch.d.ts.map +1 -1
- package/dist/components/EntitySearch.js +11 -5
- package/dist/components/EntitySearch.js.map +1 -1
- package/dist/components/FormsPanel.d.ts +18 -0
- package/dist/components/FormsPanel.d.ts.map +1 -0
- package/dist/components/FormsPanel.js +37 -0
- package/dist/components/FormsPanel.js.map +1 -0
- package/dist/components/ListsPanel.d.ts +14 -0
- package/dist/components/ListsPanel.d.ts.map +1 -0
- package/dist/components/ListsPanel.js +56 -0
- package/dist/components/ListsPanel.js.map +1 -0
- package/dist/components/NotesPanel.d.ts.map +1 -1
- package/dist/components/NotesPanel.js +6 -5
- package/dist/components/NotesPanel.js.map +1 -1
- package/dist/components/Paginator.d.ts +26 -0
- package/dist/components/Paginator.d.ts.map +1 -0
- package/dist/components/Paginator.js +16 -0
- package/dist/components/Paginator.js.map +1 -0
- package/dist/components/PlanAttention.d.ts +3 -2
- package/dist/components/PlanAttention.d.ts.map +1 -1
- package/dist/components/PlanAttention.js +7 -3
- package/dist/components/PlanAttention.js.map +1 -1
- package/dist/components/PlanPanel.d.ts.map +1 -1
- package/dist/components/PlanPanel.js +2 -1
- package/dist/components/PlanPanel.js.map +1 -1
- package/dist/components/Shell.d.ts.map +1 -1
- package/dist/components/Shell.js +4 -0
- package/dist/components/Shell.js.map +1 -1
- package/dist/components/useHasCustomFields.d.ts +13 -0
- package/dist/components/useHasCustomFields.d.ts.map +1 -0
- package/dist/components/useHasCustomFields.js +16 -0
- package/dist/components/useHasCustomFields.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/listView.d.ts +15 -0
- package/dist/lib/listView.d.ts.map +1 -0
- package/dist/lib/listView.js +66 -0
- package/dist/lib/listView.js.map +1 -0
- package/dist/lib/sort.d.ts +43 -0
- package/dist/lib/sort.d.ts.map +1 -0
- package/dist/lib/sort.js +102 -0
- package/dist/lib/sort.js.map +1 -0
- package/dist/pages/CompaniesPage.d.ts.map +1 -1
- package/dist/pages/CompaniesPage.js +88 -3
- package/dist/pages/CompaniesPage.js.map +1 -1
- package/dist/pages/CompanyDetail.d.ts.map +1 -1
- package/dist/pages/CompanyDetail.js +40 -15
- package/dist/pages/CompanyDetail.js.map +1 -1
- package/dist/pages/DealDetail.d.ts.map +1 -1
- package/dist/pages/DealDetail.js +20 -1
- package/dist/pages/DealDetail.js.map +1 -1
- package/dist/pages/DealsPage.d.ts.map +1 -1
- package/dist/pages/DealsPage.js +134 -14
- package/dist/pages/DealsPage.js.map +1 -1
- package/dist/pages/DecisionsPage.d.ts.map +1 -1
- package/dist/pages/DecisionsPage.js +52 -8
- package/dist/pages/DecisionsPage.js.map +1 -1
- package/dist/pages/EnquiriesPage.d.ts +2 -0
- package/dist/pages/EnquiriesPage.d.ts.map +1 -0
- package/dist/pages/EnquiriesPage.js +251 -0
- package/dist/pages/EnquiriesPage.js.map +1 -0
- package/dist/pages/EnquiryDetail.d.ts +2 -0
- package/dist/pages/EnquiryDetail.d.ts.map +1 -0
- package/dist/pages/EnquiryDetail.js +207 -0
- package/dist/pages/EnquiryDetail.js.map +1 -0
- package/dist/pages/EnquiryStageSettingsPage.d.ts +3 -0
- package/dist/pages/EnquiryStageSettingsPage.d.ts.map +1 -0
- package/dist/pages/EnquiryStageSettingsPage.js +34 -0
- package/dist/pages/EnquiryStageSettingsPage.js.map +1 -0
- package/dist/pages/FormDetail.d.ts.map +1 -1
- package/dist/pages/FormDetail.js +19 -1
- package/dist/pages/FormDetail.js.map +1 -1
- package/dist/pages/FormsPage.d.ts.map +1 -1
- package/dist/pages/FormsPage.js +53 -2
- package/dist/pages/FormsPage.js.map +1 -1
- package/dist/pages/FundraisingPage.d.ts.map +1 -1
- package/dist/pages/FundraisingPage.js +116 -14
- package/dist/pages/FundraisingPage.js.map +1 -1
- package/dist/pages/HiringPage.d.ts.map +1 -1
- package/dist/pages/HiringPage.js +27 -4
- package/dist/pages/HiringPage.js.map +1 -1
- package/dist/pages/ListDetail.d.ts +11 -0
- package/dist/pages/ListDetail.d.ts.map +1 -0
- package/dist/pages/ListDetail.js +214 -0
- package/dist/pages/ListDetail.js.map +1 -0
- package/dist/pages/ListsPage.d.ts +9 -0
- package/dist/pages/ListsPage.d.ts.map +1 -0
- package/dist/pages/ListsPage.js +125 -0
- package/dist/pages/ListsPage.js.map +1 -0
- package/dist/pages/OpportunitiesPage.d.ts.map +1 -1
- package/dist/pages/OpportunitiesPage.js +97 -14
- package/dist/pages/OpportunitiesPage.js.map +1 -1
- package/dist/pages/OpportunityDetail.d.ts.map +1 -1
- package/dist/pages/OpportunityDetail.js +64 -3
- package/dist/pages/OpportunityDetail.js.map +1 -1
- package/dist/pages/PartnershipDetail.d.ts.map +1 -1
- package/dist/pages/PartnershipDetail.js +19 -1
- package/dist/pages/PartnershipDetail.js.map +1 -1
- package/dist/pages/PartnershipsPage.d.ts.map +1 -1
- package/dist/pages/PartnershipsPage.js +111 -14
- package/dist/pages/PartnershipsPage.js.map +1 -1
- package/dist/pages/PeoplePage.d.ts.map +1 -1
- package/dist/pages/PeoplePage.js +119 -3
- package/dist/pages/PeoplePage.js.map +1 -1
- package/dist/pages/PersonDetail.d.ts.map +1 -1
- package/dist/pages/PersonDetail.js +67 -5
- package/dist/pages/PersonDetail.js.map +1 -1
- package/dist/pages/PlanningPage.d.ts.map +1 -1
- package/dist/pages/PlanningPage.js +88 -17
- package/dist/pages/PlanningPage.js.map +1 -1
- package/dist/pages/RaiseDetail.d.ts.map +1 -1
- package/dist/pages/RaiseDetail.js +13 -1
- package/dist/pages/RaiseDetail.js.map +1 -1
- package/dist/pages/RoleDetail.d.ts.map +1 -1
- package/dist/pages/RoleDetail.js +5 -4
- package/dist/pages/RoleDetail.js.map +1 -1
- package/dist/pages/SearchPage.d.ts.map +1 -1
- package/dist/pages/SearchPage.js +1 -0
- package/dist/pages/SearchPage.js.map +1 -1
- package/dist/pages/admin/DataPage.d.ts.map +1 -1
- package/dist/pages/admin/DataPage.js +126 -13
- package/dist/pages/admin/DataPage.js.map +1 -1
- package/dist/pages/admin/FieldsPage.d.ts +2 -0
- package/dist/pages/admin/FieldsPage.d.ts.map +1 -0
- package/dist/pages/admin/FieldsPage.js +164 -0
- package/dist/pages/admin/FieldsPage.js.map +1 -0
- package/dist/pages/admin/PrivacyPage.d.ts +9 -0
- package/dist/pages/admin/PrivacyPage.d.ts.map +1 -0
- package/dist/pages/admin/PrivacyPage.js +170 -0
- package/dist/pages/admin/PrivacyPage.js.map +1 -0
- package/dist/pages/admin/WebhookDeliveries.d.ts.map +1 -1
- package/dist/pages/admin/WebhookDeliveries.js +3 -2
- package/dist/pages/admin/WebhookDeliveries.js.map +1 -1
- package/dist/pages/admin/WebhooksPage.d.ts.map +1 -1
- package/dist/pages/admin/WebhooksPage.js +3 -2
- package/dist/pages/admin/WebhooksPage.js.map +1 -1
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
- package/dist/pages/admin/WorkspaceSettingsPage.js +45 -2
- package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
- package/dist/pages/dashboard/attention.d.ts.map +1 -1
- package/dist/pages/dashboard/attention.js +3 -0
- package/dist/pages/dashboard/attention.js.map +1 -1
- package/dist/pages/forms/AddFieldMenu.d.ts +17 -0
- package/dist/pages/forms/AddFieldMenu.d.ts.map +1 -0
- package/dist/pages/forms/AddFieldMenu.js +43 -0
- package/dist/pages/forms/AddFieldMenu.js.map +1 -0
- package/dist/pages/forms/EmbedPanel.d.ts +4 -0
- package/dist/pages/forms/EmbedPanel.d.ts.map +1 -1
- package/dist/pages/forms/EmbedPanel.js +62 -3
- package/dist/pages/forms/EmbedPanel.js.map +1 -1
- package/dist/pages/forms/FieldsEditor.d.ts.map +1 -1
- package/dist/pages/forms/FieldsEditor.js +179 -36
- package/dist/pages/forms/FieldsEditor.js.map +1 -1
- package/dist/pages/forms/FormActions.d.ts +6 -0
- package/dist/pages/forms/FormActions.d.ts.map +1 -0
- package/dist/pages/forms/FormActions.js +149 -0
- package/dist/pages/forms/FormActions.js.map +1 -0
- package/dist/pages/forms/FormSettings.d.ts.map +1 -1
- package/dist/pages/forms/FormSettings.js +15 -19
- package/dist/pages/forms/FormSettings.js.map +1 -1
- package/dist/pages/forms/SubmissionDetail.d.ts +2 -0
- package/dist/pages/forms/SubmissionDetail.d.ts.map +1 -0
- package/dist/pages/forms/SubmissionDetail.js +76 -0
- package/dist/pages/forms/SubmissionDetail.js.map +1 -0
- package/dist/pages/forms/SubmissionsTable.d.ts.map +1 -1
- package/dist/pages/forms/SubmissionsTable.js +57 -8
- package/dist/pages/forms/SubmissionsTable.js.map +1 -1
- package/dist/pages/forms/fieldList.d.ts +11 -5
- package/dist/pages/forms/fieldList.d.ts.map +1 -1
- package/dist/pages/forms/fieldList.js +66 -6
- package/dist/pages/forms/fieldList.js.map +1 -1
- package/dist/pages/forms/template.d.ts +17 -0
- package/dist/pages/forms/template.d.ts.map +1 -1
- package/dist/pages/forms/template.js +93 -0
- package/dist/pages/forms/template.js.map +1 -1
- package/dist/pages/handbook/HandbookPage.js +1 -1
- package/dist/pages/hiringDirectory.js +2 -2
- package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/WorkspaceStepPage.js +15 -2
- package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
- package/dist/pages/positionDirectory.js +2 -2
- package/package.json +2 -2
- package/src/api/resource.ts +155 -32
- package/src/api/resources/companies.ts +7 -1
- package/src/api/resources/consentPurposes.ts +83 -0
- package/src/api/resources/customFields.ts +88 -0
- package/src/api/resources/decisions.ts +3 -0
- package/src/api/resources/emailDomainLinker.ts +43 -0
- package/src/api/resources/enquiries.ts +118 -0
- package/src/api/resources/forms.ts +59 -19
- package/src/api/resources/handbookPages.ts +1 -1
- package/src/api/resources/importJobs.ts +9 -0
- package/src/api/resources/listMembers.ts +173 -0
- package/src/api/resources/lists.ts +61 -0
- package/src/api/resources/opportunities.ts +3 -0
- package/src/api/resources/people.ts +4 -1
- package/src/api/resources/roles.ts +3 -1
- package/src/api/resources/sampleData.ts +57 -0
- package/src/api/resources/webhooks.ts +8 -24
- package/src/app/KelpieApp.tsx +16 -0
- package/src/components/ActivitiesPanel.tsx +3 -10
- package/src/components/ColumnPicker.tsx +124 -0
- package/src/components/CustomFieldsPanel.tsx +330 -0
- package/src/components/DataTable.tsx +89 -10
- package/src/components/DecisionsPanel.tsx +4 -12
- package/src/components/EntitySearch.tsx +12 -5
- package/src/components/FormsPanel.tsx +86 -0
- package/src/components/ListsPanel.tsx +176 -0
- package/src/components/NotesPanel.tsx +6 -14
- package/src/components/Paginator.tsx +78 -0
- package/src/components/PlanAttention.tsx +20 -3
- package/src/components/PlanPanel.tsx +2 -10
- package/src/components/Shell.tsx +4 -0
- package/src/components/useHasCustomFields.ts +18 -0
- package/src/index.ts +10 -1
- package/src/lib/listView.ts +149 -0
- package/src/lib/sort.ts +143 -0
- package/src/pages/CompaniesPage.tsx +125 -11
- package/src/pages/CompanyDetail.tsx +98 -63
- package/src/pages/DealDetail.tsx +34 -0
- package/src/pages/DealsPage.tsx +178 -24
- package/src/pages/DecisionsPage.tsx +75 -18
- package/src/pages/EnquiriesPage.tsx +434 -0
- package/src/pages/EnquiryDetail.tsx +554 -0
- package/src/pages/EnquiryStageSettingsPage.tsx +63 -0
- package/src/pages/FormDetail.tsx +23 -1
- package/src/pages/FormsPage.tsx +81 -17
- package/src/pages/FundraisingPage.tsx +152 -24
- package/src/pages/HiringPage.tsx +39 -11
- package/src/pages/ListDetail.tsx +470 -0
- package/src/pages/ListsPage.tsx +281 -0
- package/src/pages/OpportunitiesPage.tsx +130 -24
- package/src/pages/OpportunityDetail.tsx +168 -2
- package/src/pages/PartnershipDetail.tsx +35 -0
- package/src/pages/PartnershipsPage.tsx +149 -24
- package/src/pages/PeoplePage.tsx +157 -11
- package/src/pages/PersonDetail.tsx +157 -6
- package/src/pages/PlanningPage.tsx +259 -29
- package/src/pages/RaiseDetail.tsx +24 -0
- package/src/pages/RoleDetail.tsx +21 -26
- package/src/pages/SearchPage.tsx +1 -0
- package/src/pages/admin/DataPage.tsx +324 -13
- package/src/pages/admin/FieldsPage.tsx +455 -0
- package/src/pages/admin/PrivacyPage.tsx +461 -0
- package/src/pages/admin/WebhookDeliveries.tsx +10 -15
- package/src/pages/admin/WebhooksPage.tsx +14 -19
- package/src/pages/admin/WorkspaceSettingsPage.tsx +134 -2
- package/src/pages/dashboard/attention.ts +3 -0
- package/src/pages/forms/AddFieldMenu.tsx +121 -0
- package/src/pages/forms/EmbedPanel.tsx +163 -2
- package/src/pages/forms/FieldsEditor.tsx +539 -165
- package/src/pages/forms/FormActions.tsx +552 -0
- package/src/pages/forms/FormSettings.tsx +29 -70
- package/src/pages/forms/SubmissionDetail.tsx +249 -0
- package/src/pages/forms/SubmissionsTable.tsx +112 -26
- package/src/pages/forms/fieldList.ts +68 -5
- package/src/pages/forms/template.ts +103 -0
- package/src/pages/handbook/HandbookPage.tsx +1 -1
- package/src/pages/hiringDirectory.ts +2 -2
- package/src/pages/onboarding/WorkspaceStepPage.tsx +31 -3
- package/src/pages/positionDirectory.ts +2 -2
- package/src/styles.css +3 -3
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { RECORD_TARGET_TYPE_LABELS } from '@kelpie/schemas'
|
|
2
|
+
import type { ListMember, RecordTargetType } from '@kelpie/schemas'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { Link, useNavigate, useParams } from 'react-router'
|
|
5
|
+
|
|
6
|
+
import { usePatch } from '../api/resource.ts'
|
|
7
|
+
import type { PatchResult } from '../api/resource.ts'
|
|
8
|
+
import { useCompanies } from '../api/resources/companies.ts'
|
|
9
|
+
import { useConsentPurposes } from '../api/resources/consentPurposes.ts'
|
|
10
|
+
import { useDeals } from '../api/resources/deals.ts'
|
|
11
|
+
import { useDeleteList, useList, useUpdateList } from '../api/resources/lists.ts'
|
|
12
|
+
import {
|
|
13
|
+
useAddListMember,
|
|
14
|
+
useListMembers,
|
|
15
|
+
useRemoveListMember,
|
|
16
|
+
} from '../api/resources/listMembers.ts'
|
|
17
|
+
import { useEnquiries } from '../api/resources/enquiries.ts'
|
|
18
|
+
import { useOpportunities } from '../api/resources/opportunities.ts'
|
|
19
|
+
import { usePartnerships } from '../api/resources/partnerships.ts'
|
|
20
|
+
import { usePeople } from '../api/resources/people.ts'
|
|
21
|
+
import { useRaises } from '../api/resources/raises.ts'
|
|
22
|
+
import { Chip } from '../components/Chip.tsx'
|
|
23
|
+
import { DeleteRecord } from '../components/DeleteRecord.tsx'
|
|
24
|
+
import { EntitySearch } from '../components/EntitySearch.tsx'
|
|
25
|
+
import type { SearchOption } from '../components/EntitySearch.tsx'
|
|
26
|
+
import { InlineEdit } from '../components/InlineEdit.tsx'
|
|
27
|
+
import { Paginator } from '../components/Paginator.tsx'
|
|
28
|
+
import { ErrorPanel, LoadingPanel, NotFoundPanel } from '../components/QueryState.tsx'
|
|
29
|
+
import { SectionHeader } from '../components/SectionHeader.tsx'
|
|
30
|
+
import type { List, ListInput } from '@kelpie/schemas'
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* One list, and the records on it.
|
|
34
|
+
*
|
|
35
|
+
* The type is fixed at creation, so the header shows it as a badge and the
|
|
36
|
+
* member picker below dispatches on it: a `person` list picks from people, a
|
|
37
|
+
* `company` list from companies, and so on. A candidate list has no picker in
|
|
38
|
+
* v1 because candidates carry no name of their own; add candidacies from the
|
|
39
|
+
* hiring pages.
|
|
40
|
+
*/
|
|
41
|
+
export function ListDetail(): React.JSX.Element {
|
|
42
|
+
const { id } = useParams()
|
|
43
|
+
const navigate = useNavigate()
|
|
44
|
+
const { record, isLoading, isNotFound, error } = useList(id)
|
|
45
|
+
const deleteList = useDeleteList()
|
|
46
|
+
|
|
47
|
+
if (isNotFound) {
|
|
48
|
+
return <NotFoundPanel label="List" backTo="/lists" />
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (error !== null) {
|
|
52
|
+
return <ErrorPanel error={error} />
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isLoading || record === undefined || id === undefined) {
|
|
56
|
+
return <LoadingPanel label="Loading list…" />
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="animate-fade-in mx-auto max-w-4xl">
|
|
61
|
+
<Link
|
|
62
|
+
to="/lists"
|
|
63
|
+
className="mb-4 inline-flex text-[12px] font-medium text-ink-muted transition hover:text-accent"
|
|
64
|
+
>
|
|
65
|
+
← Lists
|
|
66
|
+
</Link>
|
|
67
|
+
|
|
68
|
+
<div className="mb-6 flex flex-wrap items-start justify-between gap-3">
|
|
69
|
+
<ListHeading list={record} />
|
|
70
|
+
<div className="flex shrink-0 flex-wrap items-center gap-3">
|
|
71
|
+
<Chip>{RECORD_TARGET_TYPE_LABELS[record.targetType]}</Chip>
|
|
72
|
+
<DeleteRecord
|
|
73
|
+
recordLabel="List"
|
|
74
|
+
recordName={record.name}
|
|
75
|
+
isPending={deleteList.isPending}
|
|
76
|
+
error={deleteList.error}
|
|
77
|
+
onConfirm={() => {
|
|
78
|
+
deleteList
|
|
79
|
+
.runAsync(record.id)
|
|
80
|
+
.then(() => navigate('/lists'))
|
|
81
|
+
.catch(() => undefined)
|
|
82
|
+
}}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<ListDescription list={record} />
|
|
88
|
+
|
|
89
|
+
{record.targetType === 'person' && <ListConsentPurpose list={record} />}
|
|
90
|
+
|
|
91
|
+
<ListMembers listId={record.id} targetType={record.targetType} />
|
|
92
|
+
</div>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function ListConsentPurpose({ list }: { readonly list: List }): React.JSX.Element {
|
|
97
|
+
const { patch, error } = useListPatch(list)
|
|
98
|
+
const purposes = useConsentPurposes({ sort: 'sort_order', limit: 200 })
|
|
99
|
+
const options = [
|
|
100
|
+
{ value: '', label: '— none —' },
|
|
101
|
+
...purposes.records.map((purpose) => ({ value: purpose.id, label: purpose.label })),
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div className="mb-6 rounded-md border border-border bg-surface-raised p-4 text-[13px]">
|
|
106
|
+
{error !== null && (
|
|
107
|
+
<div className="mb-2">
|
|
108
|
+
<ErrorPanel error={error} />
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
<div className="flex flex-wrap items-baseline justify-between gap-3">
|
|
112
|
+
<div>
|
|
113
|
+
<div className="text-[12px] font-medium text-ink">Consent purpose</div>
|
|
114
|
+
<p className="mt-0.5 text-[12px] text-ink-muted">
|
|
115
|
+
A form submit that adds a person to this list also grants this purpose. Manual and
|
|
116
|
+
API adds capture nothing.
|
|
117
|
+
</p>
|
|
118
|
+
</div>
|
|
119
|
+
<label className="shrink-0">
|
|
120
|
+
<select
|
|
121
|
+
value={list.consentPurposeId ?? ''}
|
|
122
|
+
onChange={(event) => {
|
|
123
|
+
const value = event.target.value
|
|
124
|
+
patch({ consentPurposeId: value.length === 0 ? null : value })
|
|
125
|
+
}}
|
|
126
|
+
className="rounded-md border border-border bg-transparent px-2 py-1 text-[12px] outline-none focus:border-accent"
|
|
127
|
+
>
|
|
128
|
+
{options.map((option) => (
|
|
129
|
+
<option key={option.value} value={option.value}>
|
|
130
|
+
{option.label}
|
|
131
|
+
</option>
|
|
132
|
+
))}
|
|
133
|
+
</select>
|
|
134
|
+
</label>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function useListPatch(list: List): PatchResult<ListInput> {
|
|
141
|
+
return usePatch(useUpdateList, list)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function ListHeading({ list }: { readonly list: List }): React.JSX.Element {
|
|
145
|
+
const { patch, error } = useListPatch(list)
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="min-w-0 flex-1">
|
|
149
|
+
{error !== null && (
|
|
150
|
+
<div className="mb-2">
|
|
151
|
+
<ErrorPanel error={error} />
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
<InlineEdit
|
|
155
|
+
value={list.name}
|
|
156
|
+
onChange={(name) => {
|
|
157
|
+
patch({ name })
|
|
158
|
+
}}
|
|
159
|
+
displayClassName="text-[22px] font-semibold tracking-tight text-ink not-italic"
|
|
160
|
+
emptyLabel="Untitled list"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function ListDescription({ list }: { readonly list: List }): React.JSX.Element {
|
|
167
|
+
const { patch, error } = useListPatch(list)
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div className="mb-6">
|
|
171
|
+
{error !== null && (
|
|
172
|
+
<div className="mb-2">
|
|
173
|
+
<ErrorPanel error={error} />
|
|
174
|
+
</div>
|
|
175
|
+
)}
|
|
176
|
+
<InlineEdit
|
|
177
|
+
value={list.description ?? ''}
|
|
178
|
+
onChange={(description) => {
|
|
179
|
+
patch({ description: description.length > 0 ? description : null })
|
|
180
|
+
}}
|
|
181
|
+
displayClassName="text-[13px] text-ink-muted not-italic"
|
|
182
|
+
emptyLabel="Add a description"
|
|
183
|
+
multiline
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function ListMembers({
|
|
190
|
+
listId,
|
|
191
|
+
targetType,
|
|
192
|
+
}: {
|
|
193
|
+
readonly listId: string
|
|
194
|
+
readonly targetType: RecordTargetType
|
|
195
|
+
}): React.JSX.Element {
|
|
196
|
+
const members = useListMembers(listId)
|
|
197
|
+
const addMember = useAddListMember()
|
|
198
|
+
const [adding, setAdding] = useState(false)
|
|
199
|
+
|
|
200
|
+
const attached = new Set(members.records.map((member) => member.targetId))
|
|
201
|
+
|
|
202
|
+
function add(targetId: string): void {
|
|
203
|
+
if (attached.has(targetId)) {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
addMember.run({ listId, input: { targetType, targetId } })
|
|
208
|
+
setAdding(false)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<section>
|
|
213
|
+
<SectionHeader
|
|
214
|
+
title="Members"
|
|
215
|
+
onAdd={() => {
|
|
216
|
+
setAdding((current) => !current)
|
|
217
|
+
}}
|
|
218
|
+
addLabel={`Add ${RECORD_TARGET_TYPE_LABELS[targetType].toLowerCase()}`}
|
|
219
|
+
/>
|
|
220
|
+
|
|
221
|
+
{adding && (
|
|
222
|
+
<div className="mb-3 flex gap-2">
|
|
223
|
+
<div className="min-w-0 flex-1">
|
|
224
|
+
<MemberPicker
|
|
225
|
+
targetType={targetType}
|
|
226
|
+
attached={attached}
|
|
227
|
+
onPick={add}
|
|
228
|
+
/>
|
|
229
|
+
</div>
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
onClick={() => {
|
|
233
|
+
setAdding(false)
|
|
234
|
+
}}
|
|
235
|
+
className="shrink-0 rounded-md px-2.5 py-1.5 text-[12px] font-medium text-ink-muted hover:text-ink"
|
|
236
|
+
>
|
|
237
|
+
Cancel
|
|
238
|
+
</button>
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
{addMember.error !== null && (
|
|
243
|
+
<div className="mb-3">
|
|
244
|
+
<ErrorPanel error={addMember.error} />
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
{members.error !== null && <ErrorPanel error={members.error} />}
|
|
248
|
+
|
|
249
|
+
{members.isLoading ? (
|
|
250
|
+
<LoadingPanel label="Loading members…" />
|
|
251
|
+
) : members.records.length === 0 && !adding ? (
|
|
252
|
+
<p className="text-[13px] text-ink-faint">No members yet.</p>
|
|
253
|
+
) : (
|
|
254
|
+
<>
|
|
255
|
+
<Paginator list={members} placement="top" />
|
|
256
|
+
<ul className="overflow-hidden rounded-md border border-border">
|
|
257
|
+
{members.records.map((member) => (
|
|
258
|
+
<MemberRow key={member.id} listId={listId} member={member} />
|
|
259
|
+
))}
|
|
260
|
+
</ul>
|
|
261
|
+
</>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
<Paginator list={members} />
|
|
265
|
+
</section>
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function MemberRow({
|
|
270
|
+
listId,
|
|
271
|
+
member,
|
|
272
|
+
}: {
|
|
273
|
+
readonly listId: string
|
|
274
|
+
readonly member: ListMember
|
|
275
|
+
}): React.JSX.Element {
|
|
276
|
+
const remove = useRemoveListMember()
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<li className="border-b border-border px-4 py-3 last:border-0">
|
|
280
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
281
|
+
<Link
|
|
282
|
+
to={detailPathFor(member.targetType, member.targetId)}
|
|
283
|
+
className="text-left text-[13px] font-medium text-ink hover:text-accent"
|
|
284
|
+
>
|
|
285
|
+
{member.targetName ?? member.targetId}
|
|
286
|
+
</Link>
|
|
287
|
+
<button
|
|
288
|
+
type="button"
|
|
289
|
+
onClick={() => {
|
|
290
|
+
remove.run({ listId, id: member.id })
|
|
291
|
+
}}
|
|
292
|
+
className="text-[11px] font-medium text-danger hover:underline"
|
|
293
|
+
>
|
|
294
|
+
Remove
|
|
295
|
+
</button>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
{remove.error !== null && (
|
|
299
|
+
<div className="mt-2">
|
|
300
|
+
<ErrorPanel error={remove.error} />
|
|
301
|
+
</div>
|
|
302
|
+
)}
|
|
303
|
+
</li>
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Where a link on a member row should go. Every type has its own detail path. */
|
|
308
|
+
function detailPathFor(targetType: RecordTargetType, targetId: string): string {
|
|
309
|
+
switch (targetType) {
|
|
310
|
+
case 'person':
|
|
311
|
+
return `/people/${targetId}`
|
|
312
|
+
case 'company':
|
|
313
|
+
return `/companies/${targetId}`
|
|
314
|
+
case 'deal':
|
|
315
|
+
return `/deals/${targetId}`
|
|
316
|
+
case 'opportunity':
|
|
317
|
+
return `/opportunities/${targetId}`
|
|
318
|
+
case 'partnership':
|
|
319
|
+
return `/partnerships/${targetId}`
|
|
320
|
+
case 'raise':
|
|
321
|
+
return `/fundraising/${targetId}`
|
|
322
|
+
case 'enquiry':
|
|
323
|
+
return `/enquiries/${targetId}`
|
|
324
|
+
case 'candidate':
|
|
325
|
+
// A candidate has no page of its own; the person's does.
|
|
326
|
+
return `/hiring`
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* A "search or pick" combobox that queries the collection matching the list's
|
|
332
|
+
* type. Each type gets its own component so React sees a stable set of hook
|
|
333
|
+
* calls per render.
|
|
334
|
+
*/
|
|
335
|
+
function MemberPicker({
|
|
336
|
+
targetType,
|
|
337
|
+
attached,
|
|
338
|
+
onPick,
|
|
339
|
+
}: {
|
|
340
|
+
readonly targetType: RecordTargetType
|
|
341
|
+
readonly attached: ReadonlySet<string>
|
|
342
|
+
readonly onPick: (id: string) => void
|
|
343
|
+
}): React.JSX.Element {
|
|
344
|
+
switch (targetType) {
|
|
345
|
+
case 'person':
|
|
346
|
+
return <PeoplePicker attached={attached} onPick={onPick} />
|
|
347
|
+
case 'company':
|
|
348
|
+
return <CompaniesPicker attached={attached} onPick={onPick} />
|
|
349
|
+
case 'deal':
|
|
350
|
+
return <DealsPicker attached={attached} onPick={onPick} />
|
|
351
|
+
case 'opportunity':
|
|
352
|
+
return <OpportunitiesPicker attached={attached} onPick={onPick} />
|
|
353
|
+
case 'partnership':
|
|
354
|
+
return <PartnershipsPicker attached={attached} onPick={onPick} />
|
|
355
|
+
case 'raise':
|
|
356
|
+
return <RaisesPicker attached={attached} onPick={onPick} />
|
|
357
|
+
case 'enquiry':
|
|
358
|
+
return <EnquiriesPicker attached={attached} onPick={onPick} />
|
|
359
|
+
case 'candidate':
|
|
360
|
+
return (
|
|
361
|
+
<p className="text-[12px] text-ink-faint">
|
|
362
|
+
Candidate lists have no inline picker in this release. Add candidacies from a role's
|
|
363
|
+
page, then attach them here through the API.
|
|
364
|
+
</p>
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
interface PickerProps {
|
|
370
|
+
readonly attached: ReadonlySet<string>
|
|
371
|
+
readonly onPick: (id: string) => void
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function PeoplePicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
375
|
+
const [search, setSearch] = useState('')
|
|
376
|
+
const records = usePeople({ term: trimmedOrUndefined(search) })
|
|
377
|
+
const options: readonly SearchOption[] = records.records
|
|
378
|
+
.filter((row) => !attached.has(row.id))
|
|
379
|
+
.map((row) => ({ id: row.id, label: row.name, meta: row.email ?? undefined }))
|
|
380
|
+
|
|
381
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function CompaniesPicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
385
|
+
const [search, setSearch] = useState('')
|
|
386
|
+
const records = useCompanies({ term: trimmedOrUndefined(search) })
|
|
387
|
+
const options: readonly SearchOption[] = records.records
|
|
388
|
+
.filter((row) => !attached.has(row.id))
|
|
389
|
+
.map((row) => ({ id: row.id, label: row.name, meta: row.domain ?? undefined }))
|
|
390
|
+
|
|
391
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function DealsPicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
395
|
+
const [search, setSearch] = useState('')
|
|
396
|
+
const records = useDeals({ term: trimmedOrUndefined(search) })
|
|
397
|
+
const options: readonly SearchOption[] = records.records
|
|
398
|
+
.filter((row) => !attached.has(row.id))
|
|
399
|
+
.map((row) => ({ id: row.id, label: row.name }))
|
|
400
|
+
|
|
401
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function OpportunitiesPicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
405
|
+
const [search, setSearch] = useState('')
|
|
406
|
+
const records = useOpportunities({ term: trimmedOrUndefined(search) })
|
|
407
|
+
const options: readonly SearchOption[] = records.records
|
|
408
|
+
.filter((row) => !attached.has(row.id))
|
|
409
|
+
.map((row) => ({ id: row.id, label: row.name }))
|
|
410
|
+
|
|
411
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function PartnershipsPicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
415
|
+
const [search, setSearch] = useState('')
|
|
416
|
+
const records = usePartnerships({ term: trimmedOrUndefined(search) })
|
|
417
|
+
const options: readonly SearchOption[] = records.records
|
|
418
|
+
.filter((row) => !attached.has(row.id))
|
|
419
|
+
.map((row) => ({ id: row.id, label: row.name }))
|
|
420
|
+
|
|
421
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function RaisesPicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
425
|
+
const [search, setSearch] = useState('')
|
|
426
|
+
const records = useRaises({ term: trimmedOrUndefined(search) })
|
|
427
|
+
const options: readonly SearchOption[] = records.records
|
|
428
|
+
.filter((row) => !attached.has(row.id))
|
|
429
|
+
.map((row) => ({ id: row.id, label: row.name }))
|
|
430
|
+
|
|
431
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function EnquiriesPicker({ attached, onPick }: PickerProps): React.JSX.Element {
|
|
435
|
+
const [search, setSearch] = useState('')
|
|
436
|
+
const records = useEnquiries({ term: trimmedOrUndefined(search) })
|
|
437
|
+
const options: readonly SearchOption[] = records.records
|
|
438
|
+
.filter((row) => !attached.has(row.id))
|
|
439
|
+
.map((row) => ({ id: row.id, label: row.name }))
|
|
440
|
+
|
|
441
|
+
return <PickerBox onQueryChange={setSearch} onPick={onPick} options={options} />
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function PickerBox({
|
|
445
|
+
onQueryChange,
|
|
446
|
+
onPick,
|
|
447
|
+
options,
|
|
448
|
+
}: {
|
|
449
|
+
readonly onQueryChange: (value: string) => void
|
|
450
|
+
readonly onPick: (id: string) => void
|
|
451
|
+
readonly options: readonly SearchOption[]
|
|
452
|
+
}): React.JSX.Element {
|
|
453
|
+
return (
|
|
454
|
+
<EntitySearch
|
|
455
|
+
options={options}
|
|
456
|
+
value=""
|
|
457
|
+
onChange={onPick}
|
|
458
|
+
onQueryChange={onQueryChange}
|
|
459
|
+
placeholder="Search…"
|
|
460
|
+
emptyMessage="No matches"
|
|
461
|
+
size="md"
|
|
462
|
+
/>
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function trimmedOrUndefined(value: string): string | undefined {
|
|
467
|
+
const trimmed = value.trim()
|
|
468
|
+
|
|
469
|
+
return trimmed.length > 0 ? trimmed : undefined
|
|
470
|
+
}
|