@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,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createCustomFieldDefinitionBody,
|
|
3
|
+
customFieldDefinitionBody,
|
|
4
|
+
customFieldDefinitionSchema,
|
|
5
|
+
} from '@kelpie/schemas'
|
|
6
|
+
import type {
|
|
7
|
+
CreateCustomFieldDefinitionInput,
|
|
8
|
+
CustomFieldDefinition,
|
|
9
|
+
CustomFieldDefinitionInput,
|
|
10
|
+
CustomFieldObjectType,
|
|
11
|
+
} from '@kelpie/schemas'
|
|
12
|
+
|
|
13
|
+
import type { QueryParameters } from '../client.ts'
|
|
14
|
+
import { createResourceHooks } from '../resource.ts'
|
|
15
|
+
import type {
|
|
16
|
+
ListOptions,
|
|
17
|
+
MutationResult,
|
|
18
|
+
RecordListResult,
|
|
19
|
+
RecordResult,
|
|
20
|
+
UpdateArguments,
|
|
21
|
+
} from '../resource.ts'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* `/v1/custom_fields`. Workspace-defined field definitions.
|
|
25
|
+
*
|
|
26
|
+
* A record's `custom_fields` values live on that record's own resource — this
|
|
27
|
+
* hook family manages only the definitions. `alsoInvalidates` wakes every
|
|
28
|
+
* pipeline record list because a new definition changes what shows up on their
|
|
29
|
+
* detail pages.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const customFields = createResourceHooks<
|
|
33
|
+
CustomFieldDefinition,
|
|
34
|
+
CreateCustomFieldDefinitionInput,
|
|
35
|
+
CustomFieldDefinitionInput
|
|
36
|
+
>({
|
|
37
|
+
name: 'custom_fields',
|
|
38
|
+
path: '/custom_fields',
|
|
39
|
+
decode: customFieldDefinitionSchema.parse,
|
|
40
|
+
createBody: createCustomFieldDefinitionBody,
|
|
41
|
+
updateBody: customFieldDefinitionBody,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/** The documented filters on `GET /v1/custom_fields`. */
|
|
45
|
+
export interface CustomFieldFilters {
|
|
46
|
+
readonly term?: string | undefined
|
|
47
|
+
readonly objectType?: CustomFieldObjectType | undefined
|
|
48
|
+
readonly limit?: number | undefined
|
|
49
|
+
readonly sort?: string | undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function customFieldQuery(filters: CustomFieldFilters): QueryParameters {
|
|
53
|
+
return {
|
|
54
|
+
q: filters.term,
|
|
55
|
+
object_type: filters.objectType,
|
|
56
|
+
limit: filters.limit,
|
|
57
|
+
sort: filters.sort,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function useCustomFields(
|
|
62
|
+
filters: CustomFieldFilters = {},
|
|
63
|
+
options: ListOptions = {},
|
|
64
|
+
): RecordListResult<CustomFieldDefinition> {
|
|
65
|
+
return customFields.useList(customFieldQuery(filters), options)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function useCustomField(id: string | undefined): RecordResult<CustomFieldDefinition> {
|
|
69
|
+
return customFields.useRecord(id)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useCreateCustomField(): MutationResult<
|
|
73
|
+
CreateCustomFieldDefinitionInput,
|
|
74
|
+
CustomFieldDefinition
|
|
75
|
+
> {
|
|
76
|
+
return customFields.useCreate()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useUpdateCustomField(): MutationResult<
|
|
80
|
+
UpdateArguments<CustomFieldDefinitionInput>,
|
|
81
|
+
CustomFieldDefinition
|
|
82
|
+
> {
|
|
83
|
+
return customFields.useUpdate()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function useDeleteCustomField(): MutationResult<string, void> {
|
|
87
|
+
return customFields.useRemove()
|
|
88
|
+
}
|
|
@@ -33,6 +33,8 @@ export interface DecisionFilters {
|
|
|
33
33
|
readonly targetId?: string | undefined
|
|
34
34
|
/** Matches the body, the rationale, the target type, and the target's name. */
|
|
35
35
|
readonly term?: string | undefined
|
|
36
|
+
/** `field` ascending, `-field` descending. Only `decided_at`, `created_at`, `updated_at` are sortable. */
|
|
37
|
+
readonly sort?: string | undefined
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function decisionQuery(filters: DecisionFilters): QueryParameters {
|
|
@@ -40,6 +42,7 @@ function decisionQuery(filters: DecisionFilters): QueryParameters {
|
|
|
40
42
|
target_type: filters.targetType,
|
|
41
43
|
target_id: filters.targetId,
|
|
42
44
|
q: filters.term,
|
|
45
|
+
sort: filters.sort,
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { relinkEmailDomainsCountsSchema } from '@kelpie/schemas'
|
|
2
|
+
import type { RelinkEmailDomainsCounts } from '@kelpie/schemas'
|
|
3
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
4
|
+
|
|
5
|
+
import { useApiClient } from '../context.ts'
|
|
6
|
+
import type { MutationResult } from '../resource.ts'
|
|
7
|
+
import { asMutationResult } from './mutation.ts'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `POST /v1/workspaces/:id/relink-email-domains`: sweep every Company that
|
|
11
|
+
* carries a domain and stub a Position for every workspace Person whose email
|
|
12
|
+
* matches. Idempotent — a follow-up run creates no new rows.
|
|
13
|
+
*
|
|
14
|
+
* On success invalidates positions, activities, and the person-scoped /
|
|
15
|
+
* company-scoped list joins so a page open on any of those refreshes with the
|
|
16
|
+
* new links.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export interface RelinkInput {
|
|
20
|
+
readonly workspaceId: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useRelinkEmailDomains(): MutationResult<RelinkInput, RelinkEmailDomainsCounts> {
|
|
24
|
+
const client = useApiClient()
|
|
25
|
+
const cache = useQueryClient()
|
|
26
|
+
const mutation = useMutation({
|
|
27
|
+
mutationFn: ({ workspaceId }: RelinkInput) =>
|
|
28
|
+
client.post(
|
|
29
|
+
`/workspaces/${workspaceId}/relink-email-domains`,
|
|
30
|
+
{},
|
|
31
|
+
relinkEmailDomainsCountsSchema.parse,
|
|
32
|
+
),
|
|
33
|
+
onSuccess: async () => {
|
|
34
|
+
await Promise.all(
|
|
35
|
+
['positions', 'activities', 'companies', 'people'].map((name) =>
|
|
36
|
+
cache.invalidateQueries({ queryKey: [name] }),
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return asMutationResult(mutation)
|
|
43
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { dealSchema, enquiryBody, enquirySchema } from '@kelpie/schemas'
|
|
2
|
+
import type { Deal, Enquiry, EnquiryInput } from '@kelpie/schemas'
|
|
3
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
4
|
+
|
|
5
|
+
import type { QueryParameters } from '../client.ts'
|
|
6
|
+
import { useApiClient } from '../context.ts'
|
|
7
|
+
import { toError } from '../errors.ts'
|
|
8
|
+
import { createResourceHooks } from '../resource.ts'
|
|
9
|
+
import type {
|
|
10
|
+
ListOptions,
|
|
11
|
+
MutationResult,
|
|
12
|
+
RecordListResult,
|
|
13
|
+
RecordResult,
|
|
14
|
+
UpdateArguments,
|
|
15
|
+
} from '../resource.ts'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `/v1/enquiries`. Top-of-funnel: what a website form or an email drops onto
|
|
19
|
+
* the pipeline before it becomes a deal. Create needs only a name; the API
|
|
20
|
+
* lands the enquiry in the pipeline's first open stage and hands it to the
|
|
21
|
+
* caller as owner.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export interface CreateEnquiryInput extends EnquiryInput {
|
|
25
|
+
readonly name: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const enquiries = createResourceHooks<Enquiry, CreateEnquiryInput, EnquiryInput>({
|
|
29
|
+
name: 'enquiries',
|
|
30
|
+
path: '/enquiries',
|
|
31
|
+
decode: enquirySchema.parse,
|
|
32
|
+
createBody: enquiryBody,
|
|
33
|
+
updateBody: enquiryBody,
|
|
34
|
+
// Every write here emits an activity server-side, in the same transaction,
|
|
35
|
+
// so a timeline rendered on this record is stale the moment the write lands.
|
|
36
|
+
alsoInvalidates: ['activities'],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/** The documented filters on `GET /v1/enquiries`. */
|
|
40
|
+
export interface EnquiryFilters {
|
|
41
|
+
/** Matches the enquiry's name, source, summary, tags, and its company's name. */
|
|
42
|
+
readonly term?: string | undefined
|
|
43
|
+
/** Exact sources ("Website", "Email", …). Repeats on the wire. */
|
|
44
|
+
readonly sources?: readonly string[] | undefined
|
|
45
|
+
/** Enquiries at any of these companies. Repeats on the wire. */
|
|
46
|
+
readonly companyIds?: readonly string[] | undefined
|
|
47
|
+
/** Enquiries sitting in any of these stages. Repeats on the wire. */
|
|
48
|
+
readonly stageIds?: readonly string[] | undefined
|
|
49
|
+
/** Enquiries any of these people are on. Repeats on the wire. */
|
|
50
|
+
readonly personIds?: readonly string[] | undefined
|
|
51
|
+
readonly limit?: number | undefined
|
|
52
|
+
/** `field` ascending, `-field` descending. Only `name`, `created_at`, `updated_at` are sortable. */
|
|
53
|
+
readonly sort?: string | undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function enquiryQuery(filters: EnquiryFilters): QueryParameters {
|
|
57
|
+
return {
|
|
58
|
+
q: filters.term,
|
|
59
|
+
source: filters.sources,
|
|
60
|
+
company_id: filters.companyIds,
|
|
61
|
+
stage_id: filters.stageIds,
|
|
62
|
+
person_id: filters.personIds,
|
|
63
|
+
limit: filters.limit,
|
|
64
|
+
sort: filters.sort,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function useEnquiries(
|
|
69
|
+
filters: EnquiryFilters = {},
|
|
70
|
+
options: ListOptions = {},
|
|
71
|
+
): RecordListResult<Enquiry> {
|
|
72
|
+
return enquiries.useList(enquiryQuery(filters), options)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function useEnquiry(id: string | undefined): RecordResult<Enquiry> {
|
|
76
|
+
return enquiries.useRecord(id)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useCreateEnquiry(): MutationResult<CreateEnquiryInput, Enquiry> {
|
|
80
|
+
return enquiries.useCreate()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function useUpdateEnquiry(): MutationResult<UpdateArguments<EnquiryInput>, Enquiry> {
|
|
84
|
+
return enquiries.useUpdate()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function useDeleteEnquiry(): MutationResult<string, void> {
|
|
88
|
+
return enquiries.useRemove()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* `POST /v1/enquiries/:id/convert`. Returns the freshly created deal, so the
|
|
93
|
+
* caller can navigate straight to it. 409 if the enquiry has already been
|
|
94
|
+
* converted; 422 if it has no linked company yet.
|
|
95
|
+
*/
|
|
96
|
+
export function useConvertEnquiry(): MutationResult<string, Deal> {
|
|
97
|
+
const client = useApiClient()
|
|
98
|
+
const queryClient = useQueryClient()
|
|
99
|
+
const mutation = useMutation({
|
|
100
|
+
mutationFn: (id: string) =>
|
|
101
|
+
client.post(`/enquiries/${id}/convert`, {}, dealSchema.parse),
|
|
102
|
+
onSuccess: () => {
|
|
103
|
+
// The enquiry, deals list, activities on both sides are all stale.
|
|
104
|
+
void queryClient.invalidateQueries({ queryKey: ['enquiries'] })
|
|
105
|
+
void queryClient.invalidateQueries({ queryKey: ['deals'] })
|
|
106
|
+
void queryClient.invalidateQueries({ queryKey: ['activities'] })
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
run: (input) => {
|
|
112
|
+
mutation.mutate(input)
|
|
113
|
+
},
|
|
114
|
+
runAsync: (input) => mutation.mutateAsync(input),
|
|
115
|
+
isPending: mutation.isPending,
|
|
116
|
+
error: toError(mutation.error),
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -5,10 +5,9 @@ import type {
|
|
|
5
5
|
FormFieldInput,
|
|
6
6
|
FormInput,
|
|
7
7
|
FormSubmission,
|
|
8
|
+
FormSubmissionLinkTarget,
|
|
8
9
|
} from '@kelpie/schemas'
|
|
9
10
|
import {
|
|
10
|
-
keepPreviousData,
|
|
11
|
-
useInfiniteQuery,
|
|
12
11
|
useMutation,
|
|
13
12
|
useQuery,
|
|
14
13
|
useQueryClient,
|
|
@@ -16,9 +15,10 @@ import {
|
|
|
16
15
|
|
|
17
16
|
import { isRecord } from '../json.ts'
|
|
18
17
|
import type { QueryParameters } from '../client.ts'
|
|
18
|
+
import { ApiError } from '../client.ts'
|
|
19
19
|
import { useApiClient } from '../context.ts'
|
|
20
20
|
import { toError } from '../errors.ts'
|
|
21
|
-
import { createResourceHooks } from '../resource.ts'
|
|
21
|
+
import { createResourceHooks, usePagedList } from '../resource.ts'
|
|
22
22
|
import type {
|
|
23
23
|
MutationResult,
|
|
24
24
|
RecordListResult,
|
|
@@ -139,34 +139,72 @@ export function useDeleteForm(): MutationResult<string, void> {
|
|
|
139
139
|
* The result shape is the same, so a page cannot tell the difference.
|
|
140
140
|
*/
|
|
141
141
|
export function useFormSubmissions(formId: string | undefined): RecordListResult<FormSubmission> {
|
|
142
|
-
|
|
143
|
-
const result = useInfiniteQuery({
|
|
142
|
+
return usePagedList<FormSubmission>({
|
|
144
143
|
queryKey: ['forms', 'submissions', formId ?? ''],
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}),
|
|
149
|
-
initialPageParam: null as string | null,
|
|
150
|
-
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
151
|
-
placeholderData: keepPreviousData,
|
|
144
|
+
path: `/forms/${formId ?? ''}/submissions`,
|
|
145
|
+
decode: formSubmissionSchema.parse,
|
|
146
|
+
query: {},
|
|
152
147
|
enabled: formId !== undefined,
|
|
153
148
|
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Submissions that touched one record, newest first.
|
|
153
|
+
*
|
|
154
|
+
* Powers the Forms tab on every record type a submission can name (person,
|
|
155
|
+
* company, position, deal, opportunity, partnership, enquiry). Returns an
|
|
156
|
+
* empty page for a record no submission has ever named — the server does no
|
|
157
|
+
* per-target existence check, so a stale id reads the same as a real one.
|
|
158
|
+
*/
|
|
159
|
+
export function useFormSubmissionsForRecord(
|
|
160
|
+
targetType: FormSubmissionLinkTarget | undefined,
|
|
161
|
+
targetId: string | undefined,
|
|
162
|
+
): RecordListResult<FormSubmission> {
|
|
163
|
+
return usePagedList<FormSubmission>({
|
|
164
|
+
queryKey: ['form-submissions', 'by-target', targetType ?? '', targetId ?? ''],
|
|
165
|
+
path: '/form-submissions',
|
|
166
|
+
decode: formSubmissionSchema.parse,
|
|
167
|
+
query: { target_type: targetType, target_id: targetId },
|
|
168
|
+
enabled: targetType !== undefined && targetId !== undefined,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* One submission on a form.
|
|
174
|
+
*
|
|
175
|
+
* Nested under the form rather than a top-level collection: a submission id is
|
|
176
|
+
* only meaningful with its form (the field labels live there), and the path
|
|
177
|
+
* mirrors the list endpoint above it.
|
|
178
|
+
*/
|
|
179
|
+
export function useFormSubmission(
|
|
180
|
+
formId: string | undefined,
|
|
181
|
+
submissionId: string | undefined,
|
|
182
|
+
): RecordResult<FormSubmission> {
|
|
183
|
+
const client = useApiClient()
|
|
184
|
+
const result = useQuery({
|
|
185
|
+
queryKey: ['forms', 'submissions', formId ?? '', submissionId ?? ''],
|
|
186
|
+
queryFn: () =>
|
|
187
|
+
client.get(
|
|
188
|
+
`/forms/${formId ?? ''}/submissions/${submissionId ?? ''}`,
|
|
189
|
+
formSubmissionSchema.parse,
|
|
190
|
+
),
|
|
191
|
+
enabled: formId !== undefined && submissionId !== undefined,
|
|
192
|
+
})
|
|
154
193
|
|
|
155
194
|
return {
|
|
156
|
-
|
|
157
|
-
isLoading: result.isPending && formId !== undefined,
|
|
195
|
+
record: result.data,
|
|
196
|
+
isLoading: result.isPending && formId !== undefined && submissionId !== undefined,
|
|
197
|
+
isNotFound: result.error instanceof ApiError && result.error.status === 404,
|
|
158
198
|
error: toError(result.error),
|
|
159
|
-
hasMore: result.hasNextPage,
|
|
160
|
-
isLoadingMore: result.isFetchingNextPage,
|
|
161
|
-
loadMore: () => {
|
|
162
|
-
void result.fetchNextPage()
|
|
163
|
-
},
|
|
164
199
|
}
|
|
165
200
|
}
|
|
166
201
|
|
|
167
202
|
/** What a customer pastes into their site, built server-side from the request origin. */
|
|
168
203
|
export interface EmbedSnippets {
|
|
204
|
+
/** Standalone hosted page (brand chrome). */
|
|
169
205
|
readonly url: string
|
|
206
|
+
/** Bare document the iframe snippets load (fields only). */
|
|
207
|
+
readonly embedUrl: string
|
|
170
208
|
readonly iframeSnippet: string
|
|
171
209
|
readonly scriptSnippet: string
|
|
172
210
|
}
|
|
@@ -175,6 +213,7 @@ function decodeSnippets(value: unknown): EmbedSnippets {
|
|
|
175
213
|
if (
|
|
176
214
|
!isRecord(value) ||
|
|
177
215
|
typeof value.url !== 'string' ||
|
|
216
|
+
typeof value.embed_url !== 'string' ||
|
|
178
217
|
typeof value.iframe_snippet !== 'string' ||
|
|
179
218
|
typeof value.script_snippet !== 'string'
|
|
180
219
|
) {
|
|
@@ -183,6 +222,7 @@ function decodeSnippets(value: unknown): EmbedSnippets {
|
|
|
183
222
|
|
|
184
223
|
return {
|
|
185
224
|
url: value.url,
|
|
225
|
+
embedUrl: value.embed_url,
|
|
186
226
|
iframeSnippet: value.iframe_snippet,
|
|
187
227
|
scriptSnippet: value.script_snippet,
|
|
188
228
|
}
|
|
@@ -42,7 +42,7 @@ function handbookQuery(filters: HandbookPageFilters): QueryParameters {
|
|
|
42
42
|
* The same call the kanban board makes for its stages, for the same reason: a
|
|
43
43
|
* tree cannot be drawn from part of itself, and a page whose parent is on the
|
|
44
44
|
* next page of results would render at the top level as though somebody had
|
|
45
|
-
* moved it there. The caller reads `
|
|
45
|
+
* moved it there. The caller reads `hasNext` and says so rather than drawing a
|
|
46
46
|
* tree with holes in it.
|
|
47
47
|
*/
|
|
48
48
|
export function useHandbookPages(filters: HandbookPageFilters = {}): RecordListResult<HandbookPage> {
|
|
@@ -34,6 +34,11 @@ export interface CreateImportJobInput {
|
|
|
34
34
|
/** People import only: what to do with a row naming a company that is not here yet. */
|
|
35
35
|
readonly onMissingCompany: OnMissingCompany
|
|
36
36
|
readonly matchKeyId: string
|
|
37
|
+
/**
|
|
38
|
+
* People import only: the consent purpose a row's `consent_status` grants.
|
|
39
|
+
* Required whenever the map names `consent_status` or `consent_at`.
|
|
40
|
+
*/
|
|
41
|
+
readonly consentPurposeId?: string | null
|
|
37
42
|
/**
|
|
38
43
|
* Absent on the first upload, so the server derives one from the source preset
|
|
39
44
|
* and the file's own headers. The mapping screen sends the corrected map back
|
|
@@ -57,6 +62,10 @@ function jobForm(input: CreateImportJobInput): FormData {
|
|
|
57
62
|
form.set('column_map', JSON.stringify(input.columnMap))
|
|
58
63
|
}
|
|
59
64
|
|
|
65
|
+
if (input.consentPurposeId != null && input.consentPurposeId.length > 0) {
|
|
66
|
+
form.set('consent_purpose_id', input.consentPurposeId)
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
return form
|
|
61
70
|
}
|
|
62
71
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addListMemberBody,
|
|
3
|
+
listMemberSchema,
|
|
4
|
+
listMembershipSchema,
|
|
5
|
+
} from '@kelpie/schemas'
|
|
6
|
+
import type {
|
|
7
|
+
AddListMemberInput,
|
|
8
|
+
ListMember,
|
|
9
|
+
ListMembership,
|
|
10
|
+
RecordTargetType,
|
|
11
|
+
} from '@kelpie/schemas'
|
|
12
|
+
import {
|
|
13
|
+
useMutation,
|
|
14
|
+
useQuery,
|
|
15
|
+
useQueryClient,
|
|
16
|
+
} from '@tanstack/react-query'
|
|
17
|
+
import type { InfiniteData } from '@tanstack/react-query'
|
|
18
|
+
|
|
19
|
+
import type { Page } from '../client.ts'
|
|
20
|
+
import { useApiClient } from '../context.ts'
|
|
21
|
+
import { toError } from '../errors.ts'
|
|
22
|
+
import { usePagedList } from '../resource.ts'
|
|
23
|
+
import type { MutationResult, RecordListResult } from '../resource.ts'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `/v1/lists/:id/members`. Nested per list, so the standard resource hook cannot
|
|
27
|
+
* mint the path. Built by hand with the same read/write cache invalidation
|
|
28
|
+
* pattern the other resources use.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type MembersData = InfiniteData<Page<ListMember>, string | null>
|
|
32
|
+
|
|
33
|
+
function membersKey(listId: string): readonly unknown[] {
|
|
34
|
+
return ['lists', 'members', listId]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function membershipsKey(targetType: string, targetId: string): readonly unknown[] {
|
|
38
|
+
return ['lists', 'memberships', targetType, targetId]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The list-level entries whose cached counts change when a member is added or removed. */
|
|
42
|
+
function listsInvalidations(): readonly unknown[] {
|
|
43
|
+
return ['lists', 'list']
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ListMembershipsResult {
|
|
47
|
+
readonly memberships: readonly ListMembership[]
|
|
48
|
+
readonly isLoading: boolean
|
|
49
|
+
readonly error: Error | null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* "Which lists is this record on?"
|
|
54
|
+
*
|
|
55
|
+
* A plain `useQuery` rather than the paged read: the endpoint is not paged.
|
|
56
|
+
* The invalidation key overlaps with `useListMembers` so adding or removing on
|
|
57
|
+
* either side refreshes both views.
|
|
58
|
+
*/
|
|
59
|
+
export function useListMembershipsFor(
|
|
60
|
+
targetType: RecordTargetType | undefined,
|
|
61
|
+
targetId: string | undefined,
|
|
62
|
+
): ListMembershipsResult {
|
|
63
|
+
const client = useApiClient()
|
|
64
|
+
const enabled = targetType !== undefined && targetId !== undefined
|
|
65
|
+
const result = useQuery({
|
|
66
|
+
queryKey: membershipsKey(targetType ?? '', targetId ?? ''),
|
|
67
|
+
queryFn: async () => {
|
|
68
|
+
const page = await client.list(
|
|
69
|
+
'/list-memberships',
|
|
70
|
+
listMembershipSchema.parse,
|
|
71
|
+
{ target_type: targetType, target_id: targetId },
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return page.items
|
|
75
|
+
},
|
|
76
|
+
enabled,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
memberships: result.data ?? [],
|
|
81
|
+
isLoading: result.isPending && enabled,
|
|
82
|
+
error: toError(result.error),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function useListMembers(listId: string | undefined): RecordListResult<ListMember> {
|
|
87
|
+
return usePagedList<ListMember>({
|
|
88
|
+
queryKey: membersKey(listId ?? ''),
|
|
89
|
+
path: `/lists/${listId ?? ''}/members`,
|
|
90
|
+
decode: listMemberSchema.parse,
|
|
91
|
+
query: {},
|
|
92
|
+
enabled: listId !== undefined,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface AddMemberArguments {
|
|
97
|
+
readonly listId: string
|
|
98
|
+
readonly input: AddListMemberInput
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function useAddListMember(): MutationResult<AddMemberArguments, ListMember> {
|
|
102
|
+
const client = useApiClient()
|
|
103
|
+
const queryClient = useQueryClient()
|
|
104
|
+
const mutation = useMutation({
|
|
105
|
+
mutationFn: ({ listId, input }: AddMemberArguments) =>
|
|
106
|
+
client.post(`/lists/${listId}/members`, addListMemberBody(input), listMemberSchema.parse),
|
|
107
|
+
onSettled: (_record, _error, { listId }) => {
|
|
108
|
+
void queryClient.invalidateQueries({ queryKey: membersKey(listId) })
|
|
109
|
+
void queryClient.invalidateQueries({ queryKey: listsInvalidations() })
|
|
110
|
+
void queryClient.invalidateQueries({ queryKey: ['lists', 'memberships'] })
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
run: (input) => {
|
|
116
|
+
mutation.mutate(input)
|
|
117
|
+
},
|
|
118
|
+
runAsync: (input) => mutation.mutateAsync(input),
|
|
119
|
+
isPending: mutation.isPending,
|
|
120
|
+
error: toError(mutation.error),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface RemoveMemberArguments {
|
|
125
|
+
readonly listId: string
|
|
126
|
+
readonly id: string
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function useRemoveListMember(): MutationResult<RemoveMemberArguments, void> {
|
|
130
|
+
const client = useApiClient()
|
|
131
|
+
const queryClient = useQueryClient()
|
|
132
|
+
const mutation = useMutation({
|
|
133
|
+
mutationFn: ({ listId, id }: RemoveMemberArguments) =>
|
|
134
|
+
client.delete(`/lists/${listId}/members/${id}`),
|
|
135
|
+
onMutate: async ({ listId, id }) => {
|
|
136
|
+
await queryClient.cancelQueries({ queryKey: membersKey(listId) })
|
|
137
|
+
const previous = queryClient.getQueryData<MembersData>(membersKey(listId))
|
|
138
|
+
|
|
139
|
+
queryClient.setQueryData<MembersData>(membersKey(listId), (data) =>
|
|
140
|
+
data === undefined
|
|
141
|
+
? data
|
|
142
|
+
: {
|
|
143
|
+
...data,
|
|
144
|
+
pages: data.pages.map((page) => ({
|
|
145
|
+
...page,
|
|
146
|
+
items: page.items.filter((item) => item.id !== id),
|
|
147
|
+
})),
|
|
148
|
+
},
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return { previous }
|
|
152
|
+
},
|
|
153
|
+
onError: (_error, { listId }, context) => {
|
|
154
|
+
if (context?.previous !== undefined) {
|
|
155
|
+
queryClient.setQueryData(membersKey(listId), context.previous)
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
onSettled: (_record, _error, { listId }) => {
|
|
159
|
+
void queryClient.invalidateQueries({ queryKey: membersKey(listId) })
|
|
160
|
+
void queryClient.invalidateQueries({ queryKey: listsInvalidations() })
|
|
161
|
+
void queryClient.invalidateQueries({ queryKey: ['lists', 'memberships'] })
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
run: (input) => {
|
|
167
|
+
mutation.mutate(input)
|
|
168
|
+
},
|
|
169
|
+
runAsync: (input) => mutation.mutateAsync(input),
|
|
170
|
+
isPending: mutation.isPending,
|
|
171
|
+
error: toError(mutation.error),
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createListBody, listBody, listSchema } from '@kelpie/schemas'
|
|
2
|
+
import type { CreateListInput, List, ListInput, RecordTargetType } from '@kelpie/schemas'
|
|
3
|
+
|
|
4
|
+
import type { QueryParameters } from '../client.ts'
|
|
5
|
+
import { createResourceHooks } from '../resource.ts'
|
|
6
|
+
import type {
|
|
7
|
+
ListOptions,
|
|
8
|
+
MutationResult,
|
|
9
|
+
RecordListResult,
|
|
10
|
+
RecordResult,
|
|
11
|
+
UpdateArguments,
|
|
12
|
+
} from '../resource.ts'
|
|
13
|
+
|
|
14
|
+
/** `/v1/lists`. A list holds records of one type; the type is fixed at creation. */
|
|
15
|
+
|
|
16
|
+
const lists = createResourceHooks<List, CreateListInput, ListInput>({
|
|
17
|
+
name: 'lists',
|
|
18
|
+
path: '/lists',
|
|
19
|
+
decode: listSchema.parse,
|
|
20
|
+
createBody: createListBody,
|
|
21
|
+
updateBody: listBody,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export interface ListFilters {
|
|
25
|
+
readonly term?: string | undefined
|
|
26
|
+
readonly targetType?: RecordTargetType | undefined
|
|
27
|
+
readonly limit?: number | undefined
|
|
28
|
+
readonly sort?: string | undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function listsQuery(filters: ListFilters): QueryParameters {
|
|
32
|
+
return {
|
|
33
|
+
q: filters.term,
|
|
34
|
+
target_type: filters.targetType,
|
|
35
|
+
limit: filters.limit,
|
|
36
|
+
sort: filters.sort,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useLists(
|
|
41
|
+
filters: ListFilters = {},
|
|
42
|
+
options: ListOptions = {},
|
|
43
|
+
): RecordListResult<List> {
|
|
44
|
+
return lists.useList(listsQuery(filters), options)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function useList(id: string | undefined): RecordResult<List> {
|
|
48
|
+
return lists.useRecord(id)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function useCreateList(): MutationResult<CreateListInput, List> {
|
|
52
|
+
return lists.useCreate()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function useUpdateList(): MutationResult<UpdateArguments<ListInput>, List> {
|
|
56
|
+
return lists.useUpdate()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function useDeleteList(): MutationResult<string, void> {
|
|
60
|
+
return lists.useRemove()
|
|
61
|
+
}
|
|
@@ -41,6 +41,8 @@ export interface OpportunityFilters {
|
|
|
41
41
|
readonly companyIds?: readonly string[] | undefined
|
|
42
42
|
/** Opportunities sitting in any of these stages. Repeats on the wire. */
|
|
43
43
|
readonly stageIds?: readonly string[] | undefined
|
|
44
|
+
/** Opportunities any of these people are on. Repeats on the wire. */
|
|
45
|
+
readonly personIds?: readonly string[] | undefined
|
|
44
46
|
readonly limit?: number | undefined
|
|
45
47
|
/** `field` ascending, `-field` descending. Only `name`, `created_at`, `updated_at` are sortable. */
|
|
46
48
|
readonly sort?: string | undefined
|
|
@@ -52,6 +54,7 @@ function opportunityQuery(filters: OpportunityFilters): QueryParameters {
|
|
|
52
54
|
kind: filters.kinds,
|
|
53
55
|
company_id: filters.companyIds,
|
|
54
56
|
stage_id: filters.stageIds,
|
|
57
|
+
person_id: filters.personIds,
|
|
55
58
|
limit: filters.limit,
|
|
56
59
|
sort: filters.sort,
|
|
57
60
|
}
|
|
@@ -25,7 +25,10 @@ const people = createResourceHooks<Person, CreatePersonInput, PersonInput>({
|
|
|
25
25
|
updateBody: personBody,
|
|
26
26
|
// Every write here emits an activity server-side, in the same transaction, so
|
|
27
27
|
// a timeline rendered on this record is stale the moment the write lands.
|
|
28
|
-
|
|
28
|
+
// Setting or changing the email may also auto-link a Position, which changes
|
|
29
|
+
// both the person's position list and the `?person_id=` companies list that
|
|
30
|
+
// names the far side — without the latter, the new row renders as "Unknown".
|
|
31
|
+
alsoInvalidates: ['activities', 'positions', 'companies'],
|
|
29
32
|
})
|
|
30
33
|
|
|
31
34
|
/** The documented filters on `GET /v1/people`. There is no generic filter DSL. */
|