@kelpie/ui 0.1.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/LICENSE +661 -0
- package/README.md +44 -0
- package/dist/ServiceStatus.d.ts +6 -0
- package/dist/ServiceStatus.d.ts.map +1 -0
- package/dist/ServiceStatus.js +35 -0
- package/dist/ServiceStatus.js.map +1 -0
- package/dist/api/ApiProvider.d.ts +19 -0
- package/dist/api/ApiProvider.d.ts.map +1 -0
- package/dist/api/ApiProvider.js +18 -0
- package/dist/api/ApiProvider.js.map +1 -0
- package/dist/api/client.d.ts +83 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +162 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/context.d.ts +14 -0
- package/dist/api/context.d.ts.map +1 -0
- package/dist/api/context.js +20 -0
- package/dist/api/context.js.map +1 -0
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +14 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/health.d.ts +15 -0
- package/dist/api/health.d.ts.map +1 -0
- package/dist/api/health.js +22 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/json.d.ts +3 -0
- package/dist/api/json.d.ts.map +1 -0
- package/dist/api/json.js +5 -0
- package/dist/api/json.js.map +1 -0
- package/dist/api/queryClient.d.ts +17 -0
- package/dist/api/queryClient.d.ts.map +1 -0
- package/dist/api/queryClient.js +33 -0
- package/dist/api/queryClient.js.map +1 -0
- package/dist/api/resource.d.ts +110 -0
- package/dist/api/resource.d.ts.map +1 -0
- package/dist/api/resource.js +238 -0
- package/dist/api/resource.js.map +1 -0
- package/dist/api/resources/account.d.ts +49 -0
- package/dist/api/resources/account.d.ts.map +1 -0
- package/dist/api/resources/account.js +146 -0
- package/dist/api/resources/account.js.map +1 -0
- package/dist/api/resources/activities.d.ts +8 -0
- package/dist/api/resources/activities.d.ts.map +1 -0
- package/dist/api/resources/activities.js +21 -0
- package/dist/api/resources/activities.js.map +1 -0
- package/dist/api/resources/agentTasks.d.ts +31 -0
- package/dist/api/resources/agentTasks.d.ts.map +1 -0
- package/dist/api/resources/agentTasks.js +103 -0
- package/dist/api/resources/agentTasks.js.map +1 -0
- package/dist/api/resources/candidates.d.ts +17 -0
- package/dist/api/resources/candidates.d.ts.map +1 -0
- package/dist/api/resources/candidates.js +40 -0
- package/dist/api/resources/candidates.js.map +1 -0
- package/dist/api/resources/companies.d.ts +22 -0
- package/dist/api/resources/companies.d.ts.map +1 -0
- package/dist/api/resources/companies.js +36 -0
- package/dist/api/resources/companies.js.map +1 -0
- package/dist/api/resources/dashboard.d.ts +8 -0
- package/dist/api/resources/dashboard.d.ts.map +1 -0
- package/dist/api/resources/dashboard.js +27 -0
- package/dist/api/resources/dashboard.js.map +1 -0
- package/dist/api/resources/deals.d.ts +30 -0
- package/dist/api/resources/deals.d.ts.map +1 -0
- package/dist/api/resources/deals.js +38 -0
- package/dist/api/resources/deals.js.map +1 -0
- package/dist/api/resources/decisions.d.ts +13 -0
- package/dist/api/resources/decisions.d.ts.map +1 -0
- package/dist/api/resources/decisions.js +38 -0
- package/dist/api/resources/decisions.js.map +1 -0
- package/dist/api/resources/forms.d.ts +82 -0
- package/dist/api/resources/forms.d.ts.map +1 -0
- package/dist/api/resources/forms.js +129 -0
- package/dist/api/resources/forms.js.map +1 -0
- package/dist/api/resources/handbookPages.d.ts +33 -0
- package/dist/api/resources/handbookPages.d.ts.map +1 -0
- package/dist/api/resources/handbookPages.js +49 -0
- package/dist/api/resources/handbookPages.js.map +1 -0
- package/dist/api/resources/importJobs.d.ts +72 -0
- package/dist/api/resources/importJobs.d.ts.map +1 -0
- package/dist/api/resources/importJobs.js +145 -0
- package/dist/api/resources/importJobs.js.map +1 -0
- package/dist/api/resources/invites.d.ts +19 -0
- package/dist/api/resources/invites.d.ts.map +1 -0
- package/dist/api/resources/invites.js +78 -0
- package/dist/api/resources/invites.js.map +1 -0
- package/dist/api/resources/mcpTools.d.ts +8 -0
- package/dist/api/resources/mcpTools.d.ts.map +1 -0
- package/dist/api/resources/mcpTools.js +23 -0
- package/dist/api/resources/mcpTools.js.map +1 -0
- package/dist/api/resources/members.d.ts +40 -0
- package/dist/api/resources/members.d.ts.map +1 -0
- package/dist/api/resources/members.js +67 -0
- package/dist/api/resources/members.js.map +1 -0
- package/dist/api/resources/moduleSettings.d.ts +14 -0
- package/dist/api/resources/moduleSettings.d.ts.map +1 -0
- package/dist/api/resources/moduleSettings.js +43 -0
- package/dist/api/resources/moduleSettings.js.map +1 -0
- package/dist/api/resources/mutation.d.ts +18 -0
- package/dist/api/resources/mutation.d.ts.map +1 -0
- package/dist/api/resources/mutation.js +12 -0
- package/dist/api/resources/mutation.js.map +1 -0
- package/dist/api/resources/notes.d.ts +14 -0
- package/dist/api/resources/notes.d.ts.map +1 -0
- package/dist/api/resources/notes.js +39 -0
- package/dist/api/resources/notes.js.map +1 -0
- package/dist/api/resources/opportunities.d.ts +29 -0
- package/dist/api/resources/opportunities.d.ts.map +1 -0
- package/dist/api/resources/opportunities.js +38 -0
- package/dist/api/resources/opportunities.js.map +1 -0
- package/dist/api/resources/partnerships.d.ts +33 -0
- package/dist/api/resources/partnerships.d.ts.map +1 -0
- package/dist/api/resources/partnerships.js +39 -0
- package/dist/api/resources/partnerships.js.map +1 -0
- package/dist/api/resources/people.d.ts +22 -0
- package/dist/api/resources/people.d.ts.map +1 -0
- package/dist/api/resources/people.js +36 -0
- package/dist/api/resources/people.js.map +1 -0
- package/dist/api/resources/pipelineStages.d.ts +22 -0
- package/dist/api/resources/pipelineStages.d.ts.map +1 -0
- package/dist/api/resources/pipelineStages.js +61 -0
- package/dist/api/resources/pipelineStages.js.map +1 -0
- package/dist/api/resources/planItems.d.ts +31 -0
- package/dist/api/resources/planItems.d.ts.map +1 -0
- package/dist/api/resources/planItems.js +60 -0
- package/dist/api/resources/planItems.js.map +1 -0
- package/dist/api/resources/positions.d.ts +23 -0
- package/dist/api/resources/positions.d.ts.map +1 -0
- package/dist/api/resources/positions.js +31 -0
- package/dist/api/resources/positions.js.map +1 -0
- package/dist/api/resources/raises.d.ts +30 -0
- package/dist/api/resources/raises.d.ts.map +1 -0
- package/dist/api/resources/raises.js +38 -0
- package/dist/api/resources/raises.js.map +1 -0
- package/dist/api/resources/roles.d.ts +20 -0
- package/dist/api/resources/roles.d.ts.map +1 -0
- package/dist/api/resources/roles.js +31 -0
- package/dist/api/resources/roles.js.map +1 -0
- package/dist/api/resources/search.d.ts +12 -0
- package/dist/api/resources/search.d.ts.map +1 -0
- package/dist/api/resources/search.js +37 -0
- package/dist/api/resources/search.js.map +1 -0
- package/dist/api/resources/session.d.ts +58 -0
- package/dist/api/resources/session.d.ts.map +1 -0
- package/dist/api/resources/session.js +157 -0
- package/dist/api/resources/session.js.map +1 -0
- package/dist/api/resources/webhooks.d.ts +45 -0
- package/dist/api/resources/webhooks.d.ts.map +1 -0
- package/dist/api/resources/webhooks.js +106 -0
- package/dist/api/resources/webhooks.js.map +1 -0
- package/dist/api/resources/workspace.d.ts +20 -0
- package/dist/api/resources/workspace.d.ts.map +1 -0
- package/dist/api/resources/workspace.js +63 -0
- package/dist/api/resources/workspace.js.map +1 -0
- package/dist/app/KelpieApp.d.ts +18 -0
- package/dist/app/KelpieApp.d.ts.map +1 -0
- package/dist/app/KelpieApp.js +59 -0
- package/dist/app/KelpieApp.js.map +1 -0
- package/dist/app/SessionGate.d.ts +14 -0
- package/dist/app/SessionGate.d.ts.map +1 -0
- package/dist/app/SessionGate.js +33 -0
- package/dist/app/SessionGate.js.map +1 -0
- package/dist/components/ActivitiesPanel.d.ts +28 -0
- package/dist/components/ActivitiesPanel.d.ts.map +1 -0
- package/dist/components/ActivitiesPanel.js +75 -0
- package/dist/components/ActivitiesPanel.js.map +1 -0
- package/dist/components/AgentTasks.d.ts +16 -0
- package/dist/components/AgentTasks.d.ts.map +1 -0
- package/dist/components/AgentTasks.js +166 -0
- package/dist/components/AgentTasks.js.map +1 -0
- package/dist/components/Chip.d.ts +10 -0
- package/dist/components/Chip.d.ts.map +1 -0
- package/dist/components/Chip.js +21 -0
- package/dist/components/Chip.js.map +1 -0
- package/dist/components/CopyButton.d.ts +7 -0
- package/dist/components/CopyButton.d.ts.map +1 -0
- package/dist/components/CopyButton.js +30 -0
- package/dist/components/CopyButton.js.map +1 -0
- package/dist/components/DataTable.d.ts +36 -0
- package/dist/components/DataTable.d.ts.map +1 -0
- package/dist/components/DataTable.js +43 -0
- package/dist/components/DataTable.js.map +1 -0
- package/dist/components/DecisionsPanel.d.ts +14 -0
- package/dist/components/DecisionsPanel.d.ts.map +1 -0
- package/dist/components/DecisionsPanel.js +73 -0
- package/dist/components/DecisionsPanel.js.map +1 -0
- package/dist/components/DeleteRecord.d.ts +19 -0
- package/dist/components/DeleteRecord.d.ts.map +1 -0
- package/dist/components/DeleteRecord.js +22 -0
- package/dist/components/DeleteRecord.js.map +1 -0
- package/dist/components/EntitySearch.d.ts +33 -0
- package/dist/components/EntitySearch.d.ts.map +1 -0
- package/dist/components/EntitySearch.js +111 -0
- package/dist/components/EntitySearch.js.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +21 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +26 -0
- package/dist/components/ErrorBoundary.js.map +1 -0
- package/dist/components/InlineEdit.d.ts +37 -0
- package/dist/components/InlineEdit.d.ts.map +1 -0
- package/dist/components/InlineEdit.js +77 -0
- package/dist/components/InlineEdit.js.map +1 -0
- package/dist/components/KanbanBoard.d.ts +24 -0
- package/dist/components/KanbanBoard.d.ts.map +1 -0
- package/dist/components/KanbanBoard.js +92 -0
- package/dist/components/KanbanBoard.js.map +1 -0
- package/dist/components/MarkdownView.d.ts +4 -0
- package/dist/components/MarkdownView.d.ts.map +1 -0
- package/dist/components/MarkdownView.js +45 -0
- package/dist/components/MarkdownView.js.map +1 -0
- package/dist/components/NotesPanel.d.ts +17 -0
- package/dist/components/NotesPanel.d.ts.map +1 -0
- package/dist/components/NotesPanel.js +53 -0
- package/dist/components/NotesPanel.js.map +1 -0
- package/dist/components/PageHeader.d.ts +20 -0
- package/dist/components/PageHeader.d.ts.map +1 -0
- package/dist/components/PageHeader.js +15 -0
- package/dist/components/PageHeader.js.map +1 -0
- package/dist/components/PhonesField.d.ts +7 -0
- package/dist/components/PhonesField.d.ts.map +1 -0
- package/dist/components/PhonesField.js +92 -0
- package/dist/components/PhonesField.js.map +1 -0
- package/dist/components/PlanAttention.d.ts +40 -0
- package/dist/components/PlanAttention.d.ts.map +1 -0
- package/dist/components/PlanAttention.js +86 -0
- package/dist/components/PlanAttention.js.map +1 -0
- package/dist/components/PlanPanel.d.ts +18 -0
- package/dist/components/PlanPanel.d.ts.map +1 -0
- package/dist/components/PlanPanel.js +102 -0
- package/dist/components/PlanPanel.js.map +1 -0
- package/dist/components/QueryState.d.ts +24 -0
- package/dist/components/QueryState.d.ts.map +1 -0
- package/dist/components/QueryState.js +34 -0
- package/dist/components/QueryState.js.map +1 -0
- package/dist/components/RecordTabs.d.ts +16 -0
- package/dist/components/RecordTabs.d.ts.map +1 -0
- package/dist/components/RecordTabs.js +15 -0
- package/dist/components/RecordTabs.js.map +1 -0
- package/dist/components/SectionHeader.d.ts +15 -0
- package/dist/components/SectionHeader.d.ts.map +1 -0
- package/dist/components/SectionHeader.js +14 -0
- package/dist/components/SectionHeader.js.map +1 -0
- package/dist/components/SegmentedControl.d.ts +13 -0
- package/dist/components/SegmentedControl.d.ts.map +1 -0
- package/dist/components/SegmentedControl.js +14 -0
- package/dist/components/SegmentedControl.js.map +1 -0
- package/dist/components/Shell.d.ts +2 -0
- package/dist/components/Shell.d.ts.map +1 -0
- package/dist/components/Shell.js +143 -0
- package/dist/components/Shell.js.map +1 -0
- package/dist/components/SidebarField.d.ts +8 -0
- package/dist/components/SidebarField.d.ts.map +1 -0
- package/dist/components/SidebarField.js +6 -0
- package/dist/components/SidebarField.js.map +1 -0
- package/dist/components/SocialProfilesField.d.ts +8 -0
- package/dist/components/SocialProfilesField.d.ts.map +1 -0
- package/dist/components/SocialProfilesField.js +176 -0
- package/dist/components/SocialProfilesField.js.map +1 -0
- package/dist/components/StageSettings.d.ts +16 -0
- package/dist/components/StageSettings.d.ts.map +1 -0
- package/dist/components/StageSettings.js +107 -0
- package/dist/components/StageSettings.js.map +1 -0
- package/dist/components/SummaryBlock.d.ts +12 -0
- package/dist/components/SummaryBlock.d.ts.map +1 -0
- package/dist/components/SummaryBlock.js +12 -0
- package/dist/components/SummaryBlock.js.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/dates.d.ts +26 -0
- package/dist/lib/dates.d.ts.map +1 -0
- package/dist/lib/dates.js +71 -0
- package/dist/lib/dates.js.map +1 -0
- package/dist/lib/money.d.ts +7 -0
- package/dist/lib/money.d.ts.map +1 -0
- package/dist/lib/money.js +13 -0
- package/dist/lib/money.js.map +1 -0
- package/dist/lib/names.d.ts +9 -0
- package/dist/lib/names.d.ts.map +1 -0
- package/dist/lib/names.js +13 -0
- package/dist/lib/names.js.map +1 -0
- package/dist/lib/plan.d.ts +67 -0
- package/dist/lib/plan.d.ts.map +1 -0
- package/dist/lib/plan.js +96 -0
- package/dist/lib/plan.js.map +1 -0
- package/dist/lib/theme.d.ts +19 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +30 -0
- package/dist/lib/theme.js.map +1 -0
- package/dist/pages/CompaniesPage.d.ts +2 -0
- package/dist/pages/CompaniesPage.d.ts.map +1 -0
- package/dist/pages/CompaniesPage.js +77 -0
- package/dist/pages/CompaniesPage.js.map +1 -0
- package/dist/pages/CompanyDetail.d.ts +2 -0
- package/dist/pages/CompanyDetail.d.ts.map +1 -0
- package/dist/pages/CompanyDetail.js +177 -0
- package/dist/pages/CompanyDetail.js.map +1 -0
- package/dist/pages/DealDetail.d.ts +2 -0
- package/dist/pages/DealDetail.d.ts.map +1 -0
- package/dist/pages/DealDetail.js +192 -0
- package/dist/pages/DealDetail.js.map +1 -0
- package/dist/pages/DealStageSettingsPage.d.ts +7 -0
- package/dist/pages/DealStageSettingsPage.d.ts.map +1 -0
- package/dist/pages/DealStageSettingsPage.js +38 -0
- package/dist/pages/DealStageSettingsPage.js.map +1 -0
- package/dist/pages/DealsPage.d.ts +2 -0
- package/dist/pages/DealsPage.d.ts.map +1 -0
- package/dist/pages/DealsPage.js +177 -0
- package/dist/pages/DealsPage.js.map +1 -0
- package/dist/pages/DecisionsPage.d.ts +2 -0
- package/dist/pages/DecisionsPage.d.ts.map +1 -0
- package/dist/pages/DecisionsPage.js +121 -0
- package/dist/pages/DecisionsPage.js.map +1 -0
- package/dist/pages/FormDetail.d.ts +2 -0
- package/dist/pages/FormDetail.d.ts.map +1 -0
- package/dist/pages/FormDetail.js +56 -0
- package/dist/pages/FormDetail.js.map +1 -0
- package/dist/pages/FormsPage.d.ts +2 -0
- package/dist/pages/FormsPage.d.ts.map +1 -0
- package/dist/pages/FormsPage.js +67 -0
- package/dist/pages/FormsPage.js.map +1 -0
- package/dist/pages/FundraisingPage.d.ts +2 -0
- package/dist/pages/FundraisingPage.d.ts.map +1 -0
- package/dist/pages/FundraisingPage.js +187 -0
- package/dist/pages/FundraisingPage.js.map +1 -0
- package/dist/pages/HiringPage.d.ts +9 -0
- package/dist/pages/HiringPage.d.ts.map +1 -0
- package/dist/pages/HiringPage.js +73 -0
- package/dist/pages/HiringPage.js.map +1 -0
- package/dist/pages/NotFoundPage.d.ts +10 -0
- package/dist/pages/NotFoundPage.d.ts.map +1 -0
- package/dist/pages/NotFoundPage.js +14 -0
- package/dist/pages/NotFoundPage.js.map +1 -0
- package/dist/pages/OpportunitiesPage.d.ts +2 -0
- package/dist/pages/OpportunitiesPage.d.ts.map +1 -0
- package/dist/pages/OpportunitiesPage.js +184 -0
- package/dist/pages/OpportunitiesPage.js.map +1 -0
- package/dist/pages/OpportunityDetail.d.ts +2 -0
- package/dist/pages/OpportunityDetail.d.ts.map +1 -0
- package/dist/pages/OpportunityDetail.js +132 -0
- package/dist/pages/OpportunityDetail.js.map +1 -0
- package/dist/pages/OpportunityStageSettingsPage.d.ts +3 -0
- package/dist/pages/OpportunityStageSettingsPage.d.ts.map +1 -0
- package/dist/pages/OpportunityStageSettingsPage.js +34 -0
- package/dist/pages/OpportunityStageSettingsPage.js.map +1 -0
- package/dist/pages/PartnershipDetail.d.ts +2 -0
- package/dist/pages/PartnershipDetail.d.ts.map +1 -0
- package/dist/pages/PartnershipDetail.js +179 -0
- package/dist/pages/PartnershipDetail.js.map +1 -0
- package/dist/pages/PartnershipStageSettingsPage.d.ts +3 -0
- package/dist/pages/PartnershipStageSettingsPage.d.ts.map +1 -0
- package/dist/pages/PartnershipStageSettingsPage.js +34 -0
- package/dist/pages/PartnershipStageSettingsPage.js.map +1 -0
- package/dist/pages/PartnershipsPage.d.ts +2 -0
- package/dist/pages/PartnershipsPage.d.ts.map +1 -0
- package/dist/pages/PartnershipsPage.js +182 -0
- package/dist/pages/PartnershipsPage.js.map +1 -0
- package/dist/pages/PeoplePage.d.ts +10 -0
- package/dist/pages/PeoplePage.d.ts.map +1 -0
- package/dist/pages/PeoplePage.js +71 -0
- package/dist/pages/PeoplePage.js.map +1 -0
- package/dist/pages/PersonDetail.d.ts +2 -0
- package/dist/pages/PersonDetail.d.ts.map +1 -0
- package/dist/pages/PersonDetail.js +193 -0
- package/dist/pages/PersonDetail.js.map +1 -0
- package/dist/pages/PlanningPage.d.ts +2 -0
- package/dist/pages/PlanningPage.d.ts.map +1 -0
- package/dist/pages/PlanningPage.js +112 -0
- package/dist/pages/PlanningPage.js.map +1 -0
- package/dist/pages/RaiseDetail.d.ts +2 -0
- package/dist/pages/RaiseDetail.d.ts.map +1 -0
- package/dist/pages/RaiseDetail.js +198 -0
- package/dist/pages/RaiseDetail.js.map +1 -0
- package/dist/pages/RaiseStageSettingsPage.d.ts +3 -0
- package/dist/pages/RaiseStageSettingsPage.d.ts.map +1 -0
- package/dist/pages/RaiseStageSettingsPage.js +34 -0
- package/dist/pages/RaiseStageSettingsPage.js.map +1 -0
- package/dist/pages/RoleDetail.d.ts +2 -0
- package/dist/pages/RoleDetail.d.ts.map +1 -0
- package/dist/pages/RoleDetail.js +177 -0
- package/dist/pages/RoleDetail.js.map +1 -0
- package/dist/pages/SearchPage.d.ts +2 -0
- package/dist/pages/SearchPage.d.ts.map +1 -0
- package/dist/pages/SearchPage.js +51 -0
- package/dist/pages/SearchPage.js.map +1 -0
- package/dist/pages/account/AccountLayout.d.ts +2 -0
- package/dist/pages/account/AccountLayout.d.ts.map +1 -0
- package/dist/pages/account/AccountLayout.js +25 -0
- package/dist/pages/account/AccountLayout.js.map +1 -0
- package/dist/pages/account/Field.d.ts +8 -0
- package/dist/pages/account/Field.d.ts.map +1 -0
- package/dist/pages/account/Field.js +6 -0
- package/dist/pages/account/Field.js.map +1 -0
- package/dist/pages/account/PreferencesPage.d.ts +2 -0
- package/dist/pages/account/PreferencesPage.d.ts.map +1 -0
- package/dist/pages/account/PreferencesPage.js +92 -0
- package/dist/pages/account/PreferencesPage.js.map +1 -0
- package/dist/pages/account/ProfilePage.d.ts +14 -0
- package/dist/pages/account/ProfilePage.d.ts.map +1 -0
- package/dist/pages/account/ProfilePage.js +71 -0
- package/dist/pages/account/ProfilePage.js.map +1 -0
- package/dist/pages/account/SecurityPage.d.ts +11 -0
- package/dist/pages/account/SecurityPage.d.ts.map +1 -0
- package/dist/pages/account/SecurityPage.js +78 -0
- package/dist/pages/account/SecurityPage.js.map +1 -0
- package/dist/pages/admin/DataPage.d.ts +3 -0
- package/dist/pages/admin/DataPage.d.ts.map +1 -0
- package/dist/pages/admin/DataPage.js +210 -0
- package/dist/pages/admin/DataPage.js.map +1 -0
- package/dist/pages/admin/McpPage.d.ts +2 -0
- package/dist/pages/admin/McpPage.d.ts.map +1 -0
- package/dist/pages/admin/McpPage.js +128 -0
- package/dist/pages/admin/McpPage.js.map +1 -0
- package/dist/pages/admin/ModulesPage.d.ts +2 -0
- package/dist/pages/admin/ModulesPage.d.ts.map +1 -0
- package/dist/pages/admin/ModulesPage.js +53 -0
- package/dist/pages/admin/ModulesPage.js.map +1 -0
- package/dist/pages/admin/TeamPage.d.ts +2 -0
- package/dist/pages/admin/TeamPage.d.ts.map +1 -0
- package/dist/pages/admin/TeamPage.js +99 -0
- package/dist/pages/admin/TeamPage.js.map +1 -0
- package/dist/pages/admin/WebhookDeliveries.d.ts +4 -0
- package/dist/pages/admin/WebhookDeliveries.d.ts.map +1 -0
- package/dist/pages/admin/WebhookDeliveries.js +88 -0
- package/dist/pages/admin/WebhookDeliveries.js.map +1 -0
- package/dist/pages/admin/WebhooksPage.d.ts +2 -0
- package/dist/pages/admin/WebhooksPage.d.ts.map +1 -0
- package/dist/pages/admin/WebhooksPage.js +152 -0
- package/dist/pages/admin/WebhooksPage.js.map +1 -0
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts +2 -0
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -0
- package/dist/pages/admin/WorkspaceSettingsPage.js +117 -0
- package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -0
- package/dist/pages/auth/AuthForm.d.ts +46 -0
- package/dist/pages/auth/AuthForm.d.ts.map +1 -0
- package/dist/pages/auth/AuthForm.js +32 -0
- package/dist/pages/auth/AuthForm.js.map +1 -0
- package/dist/pages/auth/AuthLayout.d.ts +24 -0
- package/dist/pages/auth/AuthLayout.d.ts.map +1 -0
- package/dist/pages/auth/AuthLayout.js +19 -0
- package/dist/pages/auth/AuthLayout.js.map +1 -0
- package/dist/pages/auth/ForgotPasswordPage.d.ts +13 -0
- package/dist/pages/auth/ForgotPasswordPage.d.ts.map +1 -0
- package/dist/pages/auth/ForgotPasswordPage.js +39 -0
- package/dist/pages/auth/ForgotPasswordPage.js.map +1 -0
- package/dist/pages/auth/JoinPage.d.ts +12 -0
- package/dist/pages/auth/JoinPage.d.ts.map +1 -0
- package/dist/pages/auth/JoinPage.js +43 -0
- package/dist/pages/auth/JoinPage.js.map +1 -0
- package/dist/pages/auth/ResetPasswordPage.d.ts +13 -0
- package/dist/pages/auth/ResetPasswordPage.d.ts.map +1 -0
- package/dist/pages/auth/ResetPasswordPage.js +58 -0
- package/dist/pages/auth/ResetPasswordPage.js.map +1 -0
- package/dist/pages/auth/SignInPage.d.ts +2 -0
- package/dist/pages/auth/SignInPage.d.ts.map +1 -0
- package/dist/pages/auth/SignInPage.js +38 -0
- package/dist/pages/auth/SignInPage.js.map +1 -0
- package/dist/pages/auth/SignUpPage.d.ts +13 -0
- package/dist/pages/auth/SignUpPage.d.ts.map +1 -0
- package/dist/pages/auth/SignUpPage.js +43 -0
- package/dist/pages/auth/SignUpPage.js.map +1 -0
- package/dist/pages/candidateFields.d.ts +22 -0
- package/dist/pages/candidateFields.d.ts.map +1 -0
- package/dist/pages/candidateFields.js +82 -0
- package/dist/pages/candidateFields.js.map +1 -0
- package/dist/pages/dashboard/DashboardPage.d.ts +2 -0
- package/dist/pages/dashboard/DashboardPage.d.ts.map +1 -0
- package/dist/pages/dashboard/DashboardPage.js +40 -0
- package/dist/pages/dashboard/DashboardPage.js.map +1 -0
- package/dist/pages/dashboard/attention.d.ts +41 -0
- package/dist/pages/dashboard/attention.d.ts.map +1 -0
- package/dist/pages/dashboard/attention.js +146 -0
- package/dist/pages/dashboard/attention.js.map +1 -0
- package/dist/pages/dashboard/sections.d.ts +20 -0
- package/dist/pages/dashboard/sections.d.ts.map +1 -0
- package/dist/pages/dashboard/sections.js +60 -0
- package/dist/pages/dashboard/sections.js.map +1 -0
- package/dist/pages/fields.d.ts +11 -0
- package/dist/pages/fields.d.ts.map +1 -0
- package/dist/pages/fields.js +17 -0
- package/dist/pages/fields.js.map +1 -0
- package/dist/pages/forms/EmbedPanel.d.ts +14 -0
- package/dist/pages/forms/EmbedPanel.d.ts.map +1 -0
- package/dist/pages/forms/EmbedPanel.js +19 -0
- package/dist/pages/forms/EmbedPanel.js.map +1 -0
- package/dist/pages/forms/FieldsEditor.d.ts +6 -0
- package/dist/pages/forms/FieldsEditor.d.ts.map +1 -0
- package/dist/pages/forms/FieldsEditor.js +115 -0
- package/dist/pages/forms/FieldsEditor.js.map +1 -0
- package/dist/pages/forms/FormSettings.d.ts +6 -0
- package/dist/pages/forms/FormSettings.d.ts.map +1 -0
- package/dist/pages/forms/FormSettings.js +47 -0
- package/dist/pages/forms/FormSettings.js.map +1 -0
- package/dist/pages/forms/SubmissionsTable.d.ts +8 -0
- package/dist/pages/forms/SubmissionsTable.d.ts.map +1 -0
- package/dist/pages/forms/SubmissionsTable.js +91 -0
- package/dist/pages/forms/SubmissionsTable.js.map +1 -0
- package/dist/pages/forms/fieldList.d.ts +70 -0
- package/dist/pages/forms/fieldList.d.ts.map +1 -0
- package/dist/pages/forms/fieldList.js +126 -0
- package/dist/pages/forms/fieldList.js.map +1 -0
- package/dist/pages/forms/template.d.ts +18 -0
- package/dist/pages/forms/template.d.ts.map +1 -0
- package/dist/pages/forms/template.js +34 -0
- package/dist/pages/forms/template.js.map +1 -0
- package/dist/pages/handbook/HandbookPage.d.ts +2 -0
- package/dist/pages/handbook/HandbookPage.d.ts.map +1 -0
- package/dist/pages/handbook/HandbookPage.js +197 -0
- package/dist/pages/handbook/HandbookPage.js.map +1 -0
- package/dist/pages/handbook/tree.d.ts +62 -0
- package/dist/pages/handbook/tree.d.ts.map +1 -0
- package/dist/pages/handbook/tree.js +222 -0
- package/dist/pages/handbook/tree.js.map +1 -0
- package/dist/pages/hiringDirectory.d.ts +60 -0
- package/dist/pages/hiringDirectory.d.ts.map +1 -0
- package/dist/pages/hiringDirectory.js +81 -0
- package/dist/pages/hiringDirectory.js.map +1 -0
- package/dist/pages/onboarding/HandbookStepPage.d.ts +14 -0
- package/dist/pages/onboarding/HandbookStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/HandbookStepPage.js +29 -0
- package/dist/pages/onboarding/HandbookStepPage.js.map +1 -0
- package/dist/pages/onboarding/InvitesStepPage.d.ts +2 -0
- package/dist/pages/onboarding/InvitesStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/InvitesStepPage.js +77 -0
- package/dist/pages/onboarding/InvitesStepPage.js.map +1 -0
- package/dist/pages/onboarding/WorkspaceStepPage.d.ts +2 -0
- package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/WorkspaceStepPage.js +72 -0
- package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -0
- package/dist/pages/positionDirectory.d.ts +20 -0
- package/dist/pages/positionDirectory.d.ts.map +1 -0
- package/dist/pages/positionDirectory.js +58 -0
- package/dist/pages/positionDirectory.js.map +1 -0
- package/dist/registry/UiExtensionProvider.d.ts +12 -0
- package/dist/registry/UiExtensionProvider.d.ts.map +1 -0
- package/dist/registry/UiExtensionProvider.js +10 -0
- package/dist/registry/UiExtensionProvider.js.map +1 -0
- package/dist/registry/context.d.ts +31 -0
- package/dist/registry/context.d.ts.map +1 -0
- package/dist/registry/context.js +45 -0
- package/dist/registry/context.js.map +1 -0
- package/dist/registry/contributions.d.ts +71 -0
- package/dist/registry/contributions.d.ts.map +1 -0
- package/dist/registry/contributions.js +25 -0
- package/dist/registry/contributions.js.map +1 -0
- package/dist/registry/overridable.d.ts +27 -0
- package/dist/registry/overridable.d.ts.map +1 -0
- package/dist/registry/overridable.js +29 -0
- package/dist/registry/overridable.js.map +1 -0
- package/dist/registry/registry.d.ts +68 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +112 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/testing/inputs.d.ts +13 -0
- package/dist/testing/inputs.d.ts.map +1 -0
- package/dist/testing/inputs.js +16 -0
- package/dist/testing/inputs.js.map +1 -0
- package/dist/testing/stubClient.d.ts +42 -0
- package/dist/testing/stubClient.d.ts.map +1 -0
- package/dist/testing/stubClient.js +29 -0
- package/dist/testing/stubClient.js.map +1 -0
- package/package.json +73 -0
- package/src/ServiceStatus.tsx +61 -0
- package/src/api/ApiProvider.tsx +40 -0
- package/src/api/client.ts +267 -0
- package/src/api/context.ts +25 -0
- package/src/api/errors.ts +14 -0
- package/src/api/health.ts +38 -0
- package/src/api/json.ts +4 -0
- package/src/api/queryClient.ts +35 -0
- package/src/api/resource.ts +412 -0
- package/src/api/resources/account.ts +220 -0
- package/src/api/resources/activities.ts +31 -0
- package/src/api/resources/agentTasks.ts +172 -0
- package/src/api/resources/candidates.ts +69 -0
- package/src/api/resources/companies.ts +72 -0
- package/src/api/resources/dashboard.ts +39 -0
- package/src/api/resources/deals.ts +82 -0
- package/src/api/resources/decisions.ts +60 -0
- package/src/api/resources/forms.ts +218 -0
- package/src/api/resources/handbookPages.ts +77 -0
- package/src/api/resources/importJobs.ts +229 -0
- package/src/api/resources/invites.ts +112 -0
- package/src/api/resources/mcpTools.ts +28 -0
- package/src/api/resources/members.ts +105 -0
- package/src/api/resources/moduleSettings.ts +67 -0
- package/src/api/resources/mutation.ts +31 -0
- package/src/api/resources/notes.ts +62 -0
- package/src/api/resources/opportunities.ts +84 -0
- package/src/api/resources/partnerships.ts +89 -0
- package/src/api/resources/people.ts +72 -0
- package/src/api/resources/pipelineStages.ts +93 -0
- package/src/api/resources/planItems.ts +103 -0
- package/src/api/resources/positions.ts +70 -0
- package/src/api/resources/raises.ts +82 -0
- package/src/api/resources/roles.ts +65 -0
- package/src/api/resources/search.ts +49 -0
- package/src/api/resources/session.ts +220 -0
- package/src/api/resources/webhooks.ts +157 -0
- package/src/api/resources/workspace.ts +81 -0
- package/src/app/KelpieApp.tsx +162 -0
- package/src/app/SessionGate.tsx +42 -0
- package/src/components/ActivitiesPanel.tsx +235 -0
- package/src/components/AgentTasks.tsx +473 -0
- package/src/components/Chip.tsx +36 -0
- package/src/components/CopyButton.tsx +48 -0
- package/src/components/DataTable.tsx +209 -0
- package/src/components/DecisionsPanel.tsx +233 -0
- package/src/components/DeleteRecord.tsx +73 -0
- package/src/components/EntitySearch.tsx +268 -0
- package/src/components/ErrorBoundary.tsx +58 -0
- package/src/components/InlineEdit.tsx +179 -0
- package/src/components/KanbanBoard.tsx +234 -0
- package/src/components/MarkdownView.tsx +73 -0
- package/src/components/NotesPanel.tsx +169 -0
- package/src/components/PageHeader.tsx +61 -0
- package/src/components/PhonesField.tsx +204 -0
- package/src/components/PlanAttention.tsx +249 -0
- package/src/components/PlanPanel.tsx +317 -0
- package/src/components/QueryState.tsx +83 -0
- package/src/components/RecordTabs.tsx +69 -0
- package/src/components/SectionHeader.tsx +63 -0
- package/src/components/SegmentedControl.tsx +48 -0
- package/src/components/Shell.tsx +300 -0
- package/src/components/SidebarField.tsx +18 -0
- package/src/components/SocialProfilesField.tsx +356 -0
- package/src/components/StageSettings.tsx +319 -0
- package/src/components/SummaryBlock.tsx +27 -0
- package/src/index.ts +190 -0
- package/src/lib/dates.ts +90 -0
- package/src/lib/money.ts +12 -0
- package/src/lib/names.ts +13 -0
- package/src/lib/plan.ts +147 -0
- package/src/lib/theme.ts +53 -0
- package/src/pages/CompaniesPage.tsx +143 -0
- package/src/pages/CompanyDetail.tsx +502 -0
- package/src/pages/DealDetail.tsx +512 -0
- package/src/pages/DealStageSettingsPage.tsx +67 -0
- package/src/pages/DealsPage.tsx +342 -0
- package/src/pages/DecisionsPage.tsx +211 -0
- package/src/pages/FormDetail.tsx +130 -0
- package/src/pages/FormsPage.tsx +151 -0
- package/src/pages/FundraisingPage.tsx +354 -0
- package/src/pages/HiringPage.tsx +169 -0
- package/src/pages/NotFoundPage.tsx +27 -0
- package/src/pages/OpportunitiesPage.tsx +349 -0
- package/src/pages/OpportunityDetail.tsx +337 -0
- package/src/pages/OpportunityStageSettingsPage.tsx +63 -0
- package/src/pages/PartnershipDetail.tsx +490 -0
- package/src/pages/PartnershipStageSettingsPage.tsx +63 -0
- package/src/pages/PartnershipsPage.tsx +353 -0
- package/src/pages/PeoplePage.tsx +145 -0
- package/src/pages/PersonDetail.tsx +526 -0
- package/src/pages/PlanningPage.tsx +318 -0
- package/src/pages/RaiseDetail.tsx +498 -0
- package/src/pages/RaiseStageSettingsPage.tsx +63 -0
- package/src/pages/RoleDetail.tsx +455 -0
- package/src/pages/SearchPage.tsx +135 -0
- package/src/pages/account/AccountLayout.tsx +38 -0
- package/src/pages/account/Field.tsx +20 -0
- package/src/pages/account/PreferencesPage.tsx +224 -0
- package/src/pages/account/ProfilePage.tsx +147 -0
- package/src/pages/account/SecurityPage.tsx +237 -0
- package/src/pages/admin/DataPage.tsx +772 -0
- package/src/pages/admin/McpPage.tsx +404 -0
- package/src/pages/admin/ModulesPage.tsx +113 -0
- package/src/pages/admin/TeamPage.tsx +386 -0
- package/src/pages/admin/WebhookDeliveries.tsx +184 -0
- package/src/pages/admin/WebhooksPage.tsx +443 -0
- package/src/pages/admin/WorkspaceSettingsPage.tsx +274 -0
- package/src/pages/auth/AuthForm.tsx +118 -0
- package/src/pages/auth/AuthLayout.tsx +74 -0
- package/src/pages/auth/ForgotPasswordPage.tsx +80 -0
- package/src/pages/auth/JoinPage.tsx +85 -0
- package/src/pages/auth/ResetPasswordPage.tsx +124 -0
- package/src/pages/auth/SignInPage.tsx +83 -0
- package/src/pages/auth/SignUpPage.tsx +93 -0
- package/src/pages/candidateFields.tsx +175 -0
- package/src/pages/dashboard/DashboardPage.tsx +108 -0
- package/src/pages/dashboard/attention.ts +206 -0
- package/src/pages/dashboard/sections.tsx +269 -0
- package/src/pages/fields.ts +21 -0
- package/src/pages/forms/EmbedPanel.tsx +95 -0
- package/src/pages/forms/FieldsEditor.tsx +412 -0
- package/src/pages/forms/FormSettings.tsx +159 -0
- package/src/pages/forms/SubmissionsTable.tsx +171 -0
- package/src/pages/forms/fieldList.ts +209 -0
- package/src/pages/forms/template.ts +37 -0
- package/src/pages/handbook/HandbookPage.tsx +500 -0
- package/src/pages/handbook/tree.ts +327 -0
- package/src/pages/hiringDirectory.ts +146 -0
- package/src/pages/onboarding/HandbookStepPage.tsx +75 -0
- package/src/pages/onboarding/InvitesStepPage.tsx +180 -0
- package/src/pages/onboarding/WorkspaceStepPage.tsx +122 -0
- package/src/pages/positionDirectory.ts +98 -0
- package/src/registry/UiExtensionProvider.tsx +20 -0
- package/src/registry/context.ts +67 -0
- package/src/registry/contributions.ts +89 -0
- package/src/registry/overridable.ts +62 -0
- package/src/registry/registry.ts +204 -0
- package/src/styles.css +175 -0
- package/src/testing/inputs.ts +18 -0
- package/src/testing/stubClient.ts +78 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONFLICT_MODE_LABELS,
|
|
3
|
+
IMPORT_OBJECTS,
|
|
4
|
+
IMPORT_SOURCES,
|
|
5
|
+
MATCH_KEYS,
|
|
6
|
+
OBJECT_COLUMNS,
|
|
7
|
+
OBJECT_LABELS,
|
|
8
|
+
SOURCE_LABELS,
|
|
9
|
+
defaultMatchKeyId,
|
|
10
|
+
findMatchKey,
|
|
11
|
+
isImportJobSettled,
|
|
12
|
+
requiredColumns,
|
|
13
|
+
} from '@kelpie/schemas'
|
|
14
|
+
import type {
|
|
15
|
+
ImportColumnMap,
|
|
16
|
+
ImportConflictMode,
|
|
17
|
+
ImportJob,
|
|
18
|
+
ImportObject,
|
|
19
|
+
ImportPreviewRow,
|
|
20
|
+
ImportRowAction,
|
|
21
|
+
ImportSource,
|
|
22
|
+
} from '@kelpie/schemas'
|
|
23
|
+
import { useState } from 'react'
|
|
24
|
+
import type { ChangeEvent, FormEvent, ReactNode } from 'react'
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
saveCsv,
|
|
28
|
+
useCommitImportJob,
|
|
29
|
+
useCreateImportJob,
|
|
30
|
+
useDeleteImportJob,
|
|
31
|
+
useExportCsv,
|
|
32
|
+
useImportJob,
|
|
33
|
+
} from '../../api/resources/importJobs.ts'
|
|
34
|
+
import { PageHeader } from '../../components/PageHeader.tsx'
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Import and export, ported from the mockup's `/admin/data`.
|
|
38
|
+
*
|
|
39
|
+
* The wizard is the mockup's four steps, with the parsing moved to the server.
|
|
40
|
+
* The mockup read the CSV in the browser to fill its mapping table; here the
|
|
41
|
+
* upload is the first dry run, and the job comes back carrying the file's own
|
|
42
|
+
* headers and the map the server derived. Correcting a mapping re-uploads the
|
|
43
|
+
* same file, which is why the `File` stays in state after the first request.
|
|
44
|
+
*
|
|
45
|
+
* Narrowed from the mockup: no "Load sample" button. It fetched fixtures out of
|
|
46
|
+
* `public/fixtures`, and none of those are part of the application.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
type WizardStep = 'source' | 'upload' | 'map' | 'result'
|
|
50
|
+
|
|
51
|
+
const STEPS: readonly { readonly id: WizardStep; readonly label: string }[] = [
|
|
52
|
+
{ id: 'source', label: 'Source' },
|
|
53
|
+
{ id: 'upload', label: 'Upload' },
|
|
54
|
+
{ id: 'map', label: 'Map' },
|
|
55
|
+
{ id: 'result', label: 'Dry-run' },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
export function DataPage(): ReactNode {
|
|
59
|
+
const [exportObject, setExportObject] = useState<ImportObject>('companies')
|
|
60
|
+
|
|
61
|
+
const [step, setStep] = useState<WizardStep>('source')
|
|
62
|
+
const [source, setSource] = useState<ImportSource>('custom')
|
|
63
|
+
const [object, setObject] = useState<ImportObject>('companies')
|
|
64
|
+
const [file, setFile] = useState<File | null>(null)
|
|
65
|
+
const [columnMap, setColumnMap] = useState<ImportColumnMap>({})
|
|
66
|
+
const [conflictMode, setConflictMode] = useState<ImportConflictMode>('skip')
|
|
67
|
+
const [matchKeyId, setMatchKeyId] = useState<string>(() => defaultMatchKeyId('companies'))
|
|
68
|
+
const [jobId, setJobId] = useState<string | undefined>(undefined)
|
|
69
|
+
const [problem, setProblem] = useState<string | null>(null)
|
|
70
|
+
|
|
71
|
+
const exportCsv = useExportCsv()
|
|
72
|
+
const createJob = useCreateImportJob()
|
|
73
|
+
const commitJob = useCommitImportJob()
|
|
74
|
+
const deleteJob = useDeleteImportJob()
|
|
75
|
+
const watched = useImportJob(jobId)
|
|
76
|
+
const job = watched.record
|
|
77
|
+
|
|
78
|
+
const failure = problem ?? messageOf(exportCsv.error ?? createJob.error ?? commitJob.error ?? watched.error)
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Drops a job the wizard is walking away from, so the file stored against it
|
|
82
|
+
* goes too.
|
|
83
|
+
*
|
|
84
|
+
* Every path out of a job leads here, because each one is the same thing: a
|
|
85
|
+
* correction that uploads the file again, a step back, or starting over. None
|
|
86
|
+
* of them can reach the old job afterwards.
|
|
87
|
+
*
|
|
88
|
+
* Fire and forget, and `deleteJob.error` is deliberately left out of `failure`
|
|
89
|
+
* above. The caller has already got what they asked for, and tidying up is not
|
|
90
|
+
* theirs to fix. The one refusal the server makes is a job still committing,
|
|
91
|
+
* and that one settles on its own.
|
|
92
|
+
*/
|
|
93
|
+
function discardJob(id: string | undefined): void {
|
|
94
|
+
if (id !== undefined) {
|
|
95
|
+
deleteJob.run(id)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Starts a step that talks to the API and drops its rejection.
|
|
101
|
+
*
|
|
102
|
+
* These steps use `runAsync` because they chain: a refused mapping must not
|
|
103
|
+
* advance the wizard, and only a rejection stops the lines after the await.
|
|
104
|
+
* The failure is already on screen through `failure` above, so the rejection
|
|
105
|
+
* has nothing left to say, and discarding it with a bare `void` would leave an
|
|
106
|
+
* unhandled rejection behind every refused upload.
|
|
107
|
+
*/
|
|
108
|
+
function runStep(work: Promise<unknown>): void {
|
|
109
|
+
void work.catch(() => undefined)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resetWizard(): void {
|
|
113
|
+
discardJob(jobId)
|
|
114
|
+
setStep('source')
|
|
115
|
+
setFile(null)
|
|
116
|
+
setColumnMap({})
|
|
117
|
+
setConflictMode('skip')
|
|
118
|
+
setMatchKeyId(defaultMatchKeyId(object))
|
|
119
|
+
setJobId(undefined)
|
|
120
|
+
setProblem(null)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function onDownload(target: ImportObject, template: boolean): Promise<void> {
|
|
124
|
+
setProblem(null)
|
|
125
|
+
saveCsv(await exportCsv.runAsync({ object: target, template }))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Uploads the file and reads back the headers, the derived map, and the forecast. */
|
|
129
|
+
async function runDryRun(next: File, map: ImportColumnMap | undefined): Promise<ImportJob> {
|
|
130
|
+
const superseded = jobId
|
|
131
|
+
const created = await createJob.runAsync({
|
|
132
|
+
file: next,
|
|
133
|
+
source,
|
|
134
|
+
object,
|
|
135
|
+
conflictMode,
|
|
136
|
+
matchKeyId,
|
|
137
|
+
...(map === undefined ? {} : { columnMap: map }),
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
setJobId(created.id)
|
|
141
|
+
|
|
142
|
+
// After the create rather than before it. A map the server rejects throws
|
|
143
|
+
// out of `runAsync`, and the caller is still reading the old job's headers
|
|
144
|
+
// in the mapping table it has to correct.
|
|
145
|
+
discardJob(superseded)
|
|
146
|
+
|
|
147
|
+
return created
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function onFileChange(event: ChangeEvent<HTMLInputElement>): Promise<void> {
|
|
151
|
+
const chosen = event.target.files?.[0]
|
|
152
|
+
|
|
153
|
+
// Cleared so choosing the same file twice fires the event again, which is
|
|
154
|
+
// what a caller who fixed the file on disk expects.
|
|
155
|
+
event.target.value = ''
|
|
156
|
+
|
|
157
|
+
if (chosen === undefined) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setProblem(null)
|
|
162
|
+
setFile(chosen)
|
|
163
|
+
|
|
164
|
+
const created = await runDryRun(chosen, undefined)
|
|
165
|
+
|
|
166
|
+
// The first upload takes the server's own reading of the file, which is what
|
|
167
|
+
// the mapping table is then built from.
|
|
168
|
+
setColumnMap(created.columnMap)
|
|
169
|
+
setMatchKeyId(created.matchKey)
|
|
170
|
+
setStep('map')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function onRerun(event: FormEvent): Promise<void> {
|
|
174
|
+
event.preventDefault()
|
|
175
|
+
|
|
176
|
+
if (file === null) {
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const matchKey = findMatchKey(object, matchKeyId)
|
|
181
|
+
const missing =
|
|
182
|
+
matchKey === undefined
|
|
183
|
+
? []
|
|
184
|
+
: requiredColumns(object, matchKey).filter((column) => (columnMap[column] ?? null) === null)
|
|
185
|
+
|
|
186
|
+
if (missing.length > 0) {
|
|
187
|
+
setProblem(`Map these columns first: ${missing.join(', ')}`)
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
setProblem(null)
|
|
192
|
+
await runDryRun(file, columnMap)
|
|
193
|
+
setStep('result')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function onCommit(): void {
|
|
197
|
+
// The file goes back with the commit: the server kept its digest, not its
|
|
198
|
+
// bytes. This is the same `File` every dry run was run against.
|
|
199
|
+
if (jobId === undefined || file === null) {
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
setProblem(null)
|
|
204
|
+
commitJob.run({ id: jobId, file })
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<div className="animate-slide-in space-y-10">
|
|
209
|
+
<PageHeader
|
|
210
|
+
title="Import & export"
|
|
211
|
+
description="CSV for People, Companies, Positions, and Deals. Custom files or HubSpot / Salesforce packs."
|
|
212
|
+
/>
|
|
213
|
+
|
|
214
|
+
{failure === null ? null : (
|
|
215
|
+
<p role="alert" className="text-[12px] font-medium text-danger">
|
|
216
|
+
{failure}
|
|
217
|
+
</p>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
<section className="space-y-4">
|
|
221
|
+
<div>
|
|
222
|
+
<h2 className="text-[15px] font-semibold text-ink">Export</h2>
|
|
223
|
+
<p className="mt-1 text-[13px] text-ink-muted">
|
|
224
|
+
Download this workspace as CSV. A Kelpie export reads straight back in with no column
|
|
225
|
+
mapping.
|
|
226
|
+
</p>
|
|
227
|
+
</div>
|
|
228
|
+
<div className="flex flex-wrap items-end gap-3">
|
|
229
|
+
<Field label="Object">
|
|
230
|
+
<select
|
|
231
|
+
value={exportObject}
|
|
232
|
+
onChange={(event) => setExportObject(event.target.value as ImportObject)}
|
|
233
|
+
className="w-48 rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent"
|
|
234
|
+
>
|
|
235
|
+
{IMPORT_OBJECTS.map((item) => (
|
|
236
|
+
<option key={item} value={item}>
|
|
237
|
+
{OBJECT_LABELS[item]}
|
|
238
|
+
</option>
|
|
239
|
+
))}
|
|
240
|
+
</select>
|
|
241
|
+
</Field>
|
|
242
|
+
<button
|
|
243
|
+
type="button"
|
|
244
|
+
onClick={() => runStep(onDownload(exportObject, false))}
|
|
245
|
+
disabled={exportCsv.isPending}
|
|
246
|
+
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"
|
|
247
|
+
>
|
|
248
|
+
{exportCsv.isPending ? 'Preparing…' : 'Download CSV'}
|
|
249
|
+
</button>
|
|
250
|
+
</div>
|
|
251
|
+
</section>
|
|
252
|
+
|
|
253
|
+
<section className="space-y-4 border-t border-border pt-8">
|
|
254
|
+
<div className="flex items-start justify-between gap-3">
|
|
255
|
+
<div>
|
|
256
|
+
<h2 className="text-[15px] font-semibold text-ink">Import</h2>
|
|
257
|
+
<p className="mt-1 text-[13px] text-ink-muted">
|
|
258
|
+
Custom CSV or HubSpot / Salesforce export packs. Every job is dry-run first; nothing is
|
|
259
|
+
written until you commit.
|
|
260
|
+
</p>
|
|
261
|
+
</div>
|
|
262
|
+
{step === 'source' ? null : (
|
|
263
|
+
<button
|
|
264
|
+
type="button"
|
|
265
|
+
onClick={resetWizard}
|
|
266
|
+
className="shrink-0 text-[12px] font-medium text-ink-muted hover:text-ink"
|
|
267
|
+
>
|
|
268
|
+
Start over
|
|
269
|
+
</button>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<StepIndicator step={step} />
|
|
274
|
+
|
|
275
|
+
{step === 'source' ? (
|
|
276
|
+
<form
|
|
277
|
+
className="space-y-4"
|
|
278
|
+
onSubmit={(event) => {
|
|
279
|
+
event.preventDefault()
|
|
280
|
+
setStep('upload')
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
<Field label="Source">
|
|
284
|
+
<select
|
|
285
|
+
value={source}
|
|
286
|
+
onChange={(event) => setSource(event.target.value as ImportSource)}
|
|
287
|
+
className="w-full max-w-md rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent"
|
|
288
|
+
>
|
|
289
|
+
{IMPORT_SOURCES.map((item) => (
|
|
290
|
+
<option key={item} value={item}>
|
|
291
|
+
{SOURCE_LABELS[item]}
|
|
292
|
+
</option>
|
|
293
|
+
))}
|
|
294
|
+
</select>
|
|
295
|
+
</Field>
|
|
296
|
+
<Field label="Object">
|
|
297
|
+
<select
|
|
298
|
+
value={object}
|
|
299
|
+
onChange={(event) => {
|
|
300
|
+
const next = event.target.value as ImportObject
|
|
301
|
+
|
|
302
|
+
setObject(next)
|
|
303
|
+
setMatchKeyId(defaultMatchKeyId(next))
|
|
304
|
+
}}
|
|
305
|
+
className="w-full max-w-md rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent"
|
|
306
|
+
>
|
|
307
|
+
{IMPORT_OBJECTS.map((item) => (
|
|
308
|
+
<option key={item} value={item}>
|
|
309
|
+
{OBJECT_LABELS[item]}
|
|
310
|
+
</option>
|
|
311
|
+
))}
|
|
312
|
+
</select>
|
|
313
|
+
</Field>
|
|
314
|
+
<div className="rounded-md border border-border px-4 py-3">
|
|
315
|
+
<div className="text-[13px] font-medium text-ink">Need a blank file?</div>
|
|
316
|
+
<p className="mt-0.5 text-[12px] text-ink-muted">
|
|
317
|
+
Download a Kelpie CSV template for {OBJECT_LABELS[object].toLowerCase()}, fill it in,
|
|
318
|
+
then continue.
|
|
319
|
+
</p>
|
|
320
|
+
<button
|
|
321
|
+
type="button"
|
|
322
|
+
onClick={() => runStep(onDownload(object, true))}
|
|
323
|
+
className="mt-3 rounded-md border border-border bg-surface px-3.5 py-2 text-[12px] font-semibold text-ink transition hover:border-accent"
|
|
324
|
+
>
|
|
325
|
+
Download template
|
|
326
|
+
</button>
|
|
327
|
+
</div>
|
|
328
|
+
<p className="text-[12px] text-ink-muted">
|
|
329
|
+
Import order for linked data: Companies → People → Positions → Deals.
|
|
330
|
+
</p>
|
|
331
|
+
<button
|
|
332
|
+
type="submit"
|
|
333
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover"
|
|
334
|
+
>
|
|
335
|
+
Continue
|
|
336
|
+
</button>
|
|
337
|
+
</form>
|
|
338
|
+
) : null}
|
|
339
|
+
|
|
340
|
+
{step === 'upload' ? (
|
|
341
|
+
<div className="space-y-4">
|
|
342
|
+
<p className="text-[13px] text-ink-muted">
|
|
343
|
+
{SOURCE_LABELS[source]} → {OBJECT_LABELS[object]}
|
|
344
|
+
</p>
|
|
345
|
+
<label 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">
|
|
346
|
+
<span className="text-[13px] font-medium text-ink">
|
|
347
|
+
{createJob.isPending ? 'Reading the file…' : 'Drop a CSV here or click to browse'}
|
|
348
|
+
</span>
|
|
349
|
+
<span className="mt-1 text-[12px] text-ink-muted">
|
|
350
|
+
UTF-8 CSV · max 10 MB · max 10,000 rows
|
|
351
|
+
</span>
|
|
352
|
+
<input
|
|
353
|
+
type="file"
|
|
354
|
+
accept=".csv,text/csv"
|
|
355
|
+
className="sr-only"
|
|
356
|
+
disabled={createJob.isPending}
|
|
357
|
+
onChange={(event) => runStep(onFileChange(event))}
|
|
358
|
+
/>
|
|
359
|
+
</label>
|
|
360
|
+
<button
|
|
361
|
+
type="button"
|
|
362
|
+
onClick={() => setStep('source')}
|
|
363
|
+
className="rounded-md px-3 py-2 text-[12px] font-medium text-ink-muted hover:text-ink"
|
|
364
|
+
>
|
|
365
|
+
Back
|
|
366
|
+
</button>
|
|
367
|
+
</div>
|
|
368
|
+
) : null}
|
|
369
|
+
|
|
370
|
+
{step === 'map' && job !== undefined ? (
|
|
371
|
+
<MappingForm
|
|
372
|
+
job={job}
|
|
373
|
+
object={object}
|
|
374
|
+
columnMap={columnMap}
|
|
375
|
+
conflictMode={conflictMode}
|
|
376
|
+
matchKeyId={matchKeyId}
|
|
377
|
+
isPending={createJob.isPending}
|
|
378
|
+
onColumnMapChange={setColumnMap}
|
|
379
|
+
onConflictModeChange={setConflictMode}
|
|
380
|
+
onMatchKeyChange={setMatchKeyId}
|
|
381
|
+
onSubmit={(event) => runStep(onRerun(event))}
|
|
382
|
+
onBack={() => {
|
|
383
|
+
discardJob(jobId)
|
|
384
|
+
setFile(null)
|
|
385
|
+
setJobId(undefined)
|
|
386
|
+
setStep('upload')
|
|
387
|
+
}}
|
|
388
|
+
/>
|
|
389
|
+
) : null}
|
|
390
|
+
|
|
391
|
+
{step === 'result' && job !== undefined ? (
|
|
392
|
+
<ResultPanel
|
|
393
|
+
job={job}
|
|
394
|
+
object={object}
|
|
395
|
+
isCommitting={commitJob.isPending}
|
|
396
|
+
onCommit={onCommit}
|
|
397
|
+
onBack={() => setStep('map')}
|
|
398
|
+
/>
|
|
399
|
+
) : null}
|
|
400
|
+
</section>
|
|
401
|
+
</div>
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
interface MappingFormProps {
|
|
406
|
+
readonly job: ImportJob
|
|
407
|
+
readonly object: ImportObject
|
|
408
|
+
readonly columnMap: ImportColumnMap
|
|
409
|
+
readonly conflictMode: ImportConflictMode
|
|
410
|
+
readonly matchKeyId: string
|
|
411
|
+
readonly isPending: boolean
|
|
412
|
+
readonly onColumnMapChange: (map: ImportColumnMap) => void
|
|
413
|
+
readonly onConflictModeChange: (mode: ImportConflictMode) => void
|
|
414
|
+
readonly onMatchKeyChange: (id: string) => void
|
|
415
|
+
readonly onSubmit: (event: FormEvent) => void
|
|
416
|
+
readonly onBack: () => void
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function MappingForm(props: MappingFormProps): ReactNode {
|
|
420
|
+
const matchKey = findMatchKey(props.object, props.matchKeyId)
|
|
421
|
+
const keyColumns = new Set(matchKey?.columns ?? [])
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<form className="space-y-4" onSubmit={props.onSubmit}>
|
|
425
|
+
<p className="text-[13px] text-ink-muted">
|
|
426
|
+
Mapping <span className="font-medium text-ink">{props.job.fileName ?? 'the file'}</span> (
|
|
427
|
+
{props.job.counts.total} rows). Match Kelpie fields to CSV columns.
|
|
428
|
+
</p>
|
|
429
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
430
|
+
<Field label="On match">
|
|
431
|
+
<select
|
|
432
|
+
value={props.conflictMode}
|
|
433
|
+
onChange={(event) => props.onConflictModeChange(event.target.value as ImportConflictMode)}
|
|
434
|
+
className="w-full rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent"
|
|
435
|
+
>
|
|
436
|
+
{Object.entries(CONFLICT_MODE_LABELS).map(([mode, label]) => (
|
|
437
|
+
<option key={mode} value={mode}>
|
|
438
|
+
{label}
|
|
439
|
+
</option>
|
|
440
|
+
))}
|
|
441
|
+
</select>
|
|
442
|
+
</Field>
|
|
443
|
+
<Field label="Key column">
|
|
444
|
+
<select
|
|
445
|
+
value={props.matchKeyId}
|
|
446
|
+
onChange={(event) => props.onMatchKeyChange(event.target.value)}
|
|
447
|
+
className="w-full rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent"
|
|
448
|
+
>
|
|
449
|
+
{MATCH_KEYS[props.object].map((key) => (
|
|
450
|
+
<option key={key.id} value={key.id}>
|
|
451
|
+
{key.label}
|
|
452
|
+
</option>
|
|
453
|
+
))}
|
|
454
|
+
</select>
|
|
455
|
+
</Field>
|
|
456
|
+
</div>
|
|
457
|
+
<p className="text-[12px] text-ink-muted">
|
|
458
|
+
{props.conflictMode === 'update'
|
|
459
|
+
? 'Rows matching an existing record on the key column overwrite the fields you map below. A blank cell changes nothing.'
|
|
460
|
+
: 'Rows matching an existing record on the key column are skipped.'}{' '}
|
|
461
|
+
Key columns must be mapped.
|
|
462
|
+
</p>
|
|
463
|
+
<div className="overflow-hidden rounded-md border border-border">
|
|
464
|
+
<table className="w-full text-left text-[13px]">
|
|
465
|
+
<thead>
|
|
466
|
+
<tr className="border-b border-border bg-surface text-[11px] font-semibold tracking-wide text-ink-muted uppercase">
|
|
467
|
+
<th className="px-4 py-2.5">Kelpie field</th>
|
|
468
|
+
<th className="px-4 py-2.5">CSV column</th>
|
|
469
|
+
</tr>
|
|
470
|
+
</thead>
|
|
471
|
+
<tbody>
|
|
472
|
+
{OBJECT_COLUMNS[props.object].map((column) => {
|
|
473
|
+
const isKey = keyColumns.has(column.key)
|
|
474
|
+
|
|
475
|
+
return (
|
|
476
|
+
<tr key={column.key} className="border-b border-border last:border-0">
|
|
477
|
+
<td className="px-4 py-2.5">
|
|
478
|
+
<span className="font-medium">{column.label}</span>
|
|
479
|
+
<span className="ml-2 font-mono text-[11px] text-ink-faint">{column.key}</span>
|
|
480
|
+
{column.required || isKey ? (
|
|
481
|
+
<span className="ml-2 text-[11px] text-danger">
|
|
482
|
+
{isKey ? 'key' : 'required'}
|
|
483
|
+
</span>
|
|
484
|
+
) : null}
|
|
485
|
+
</td>
|
|
486
|
+
<td className="px-4 py-2.5">
|
|
487
|
+
<select
|
|
488
|
+
value={props.columnMap[column.key] ?? ''}
|
|
489
|
+
onChange={(event) =>
|
|
490
|
+
props.onColumnMapChange({
|
|
491
|
+
...props.columnMap,
|
|
492
|
+
[column.key]: event.target.value === '' ? null : event.target.value,
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
className="w-full max-w-xs rounded-md border border-border bg-surface px-2 py-1.5 text-[13px] outline-none focus:border-accent"
|
|
496
|
+
>
|
|
497
|
+
<option value="">— ignore —</option>
|
|
498
|
+
{props.job.sourceHeaders.map((header) => (
|
|
499
|
+
<option key={header} value={header}>
|
|
500
|
+
{header}
|
|
501
|
+
</option>
|
|
502
|
+
))}
|
|
503
|
+
</select>
|
|
504
|
+
</td>
|
|
505
|
+
</tr>
|
|
506
|
+
)
|
|
507
|
+
})}
|
|
508
|
+
</tbody>
|
|
509
|
+
</table>
|
|
510
|
+
</div>
|
|
511
|
+
<div className="flex flex-wrap gap-3">
|
|
512
|
+
<button
|
|
513
|
+
type="submit"
|
|
514
|
+
disabled={props.isPending}
|
|
515
|
+
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"
|
|
516
|
+
>
|
|
517
|
+
{props.isPending ? 'Running…' : 'Run dry-run'}
|
|
518
|
+
</button>
|
|
519
|
+
<button
|
|
520
|
+
type="button"
|
|
521
|
+
onClick={props.onBack}
|
|
522
|
+
className="rounded-md px-3 py-2 text-[12px] font-medium text-ink-muted hover:text-ink"
|
|
523
|
+
>
|
|
524
|
+
Back
|
|
525
|
+
</button>
|
|
526
|
+
</div>
|
|
527
|
+
</form>
|
|
528
|
+
)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
interface ResultPanelProps {
|
|
532
|
+
readonly job: ImportJob
|
|
533
|
+
readonly object: ImportObject
|
|
534
|
+
readonly isCommitting: boolean
|
|
535
|
+
readonly onCommit: () => void
|
|
536
|
+
readonly onBack: () => void
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function ResultPanel({ job, object, isCommitting, onCommit, onBack }: ResultPanelProps): ReactNode {
|
|
540
|
+
const working = !isImportJobSettled(job.status)
|
|
541
|
+
const done = job.status === 'completed'
|
|
542
|
+
const nothingToDo = job.counts.create === 0 && job.counts.update === 0
|
|
543
|
+
|
|
544
|
+
return (
|
|
545
|
+
<div className="space-y-4">
|
|
546
|
+
<p className="text-[13px] text-ink-muted">
|
|
547
|
+
Job <span className="font-mono text-[12px] text-ink">{job.id}</span> · status {job.status}
|
|
548
|
+
{working ? ' · working…' : null}
|
|
549
|
+
</p>
|
|
550
|
+
<div className="grid grid-cols-2 gap-3 sm:grid-cols-5">
|
|
551
|
+
<Stat label="Total" value={job.counts.total} />
|
|
552
|
+
<Stat label="Create" value={job.counts.create} />
|
|
553
|
+
<Stat label="Update" value={job.counts.update} />
|
|
554
|
+
<Stat label="Skip" value={job.counts.skip} />
|
|
555
|
+
<Stat label="Error" value={job.counts.error} danger />
|
|
556
|
+
</div>
|
|
557
|
+
<p className="text-[12px] text-ink-muted">
|
|
558
|
+
On match: {CONFLICT_MODE_LABELS[job.conflictMode].toLowerCase()}. Key column:{' '}
|
|
559
|
+
<span className="font-mono text-[11px] text-ink">
|
|
560
|
+
{findMatchKey(object, job.matchKey)?.label ?? job.matchKey}
|
|
561
|
+
</span>
|
|
562
|
+
</p>
|
|
563
|
+
|
|
564
|
+
{job.preview.length > 0 ? (
|
|
565
|
+
<div className="space-y-2">
|
|
566
|
+
<div>
|
|
567
|
+
<h3 className="text-[13px] font-semibold text-ink">Mapping preview</h3>
|
|
568
|
+
<p className="mt-0.5 text-[12px] text-ink-muted">
|
|
569
|
+
The first {job.preview.length} row{job.preview.length === 1 ? '' : 's'} as Kelpie read
|
|
570
|
+
them. Go back to mapping if a column looks wrong.
|
|
571
|
+
</p>
|
|
572
|
+
</div>
|
|
573
|
+
<PreviewTable object={object} rows={job.preview} />
|
|
574
|
+
</div>
|
|
575
|
+
) : null}
|
|
576
|
+
|
|
577
|
+
{job.errors.length > 0 ? (
|
|
578
|
+
<div className="space-y-2">
|
|
579
|
+
<div className="overflow-hidden rounded-md border border-border">
|
|
580
|
+
<table className="w-full text-left text-[13px]">
|
|
581
|
+
<thead>
|
|
582
|
+
<tr className="border-b border-border bg-surface text-[11px] font-semibold tracking-wide text-ink-muted uppercase">
|
|
583
|
+
<th className="px-4 py-2.5">Row</th>
|
|
584
|
+
<th className="px-4 py-2.5">Field</th>
|
|
585
|
+
<th className="px-4 py-2.5">Message</th>
|
|
586
|
+
</tr>
|
|
587
|
+
</thead>
|
|
588
|
+
<tbody>
|
|
589
|
+
{job.errors.map((error, index) => (
|
|
590
|
+
<tr
|
|
591
|
+
key={`${String(error.row)}-${error.field}-${String(index)}`}
|
|
592
|
+
className="border-b border-border last:border-0"
|
|
593
|
+
>
|
|
594
|
+
<td className="px-4 py-2.5 font-mono text-[12px]">{error.row}</td>
|
|
595
|
+
<td className="px-4 py-2.5 font-mono text-[12px]">{error.field}</td>
|
|
596
|
+
<td className="px-4 py-2.5 text-ink-muted">{error.message}</td>
|
|
597
|
+
</tr>
|
|
598
|
+
))}
|
|
599
|
+
</tbody>
|
|
600
|
+
</table>
|
|
601
|
+
</div>
|
|
602
|
+
{job.counts.error > job.errors.length ? (
|
|
603
|
+
<p className="text-[12px] text-ink-muted">
|
|
604
|
+
Showing {job.errors.length} of {job.counts.error} failing rows.
|
|
605
|
+
</p>
|
|
606
|
+
) : null}
|
|
607
|
+
</div>
|
|
608
|
+
) : (
|
|
609
|
+
<p className="text-[13px] text-ink-muted">No row errors.</p>
|
|
610
|
+
)}
|
|
611
|
+
|
|
612
|
+
<div className="flex flex-wrap gap-3">
|
|
613
|
+
<button
|
|
614
|
+
type="button"
|
|
615
|
+
onClick={onCommit}
|
|
616
|
+
disabled={done || working || isCommitting || nothingToDo}
|
|
617
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover disabled:cursor-not-allowed disabled:opacity-50"
|
|
618
|
+
>
|
|
619
|
+
{done ? 'Committed' : isCommitting || working ? 'Working…' : 'Commit import'}
|
|
620
|
+
</button>
|
|
621
|
+
<button
|
|
622
|
+
type="button"
|
|
623
|
+
onClick={onBack}
|
|
624
|
+
className="rounded-md px-3 py-2 text-[12px] font-medium text-ink-muted hover:text-ink"
|
|
625
|
+
>
|
|
626
|
+
Back to mapping
|
|
627
|
+
</button>
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
630
|
+
)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function PreviewTable({
|
|
634
|
+
object,
|
|
635
|
+
rows,
|
|
636
|
+
}: {
|
|
637
|
+
readonly object: ImportObject
|
|
638
|
+
readonly rows: readonly ImportPreviewRow[]
|
|
639
|
+
}): ReactNode {
|
|
640
|
+
const keys = OBJECT_COLUMNS[object]
|
|
641
|
+
.map((column) => column.key)
|
|
642
|
+
.filter((key) => rows.some((row) => key in row.values))
|
|
643
|
+
|
|
644
|
+
return (
|
|
645
|
+
<div className="overflow-x-auto rounded-md border border-border">
|
|
646
|
+
<table className="w-full min-w-[36rem] text-left text-[13px]">
|
|
647
|
+
<thead>
|
|
648
|
+
<tr className="border-b border-border bg-surface text-[11px] font-semibold tracking-wide text-ink-muted uppercase">
|
|
649
|
+
<th className="px-3 py-2.5">Row</th>
|
|
650
|
+
<th className="px-3 py-2.5">Action</th>
|
|
651
|
+
{keys.map((key) => (
|
|
652
|
+
<th key={key} className="px-3 py-2.5 font-mono normal-case">
|
|
653
|
+
{key}
|
|
654
|
+
</th>
|
|
655
|
+
))}
|
|
656
|
+
</tr>
|
|
657
|
+
</thead>
|
|
658
|
+
<tbody>
|
|
659
|
+
{rows.map((row) => (
|
|
660
|
+
<tr key={row.row} className="border-b border-border last:border-0">
|
|
661
|
+
<td className="px-3 py-2.5 font-mono text-[12px] text-ink-muted">{row.row}</td>
|
|
662
|
+
<td className="px-3 py-2.5">
|
|
663
|
+
<ActionChip action={row.action} />
|
|
664
|
+
</td>
|
|
665
|
+
{keys.map((key) => (
|
|
666
|
+
<td
|
|
667
|
+
key={key}
|
|
668
|
+
className="max-w-[12rem] truncate px-3 py-2.5 text-ink"
|
|
669
|
+
title={row.values[key] ?? ''}
|
|
670
|
+
>
|
|
671
|
+
{row.values[key] === undefined || row.values[key] === '' ? (
|
|
672
|
+
<span className="text-ink-faint">—</span>
|
|
673
|
+
) : (
|
|
674
|
+
row.values[key]
|
|
675
|
+
)}
|
|
676
|
+
</td>
|
|
677
|
+
))}
|
|
678
|
+
</tr>
|
|
679
|
+
))}
|
|
680
|
+
</tbody>
|
|
681
|
+
</table>
|
|
682
|
+
</div>
|
|
683
|
+
)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const ACTION_STYLES: Readonly<Record<ImportRowAction, string>> = {
|
|
687
|
+
pending: 'bg-surface text-ink-faint',
|
|
688
|
+
create: 'bg-accent/15 text-accent-hover',
|
|
689
|
+
update: 'bg-warning-soft text-warning',
|
|
690
|
+
skip: 'bg-surface text-ink-muted',
|
|
691
|
+
error: 'bg-danger-soft text-danger',
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function ActionChip({ action }: { readonly action: ImportRowAction }): ReactNode {
|
|
695
|
+
return (
|
|
696
|
+
<span
|
|
697
|
+
className={[
|
|
698
|
+
'inline-block rounded px-1.5 py-0.5 text-[11px] font-semibold uppercase',
|
|
699
|
+
ACTION_STYLES[action],
|
|
700
|
+
].join(' ')}
|
|
701
|
+
>
|
|
702
|
+
{action}
|
|
703
|
+
</span>
|
|
704
|
+
)
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function StepIndicator({ step }: { readonly step: WizardStep }): ReactNode {
|
|
708
|
+
const active = STEPS.findIndex((item) => item.id === step)
|
|
709
|
+
|
|
710
|
+
return (
|
|
711
|
+
<ol className="flex flex-wrap gap-2 text-[12px]">
|
|
712
|
+
{STEPS.map((item, index) => (
|
|
713
|
+
<li
|
|
714
|
+
key={item.id}
|
|
715
|
+
className={[
|
|
716
|
+
'rounded-md px-2.5 py-1 font-medium',
|
|
717
|
+
index === active
|
|
718
|
+
? 'bg-accent/15 text-accent-hover'
|
|
719
|
+
: index < active
|
|
720
|
+
? 'text-ink'
|
|
721
|
+
: 'text-ink-faint',
|
|
722
|
+
].join(' ')}
|
|
723
|
+
>
|
|
724
|
+
{index + 1}. {item.label}
|
|
725
|
+
</li>
|
|
726
|
+
))}
|
|
727
|
+
</ol>
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function Stat({
|
|
732
|
+
label,
|
|
733
|
+
value,
|
|
734
|
+
danger,
|
|
735
|
+
}: {
|
|
736
|
+
readonly label: string
|
|
737
|
+
readonly value: number
|
|
738
|
+
readonly danger?: boolean
|
|
739
|
+
}): ReactNode {
|
|
740
|
+
return (
|
|
741
|
+
<div className="rounded-md border border-border px-3 py-2.5">
|
|
742
|
+
<div className="text-[11px] font-semibold tracking-wide text-ink-muted uppercase">{label}</div>
|
|
743
|
+
<div
|
|
744
|
+
className={[
|
|
745
|
+
'mt-1 text-[20px] font-semibold tabular-nums',
|
|
746
|
+
danger === true && value > 0 ? 'text-danger' : 'text-ink',
|
|
747
|
+
].join(' ')}
|
|
748
|
+
>
|
|
749
|
+
{value}
|
|
750
|
+
</div>
|
|
751
|
+
</div>
|
|
752
|
+
)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function Field({
|
|
756
|
+
label,
|
|
757
|
+
children,
|
|
758
|
+
}: {
|
|
759
|
+
readonly label: string
|
|
760
|
+
readonly children: ReactNode
|
|
761
|
+
}): ReactNode {
|
|
762
|
+
return (
|
|
763
|
+
<label className="block">
|
|
764
|
+
<span className="mb-1.5 block text-[12px] font-medium text-ink">{label}</span>
|
|
765
|
+
{children}
|
|
766
|
+
</label>
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function messageOf(error: Error | null | undefined): string | null {
|
|
771
|
+
return error === null || error === undefined ? null : error.message
|
|
772
|
+
}
|