@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
|
@@ -22,10 +22,13 @@ import type {
|
|
|
22
22
|
ImportRowError,
|
|
23
23
|
ImportSource,
|
|
24
24
|
OnMissingCompany,
|
|
25
|
+
SampleDataCounts,
|
|
25
26
|
} from '@kelpie/schemas'
|
|
26
|
-
import { useState } from 'react'
|
|
27
|
-
import type { ChangeEvent, FormEvent, ReactNode } from 'react'
|
|
27
|
+
import { useEffect, useState } from 'react'
|
|
28
|
+
import type { ChangeEvent, DragEvent, FormEvent, ReactNode } from 'react'
|
|
28
29
|
|
|
30
|
+
import { useConsentPurposes } from '../../api/resources/consentPurposes.ts'
|
|
31
|
+
import { useRelinkEmailDomains } from '../../api/resources/emailDomainLinker.ts'
|
|
29
32
|
import {
|
|
30
33
|
saveCsv,
|
|
31
34
|
useCommitImportJob,
|
|
@@ -34,6 +37,8 @@ import {
|
|
|
34
37
|
useExportCsv,
|
|
35
38
|
useImportJob,
|
|
36
39
|
} from '../../api/resources/importJobs.ts'
|
|
40
|
+
import { useInstallSampleData } from '../../api/resources/sampleData.ts'
|
|
41
|
+
import { useSession } from '../../api/resources/session.ts'
|
|
37
42
|
import { PageHeader } from '../../components/PageHeader.tsx'
|
|
38
43
|
|
|
39
44
|
/**
|
|
@@ -49,13 +54,14 @@ import { PageHeader } from '../../components/PageHeader.tsx'
|
|
|
49
54
|
* `public/fixtures`, and none of those are part of the application.
|
|
50
55
|
*/
|
|
51
56
|
|
|
52
|
-
type WizardStep = 'source' | 'upload' | 'map' | 'result'
|
|
57
|
+
type WizardStep = 'source' | 'upload' | 'map' | 'result' | 'done'
|
|
53
58
|
|
|
54
59
|
const STEPS: readonly { readonly id: WizardStep; readonly label: string }[] = [
|
|
55
60
|
{ id: 'source', label: 'Source' },
|
|
56
61
|
{ id: 'upload', label: 'Upload' },
|
|
57
62
|
{ id: 'map', label: 'Map' },
|
|
58
63
|
{ id: 'result', label: 'Dry-run' },
|
|
64
|
+
{ id: 'done', label: 'Done' },
|
|
59
65
|
]
|
|
60
66
|
|
|
61
67
|
export function DataPage(): ReactNode {
|
|
@@ -68,6 +74,7 @@ export function DataPage(): ReactNode {
|
|
|
68
74
|
const [columnMap, setColumnMap] = useState<ImportColumnMap>({})
|
|
69
75
|
const [conflictMode, setConflictMode] = useState<ImportConflictMode>('skip')
|
|
70
76
|
const [onMissingCompany, setOnMissingCompany] = useState<OnMissingCompany>('skip')
|
|
77
|
+
const [consentPurposeId, setConsentPurposeId] = useState<string | null>(null)
|
|
71
78
|
const [matchKeyId, setMatchKeyId] = useState<string>(() => defaultMatchKeyId('companies'))
|
|
72
79
|
const [jobId, setJobId] = useState<string | undefined>(undefined)
|
|
73
80
|
const [problem, setProblem] = useState<string | null>(null)
|
|
@@ -120,6 +127,7 @@ export function DataPage(): ReactNode {
|
|
|
120
127
|
setColumnMap({})
|
|
121
128
|
setConflictMode('skip')
|
|
122
129
|
setOnMissingCompany('skip')
|
|
130
|
+
setConsentPurposeId(null)
|
|
123
131
|
setMatchKeyId(defaultMatchKeyId(object))
|
|
124
132
|
setJobId(undefined)
|
|
125
133
|
setProblem(null)
|
|
@@ -140,6 +148,7 @@ export function DataPage(): ReactNode {
|
|
|
140
148
|
conflictMode,
|
|
141
149
|
onMissingCompany,
|
|
142
150
|
matchKeyId,
|
|
151
|
+
consentPurposeId,
|
|
143
152
|
...(map === undefined ? {} : { columnMap: map }),
|
|
144
153
|
})
|
|
145
154
|
|
|
@@ -153,6 +162,19 @@ export function DataPage(): ReactNode {
|
|
|
153
162
|
return created
|
|
154
163
|
}
|
|
155
164
|
|
|
165
|
+
async function processChosenFile(chosen: File): Promise<void> {
|
|
166
|
+
setProblem(null)
|
|
167
|
+
setFile(chosen)
|
|
168
|
+
|
|
169
|
+
const created = await runDryRun(chosen, undefined)
|
|
170
|
+
|
|
171
|
+
// The first upload takes the server's own reading of the file, which is what
|
|
172
|
+
// the mapping table is then built from.
|
|
173
|
+
setColumnMap(created.columnMap)
|
|
174
|
+
setMatchKeyId(created.matchKey)
|
|
175
|
+
setStep('map')
|
|
176
|
+
}
|
|
177
|
+
|
|
156
178
|
async function onFileChange(event: ChangeEvent<HTMLInputElement>): Promise<void> {
|
|
157
179
|
const chosen = event.target.files?.[0]
|
|
158
180
|
|
|
@@ -164,16 +186,22 @@ export function DataPage(): ReactNode {
|
|
|
164
186
|
return
|
|
165
187
|
}
|
|
166
188
|
|
|
167
|
-
|
|
168
|
-
|
|
189
|
+
await processChosenFile(chosen)
|
|
190
|
+
}
|
|
169
191
|
|
|
170
|
-
|
|
192
|
+
async function onFileDrop(event: DragEvent<HTMLLabelElement>): Promise<void> {
|
|
193
|
+
event.preventDefault()
|
|
171
194
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
195
|
+
if (createJob.isPending) {
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const chosen = event.dataTransfer.files[0]
|
|
200
|
+
if (chosen === undefined) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
await processChosenFile(chosen)
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
async function onRerun(event: FormEvent): Promise<void> {
|
|
@@ -199,6 +227,19 @@ export function DataPage(): ReactNode {
|
|
|
199
227
|
setStep('result')
|
|
200
228
|
}
|
|
201
229
|
|
|
230
|
+
// Commit finishes on the server, not the click, so the wizard waits for the
|
|
231
|
+
// job to reach `completed` before showing the outcome step. `failed` lands
|
|
232
|
+
// here too — the outcome is that the commit did not go through.
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
if (step !== 'result' || job === undefined) {
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (job.status === 'completed' || job.status === 'failed') {
|
|
239
|
+
setStep('done')
|
|
240
|
+
}
|
|
241
|
+
}, [step, job])
|
|
242
|
+
|
|
202
243
|
function onCommit(): void {
|
|
203
244
|
// The file goes back with the commit: the server kept its digest, not its
|
|
204
245
|
// bytes. This is the same `File` every dry run was run against.
|
|
@@ -223,7 +264,11 @@ export function DataPage(): ReactNode {
|
|
|
223
264
|
</p>
|
|
224
265
|
)}
|
|
225
266
|
|
|
226
|
-
<
|
|
267
|
+
<SampleDataSection />
|
|
268
|
+
|
|
269
|
+
<RelinkEmailDomainsSection />
|
|
270
|
+
|
|
271
|
+
<section className="space-y-4 border-t border-border pt-8">
|
|
227
272
|
<div>
|
|
228
273
|
<h2 className="text-[15px] font-semibold text-ink">Export</h2>
|
|
229
274
|
<p className="mt-1 text-[13px] text-ink-muted">
|
|
@@ -348,7 +393,14 @@ export function DataPage(): ReactNode {
|
|
|
348
393
|
<p className="text-[13px] text-ink-muted">
|
|
349
394
|
{SOURCE_LABELS[source]} → {OBJECT_LABELS[object]}
|
|
350
395
|
</p>
|
|
351
|
-
<label
|
|
396
|
+
<label
|
|
397
|
+
className="flex cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed border-border bg-surface-raised px-6 py-10 transition hover:border-accent"
|
|
398
|
+
onDragOver={(event) => {
|
|
399
|
+
event.preventDefault()
|
|
400
|
+
event.dataTransfer.dropEffect = 'copy'
|
|
401
|
+
}}
|
|
402
|
+
onDrop={(event) => runStep(onFileDrop(event))}
|
|
403
|
+
>
|
|
352
404
|
<span className="text-[13px] font-medium text-ink">
|
|
353
405
|
{createJob.isPending ? 'Reading the file…' : 'Drop a CSV here or click to browse'}
|
|
354
406
|
</span>
|
|
@@ -380,11 +432,13 @@ export function DataPage(): ReactNode {
|
|
|
380
432
|
columnMap={columnMap}
|
|
381
433
|
conflictMode={conflictMode}
|
|
382
434
|
onMissingCompany={onMissingCompany}
|
|
435
|
+
consentPurposeId={consentPurposeId}
|
|
383
436
|
matchKeyId={matchKeyId}
|
|
384
437
|
isPending={createJob.isPending}
|
|
385
438
|
onColumnMapChange={setColumnMap}
|
|
386
439
|
onConflictModeChange={setConflictMode}
|
|
387
440
|
onMissingCompanyChange={setOnMissingCompany}
|
|
441
|
+
onConsentPurposeChange={setConsentPurposeId}
|
|
388
442
|
onMatchKeyChange={setMatchKeyId}
|
|
389
443
|
onSubmit={(event) => runStep(onRerun(event))}
|
|
390
444
|
onBack={() => {
|
|
@@ -405,6 +459,10 @@ export function DataPage(): ReactNode {
|
|
|
405
459
|
onBack={() => setStep('map')}
|
|
406
460
|
/>
|
|
407
461
|
) : null}
|
|
462
|
+
|
|
463
|
+
{step === 'done' && job !== undefined ? (
|
|
464
|
+
<DonePanel job={job} object={object} onAgain={resetWizard} />
|
|
465
|
+
) : null}
|
|
408
466
|
</section>
|
|
409
467
|
</div>
|
|
410
468
|
)
|
|
@@ -416,11 +474,13 @@ interface MappingFormProps {
|
|
|
416
474
|
readonly columnMap: ImportColumnMap
|
|
417
475
|
readonly conflictMode: ImportConflictMode
|
|
418
476
|
readonly onMissingCompany: OnMissingCompany
|
|
477
|
+
readonly consentPurposeId: string | null
|
|
419
478
|
readonly matchKeyId: string
|
|
420
479
|
readonly isPending: boolean
|
|
421
480
|
readonly onColumnMapChange: (map: ImportColumnMap) => void
|
|
422
481
|
readonly onConflictModeChange: (mode: ImportConflictMode) => void
|
|
423
482
|
readonly onMissingCompanyChange: (mode: OnMissingCompany) => void
|
|
483
|
+
readonly onConsentPurposeChange: (id: string | null) => void
|
|
424
484
|
readonly onMatchKeyChange: (id: string) => void
|
|
425
485
|
readonly onSubmit: (event: FormEvent) => void
|
|
426
486
|
readonly onBack: () => void
|
|
@@ -483,6 +543,12 @@ function MappingForm(props: MappingFormProps): ReactNode {
|
|
|
483
543
|
</select>
|
|
484
544
|
</Field>
|
|
485
545
|
) : null}
|
|
546
|
+
{showMissingCompany ? (
|
|
547
|
+
<ConsentPurposeField
|
|
548
|
+
value={props.consentPurposeId}
|
|
549
|
+
onChange={props.onConsentPurposeChange}
|
|
550
|
+
/>
|
|
551
|
+
) : null}
|
|
486
552
|
</div>
|
|
487
553
|
<p className="text-[12px] text-ink-muted">
|
|
488
554
|
{props.conflictMode === 'update'
|
|
@@ -661,6 +727,81 @@ function ResultPanel({ job, object, isCommitting, onCommit, onBack }: ResultPane
|
|
|
661
727
|
)
|
|
662
728
|
}
|
|
663
729
|
|
|
730
|
+
interface DonePanelProps {
|
|
731
|
+
readonly job: ImportJob
|
|
732
|
+
readonly object: ImportObject
|
|
733
|
+
readonly onAgain: () => void
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function DonePanel({ job, object, onAgain }: DonePanelProps): ReactNode {
|
|
737
|
+
const failed = job.status === 'failed'
|
|
738
|
+
const wrote = job.counts.create + job.counts.update
|
|
739
|
+
|
|
740
|
+
return (
|
|
741
|
+
<div className="space-y-4">
|
|
742
|
+
<div>
|
|
743
|
+
<h3 className="text-[15px] font-semibold text-ink">
|
|
744
|
+
{failed ? 'Import failed' : 'Import complete'}
|
|
745
|
+
</h3>
|
|
746
|
+
<p className="mt-1 text-[13px] text-ink-muted">
|
|
747
|
+
{failed
|
|
748
|
+
? 'The server refused the commit. Nothing was written.'
|
|
749
|
+
: `${wrote} ${OBJECT_LABELS[object].toLowerCase()} row${wrote === 1 ? '' : 's'} written to this workspace.`}
|
|
750
|
+
</p>
|
|
751
|
+
</div>
|
|
752
|
+
<p className="text-[12px] text-ink-muted">
|
|
753
|
+
Job <span className="font-mono text-[12px] text-ink">{job.id}</span> · status {job.status}
|
|
754
|
+
</p>
|
|
755
|
+
<div className="grid grid-cols-2 gap-3 sm:grid-cols-5">
|
|
756
|
+
<Stat label="Total" value={job.counts.total} />
|
|
757
|
+
<Stat label="Created" value={job.counts.create} />
|
|
758
|
+
<Stat label="Updated" value={job.counts.update} />
|
|
759
|
+
<Stat label="Skipped" value={job.counts.skip} />
|
|
760
|
+
<Stat label="Errors" value={job.counts.error} danger />
|
|
761
|
+
</div>
|
|
762
|
+
|
|
763
|
+
{job.errors.length > 0 ? (
|
|
764
|
+
<div className="space-y-2">
|
|
765
|
+
<div>
|
|
766
|
+
<h3 className="text-[13px] font-semibold text-ink">Row errors</h3>
|
|
767
|
+
<p className="mt-0.5 text-[12px] text-ink-muted">
|
|
768
|
+
These rows were not written. Fix them in the CSV and import again.
|
|
769
|
+
</p>
|
|
770
|
+
</div>
|
|
771
|
+
<IssueTable rows={job.errors} />
|
|
772
|
+
{job.counts.error > job.errors.length ? (
|
|
773
|
+
<p className="text-[12px] text-ink-muted">
|
|
774
|
+
Showing {job.errors.length} of {job.counts.error} failing rows.
|
|
775
|
+
</p>
|
|
776
|
+
) : null}
|
|
777
|
+
</div>
|
|
778
|
+
) : null}
|
|
779
|
+
|
|
780
|
+
{job.warnings.length > 0 ? (
|
|
781
|
+
<div className="space-y-2">
|
|
782
|
+
<div>
|
|
783
|
+
<h3 className="text-[13px] font-semibold text-ink">Warnings</h3>
|
|
784
|
+
<p className="mt-0.5 text-[12px] text-ink-muted">
|
|
785
|
+
These rows were written. Each note says what was left out.
|
|
786
|
+
</p>
|
|
787
|
+
</div>
|
|
788
|
+
<IssueTable rows={job.warnings} />
|
|
789
|
+
</div>
|
|
790
|
+
) : null}
|
|
791
|
+
|
|
792
|
+
<div className="flex flex-wrap gap-3">
|
|
793
|
+
<button
|
|
794
|
+
type="button"
|
|
795
|
+
onClick={onAgain}
|
|
796
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover"
|
|
797
|
+
>
|
|
798
|
+
Import another
|
|
799
|
+
</button>
|
|
800
|
+
</div>
|
|
801
|
+
</div>
|
|
802
|
+
)
|
|
803
|
+
}
|
|
804
|
+
|
|
664
805
|
/** The row / field / message table shared by the error and warning lists. */
|
|
665
806
|
function IssueTable({ rows }: { readonly rows: readonly ImportRowError[] }): ReactNode {
|
|
666
807
|
return (
|
|
@@ -827,6 +968,176 @@ function Field({
|
|
|
827
968
|
)
|
|
828
969
|
}
|
|
829
970
|
|
|
971
|
+
/**
|
|
972
|
+
* People imports only. Required whenever the map names `consent_status` or
|
|
973
|
+
* `consent_at` — the server refuses the job otherwise, so the picker is next
|
|
974
|
+
* to the columns rather than tucked away.
|
|
975
|
+
*/
|
|
976
|
+
function ConsentPurposeField({
|
|
977
|
+
value,
|
|
978
|
+
onChange,
|
|
979
|
+
}: {
|
|
980
|
+
readonly value: string | null
|
|
981
|
+
readonly onChange: (id: string | null) => void
|
|
982
|
+
}): ReactNode {
|
|
983
|
+
const purposes = useConsentPurposes({ sort: 'sort_order', limit: 200 })
|
|
984
|
+
|
|
985
|
+
return (
|
|
986
|
+
<Field label="Consent purpose">
|
|
987
|
+
<select
|
|
988
|
+
value={value ?? ''}
|
|
989
|
+
onChange={(event) => {
|
|
990
|
+
onChange(event.target.value.length === 0 ? null : event.target.value)
|
|
991
|
+
}}
|
|
992
|
+
className="w-full rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent"
|
|
993
|
+
>
|
|
994
|
+
<option value="">— none —</option>
|
|
995
|
+
{purposes.records.map((purpose) => (
|
|
996
|
+
<option key={purpose.id} value={purpose.id}>
|
|
997
|
+
{purpose.label}
|
|
998
|
+
</option>
|
|
999
|
+
))}
|
|
1000
|
+
</select>
|
|
1001
|
+
</Field>
|
|
1002
|
+
)
|
|
1003
|
+
}
|
|
1004
|
+
|
|
830
1005
|
function messageOf(error: Error | null | undefined): string | null {
|
|
831
1006
|
return error === null || error === undefined ? null : error.message
|
|
832
1007
|
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* One button that fills the workspace with a small demo fixture.
|
|
1011
|
+
*
|
|
1012
|
+
* The endpoint refuses on a workspace that already has data, so the button
|
|
1013
|
+
* never doubles the seed and the "already has data" answer surfaces here as
|
|
1014
|
+
* a plain error line.
|
|
1015
|
+
*/
|
|
1016
|
+
function SampleDataSection(): ReactNode {
|
|
1017
|
+
const { session } = useSession()
|
|
1018
|
+
const workspaceId = session?.workspaceId ?? null
|
|
1019
|
+
const install = useInstallSampleData()
|
|
1020
|
+
const [counts, setCounts] = useState<SampleDataCounts | null>(null)
|
|
1021
|
+
|
|
1022
|
+
function onInstall(): void {
|
|
1023
|
+
if (workspaceId === null) {
|
|
1024
|
+
return
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
install
|
|
1028
|
+
.runAsync({ workspaceId })
|
|
1029
|
+
.then((result) => {
|
|
1030
|
+
setCounts(result)
|
|
1031
|
+
})
|
|
1032
|
+
.catch(() => undefined)
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const failure = messageOf(install.error)
|
|
1036
|
+
|
|
1037
|
+
return (
|
|
1038
|
+
<section className="space-y-4">
|
|
1039
|
+
<div>
|
|
1040
|
+
<h2 className="text-[15px] font-semibold text-ink">Install sample data</h2>
|
|
1041
|
+
<p className="mt-1 text-[13px] text-ink-muted">
|
|
1042
|
+
A small set of companies, people, and deals so a fresh workspace has something
|
|
1043
|
+
to look at. Refuses if this workspace already has CRM data.
|
|
1044
|
+
</p>
|
|
1045
|
+
</div>
|
|
1046
|
+
<button
|
|
1047
|
+
type="button"
|
|
1048
|
+
onClick={onInstall}
|
|
1049
|
+
disabled={install.isPending || workspaceId === null || counts !== null}
|
|
1050
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover disabled:opacity-50"
|
|
1051
|
+
>
|
|
1052
|
+
{counts !== null
|
|
1053
|
+
? 'Installed'
|
|
1054
|
+
: install.isPending
|
|
1055
|
+
? 'Installing…'
|
|
1056
|
+
: 'Install sample data'}
|
|
1057
|
+
</button>
|
|
1058
|
+
{counts !== null ? (
|
|
1059
|
+
<p className="text-[12px] text-ink-muted">
|
|
1060
|
+
Added {counts.companies} companies, {counts.people} people, {counts.positions}{' '}
|
|
1061
|
+
positions, {counts.deals} deals, {counts.opportunities} opportunities,{' '}
|
|
1062
|
+
{counts.raises} raises, {counts.partnerships} partnerships, {counts.enquiries}{' '}
|
|
1063
|
+
enquiries, {counts.roles} roles, {counts.candidates} candidates,{' '}
|
|
1064
|
+
{counts.planItems} plan items, and {counts.notes} notes.
|
|
1065
|
+
</p>
|
|
1066
|
+
) : null}
|
|
1067
|
+
{failure === null ? null : (
|
|
1068
|
+
<p role="alert" className="text-[12px] font-medium text-danger">
|
|
1069
|
+
{failure}
|
|
1070
|
+
</p>
|
|
1071
|
+
)}
|
|
1072
|
+
</section>
|
|
1073
|
+
)
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Sweep every workspace Company that carries a domain and attach each Person
|
|
1078
|
+
* whose email is at that domain.
|
|
1079
|
+
*
|
|
1080
|
+
* Backfills links for records that predate the auto-linker or arrived through
|
|
1081
|
+
* a bulk import. Idempotent: a follow-up run adds nothing. Never removes.
|
|
1082
|
+
*/
|
|
1083
|
+
function RelinkEmailDomainsSection(): ReactNode {
|
|
1084
|
+
const { session } = useSession()
|
|
1085
|
+
const workspaceId = session?.workspaceId ?? null
|
|
1086
|
+
const relink = useRelinkEmailDomains()
|
|
1087
|
+
const [outcome, setOutcome] = useState<{
|
|
1088
|
+
readonly companiesScanned: number
|
|
1089
|
+
readonly positionsCreated: number
|
|
1090
|
+
} | null>(null)
|
|
1091
|
+
|
|
1092
|
+
function onRun(): void {
|
|
1093
|
+
if (workspaceId === null) {
|
|
1094
|
+
return
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
relink
|
|
1098
|
+
.runAsync({ workspaceId })
|
|
1099
|
+
.then((result) => {
|
|
1100
|
+
setOutcome({
|
|
1101
|
+
companiesScanned: result.companiesScanned,
|
|
1102
|
+
positionsCreated: result.positionsCreated,
|
|
1103
|
+
})
|
|
1104
|
+
})
|
|
1105
|
+
.catch(() => undefined)
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
const failure = messageOf(relink.error)
|
|
1109
|
+
|
|
1110
|
+
return (
|
|
1111
|
+
<section className="space-y-4 border-t border-border pt-8">
|
|
1112
|
+
<div>
|
|
1113
|
+
<h2 className="text-[15px] font-semibold text-ink">Rebuild email-domain links</h2>
|
|
1114
|
+
<p className="mt-1 text-[13px] text-ink-muted">
|
|
1115
|
+
Attach every workspace Person to a Company whose domain matches their email. Safe to run
|
|
1116
|
+
more than once — a Person already at a Company is left alone, and consumer email hosts
|
|
1117
|
+
(gmail, hotmail, …) are skipped.
|
|
1118
|
+
</p>
|
|
1119
|
+
</div>
|
|
1120
|
+
<button
|
|
1121
|
+
type="button"
|
|
1122
|
+
onClick={onRun}
|
|
1123
|
+
disabled={relink.isPending || workspaceId === null}
|
|
1124
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover disabled:opacity-50"
|
|
1125
|
+
>
|
|
1126
|
+
{relink.isPending ? 'Rebuilding…' : 'Rebuild links'}
|
|
1127
|
+
</button>
|
|
1128
|
+
{outcome !== null ? (
|
|
1129
|
+
<p className="text-[12px] text-ink-muted">
|
|
1130
|
+
Scanned {outcome.companiesScanned}{' '}
|
|
1131
|
+
{outcome.companiesScanned === 1 ? 'company' : 'companies'}, added{' '}
|
|
1132
|
+
{outcome.positionsCreated}{' '}
|
|
1133
|
+
{outcome.positionsCreated === 1 ? 'position' : 'positions'}.
|
|
1134
|
+
</p>
|
|
1135
|
+
) : null}
|
|
1136
|
+
{failure === null ? null : (
|
|
1137
|
+
<p role="alert" className="text-[12px] font-medium text-danger">
|
|
1138
|
+
{failure}
|
|
1139
|
+
</p>
|
|
1140
|
+
)}
|
|
1141
|
+
</section>
|
|
1142
|
+
)
|
|
1143
|
+
}
|