@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,180 @@
|
|
|
1
|
+
import { INVITABLE_ROLES } from '@kelpie/schemas'
|
|
2
|
+
import type { InvitableRole } from '@kelpie/schemas'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import type { FormEvent } from 'react'
|
|
5
|
+
import { useNavigate } from 'react-router'
|
|
6
|
+
|
|
7
|
+
import { useSendInvite } from '../../api/resources/invites.ts'
|
|
8
|
+
import { AUTH_INPUT_CLASS, SecondaryButton, SubmitButton } from '../auth/AuthForm.tsx'
|
|
9
|
+
import { AuthLayout } from '../auth/AuthLayout.tsx'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Onboarding step 2: invitations, against `POST /v1/workspaces/:id/invites`.
|
|
13
|
+
*
|
|
14
|
+
* One request per address, and they can fail one at a time — an address already
|
|
15
|
+
* invited answers `409` while its neighbours succeed. The mockup could ignore
|
|
16
|
+
* this because it sent nothing. Here a row carries its own outcome, a sent one
|
|
17
|
+
* is never sent twice, and the step only advances once nothing is outstanding.
|
|
18
|
+
*
|
|
19
|
+
* Skippable, per `onboarding.md`. Admin → Team does the same job later.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
interface InviteRow {
|
|
23
|
+
readonly email: string
|
|
24
|
+
readonly role: InvitableRole
|
|
25
|
+
/** Accepted by the service. A retry of the other rows must not send it again. */
|
|
26
|
+
readonly sent: boolean
|
|
27
|
+
readonly failure: string | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function emptyRow(): InviteRow {
|
|
31
|
+
return { email: '', role: 'member', sent: false, failure: null }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A row worth sending: it has an address and has not already been accepted. */
|
|
35
|
+
function isOutstanding(row: InviteRow): boolean {
|
|
36
|
+
return row.email.trim().length > 0 && !row.sent
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function InvitesStepPage(): React.JSX.Element {
|
|
40
|
+
const navigate = useNavigate()
|
|
41
|
+
const sendInvite = useSendInvite()
|
|
42
|
+
const [rows, setRows] = useState<readonly InviteRow[]>([emptyRow()])
|
|
43
|
+
|
|
44
|
+
function changeRow(index: number, changes: Partial<InviteRow>): void {
|
|
45
|
+
setRows((previous) =>
|
|
46
|
+
previous.map((row, at) => (at === index ? { ...row, ...changes } : row)),
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sends every outstanding row, one at a time, and reports each result on its
|
|
52
|
+
* own row. Sequential rather than concurrent: a person reading a list of
|
|
53
|
+
* failures wants them in the order they typed the addresses.
|
|
54
|
+
*/
|
|
55
|
+
async function sendOutstanding(): Promise<boolean> {
|
|
56
|
+
let allSent = true
|
|
57
|
+
|
|
58
|
+
for (const [index, row] of rows.entries()) {
|
|
59
|
+
if (!isOutstanding(row)) {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await sendInvite.runAsync({ email: row.email.trim(), role: row.role })
|
|
65
|
+
changeRow(index, { sent: true, failure: null })
|
|
66
|
+
} catch (error: unknown) {
|
|
67
|
+
allSent = false
|
|
68
|
+
changeRow(index, {
|
|
69
|
+
failure: error instanceof Error ? error.message : 'Could not send this invitation.',
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return allSent
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function submit(event: FormEvent): void {
|
|
78
|
+
event.preventDefault()
|
|
79
|
+
|
|
80
|
+
sendOutstanding()
|
|
81
|
+
.then((allSent) => {
|
|
82
|
+
if (allSent) {
|
|
83
|
+
navigate('/onboarding/handbook', { replace: true })
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.catch(() => undefined)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const outstanding = rows.filter(isOutstanding).length
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<AuthLayout
|
|
93
|
+
step={2}
|
|
94
|
+
title="Invite teammates"
|
|
95
|
+
description="Optional. You can always invite people later from Admin → Team."
|
|
96
|
+
>
|
|
97
|
+
<form onSubmit={submit} className="mt-5 space-y-3">
|
|
98
|
+
{rows.map((row, index) => (
|
|
99
|
+
<InviteRowFields
|
|
100
|
+
// Rows are only appended, so the position is stable for the life of
|
|
101
|
+
// the page and identifies the row a result belongs to.
|
|
102
|
+
key={index}
|
|
103
|
+
row={row}
|
|
104
|
+
onChange={(changes) => {
|
|
105
|
+
changeRow(index, changes)
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
))}
|
|
109
|
+
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
onClick={() => {
|
|
113
|
+
setRows((previous) => [...previous, emptyRow()])
|
|
114
|
+
}}
|
|
115
|
+
className="text-[12px] font-medium text-accent hover:underline"
|
|
116
|
+
>
|
|
117
|
+
Add another
|
|
118
|
+
</button>
|
|
119
|
+
|
|
120
|
+
<div className="flex flex-col gap-2 pt-1">
|
|
121
|
+
<SubmitButton
|
|
122
|
+
label={outstanding === 0 ? 'Continue' : 'Send invitations'}
|
|
123
|
+
pendingLabel="Sending…"
|
|
124
|
+
isPending={sendInvite.isPending}
|
|
125
|
+
/>
|
|
126
|
+
<SecondaryButton
|
|
127
|
+
label="Skip for now"
|
|
128
|
+
disabled={sendInvite.isPending}
|
|
129
|
+
onClick={() => {
|
|
130
|
+
navigate('/onboarding/handbook', { replace: true })
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
</form>
|
|
135
|
+
</AuthLayout>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function InviteRowFields({
|
|
140
|
+
row,
|
|
141
|
+
onChange,
|
|
142
|
+
}: {
|
|
143
|
+
readonly row: InviteRow
|
|
144
|
+
readonly onChange: (changes: Partial<InviteRow>) => void
|
|
145
|
+
}): React.JSX.Element {
|
|
146
|
+
return (
|
|
147
|
+
<div>
|
|
148
|
+
<div className="flex gap-2">
|
|
149
|
+
<input
|
|
150
|
+
type="email"
|
|
151
|
+
value={row.email}
|
|
152
|
+
onChange={(event) => {
|
|
153
|
+
// Editing an address makes it a different invitation, so a failure
|
|
154
|
+
// recorded against the old one no longer applies.
|
|
155
|
+
onChange({ email: event.target.value, failure: null })
|
|
156
|
+
}}
|
|
157
|
+
disabled={row.sent}
|
|
158
|
+
placeholder="colleague@company.com"
|
|
159
|
+
className={`min-w-0 flex-1 ${AUTH_INPUT_CLASS} disabled:opacity-60`}
|
|
160
|
+
/>
|
|
161
|
+
<select
|
|
162
|
+
value={row.role}
|
|
163
|
+
onChange={(event) => {
|
|
164
|
+
onChange({ role: event.target.value as InvitableRole })
|
|
165
|
+
}}
|
|
166
|
+
disabled={row.sent}
|
|
167
|
+
className="rounded-md border border-border bg-surface px-2 py-2 text-[12px] outline-none focus:border-accent disabled:opacity-60"
|
|
168
|
+
>
|
|
169
|
+
{INVITABLE_ROLES.map((role) => (
|
|
170
|
+
<option key={role} value={role}>
|
|
171
|
+
{role === 'admin' ? 'Admin' : 'Member'}
|
|
172
|
+
</option>
|
|
173
|
+
))}
|
|
174
|
+
</select>
|
|
175
|
+
</div>
|
|
176
|
+
{row.sent && <p className="mt-1 text-[11px] font-medium text-success">Invitation sent</p>}
|
|
177
|
+
{row.failure !== null && <p className="mt-1 text-[11px] text-danger">{row.failure}</p>}
|
|
178
|
+
</div>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { FormEvent } from 'react'
|
|
3
|
+
import { Link, Navigate, useNavigate } from 'react-router'
|
|
4
|
+
|
|
5
|
+
import { useCreateWorkspace, useSession } from '../../api/resources/session.ts'
|
|
6
|
+
import { ErrorPanel } from '../../components/QueryState.tsx'
|
|
7
|
+
import { SubmitButton, TextField } from '../auth/AuthForm.tsx'
|
|
8
|
+
import { AuthLayout } from '../auth/AuthLayout.tsx'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Onboarding step 1: the workspace, against `POST /v1/workspaces`.
|
|
12
|
+
*
|
|
13
|
+
* This is the step the rest of the app waits on. Signup creates an account and
|
|
14
|
+
* no workspace, so until this succeeds every CRM endpoint answers `403`, and
|
|
15
|
+
* `SessionGate` sends anyone in that state here.
|
|
16
|
+
*
|
|
17
|
+
* One request does the whole thing: the service seeds the starter handbook and
|
|
18
|
+
* the pipeline stages in the same transaction and moves this session into the
|
|
19
|
+
* new workspace. There is nothing to re-login for and nothing to create next.
|
|
20
|
+
*
|
|
21
|
+
* It sits outside `SessionGate`, because being sent here *is* what the gate does
|
|
22
|
+
* with an account that has no workspace, so gating it would loop. That leaves
|
|
23
|
+
* the signed-out case to the page: without this check, somebody arriving here
|
|
24
|
+
* signed out fills in a form whose only possible answer is `401`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** The rule the API enforces: lowercase letters, digits, and hyphens. */
|
|
28
|
+
function slugify(name: string): string {
|
|
29
|
+
return name
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9]+/gu, '-')
|
|
32
|
+
.replace(/^-+|-+$/gu, '')
|
|
33
|
+
.slice(0, 63)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The zone the browser is in.
|
|
38
|
+
*
|
|
39
|
+
* The mockup offered a list of five. A guess from the platform is right more
|
|
40
|
+
* often than a five-item list is, and Admin → Workspace is where it gets
|
|
41
|
+
* changed. The value is shown rather than hidden so a wrong guess is visible.
|
|
42
|
+
*/
|
|
43
|
+
function browserTimezone(): string {
|
|
44
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function WorkspaceStepPage(): React.JSX.Element {
|
|
48
|
+
const navigate = useNavigate()
|
|
49
|
+
const { isSignedOut } = useSession()
|
|
50
|
+
const createWorkspace = useCreateWorkspace()
|
|
51
|
+
const [name, setName] = useState('')
|
|
52
|
+
const [slug, setSlug] = useState('')
|
|
53
|
+
const [slugEdited, setSlugEdited] = useState(false)
|
|
54
|
+
const [timezone] = useState(browserTimezone)
|
|
55
|
+
|
|
56
|
+
function changeName(value: string): void {
|
|
57
|
+
setName(value)
|
|
58
|
+
|
|
59
|
+
if (!slugEdited) {
|
|
60
|
+
setSlug(slugify(value))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function changeSlug(value: string): void {
|
|
65
|
+
setSlugEdited(true)
|
|
66
|
+
setSlug(slugify(value))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function submit(event: FormEvent): void {
|
|
70
|
+
event.preventDefault()
|
|
71
|
+
|
|
72
|
+
createWorkspace
|
|
73
|
+
.runAsync({ name: name.trim(), slug: slug.trim(), timezone })
|
|
74
|
+
.then(() => navigate('/onboarding/invites', { replace: true }))
|
|
75
|
+
.catch(() => undefined)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isSignedOut) {
|
|
79
|
+
return <Navigate to="/login" replace />
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<AuthLayout
|
|
84
|
+
step={1}
|
|
85
|
+
title="Create your workspace"
|
|
86
|
+
description="A workspace is your company brain — CRM records, handbook, and team."
|
|
87
|
+
footer={
|
|
88
|
+
<Link to="/login" className="font-medium text-accent hover:underline">
|
|
89
|
+
Sign in as somebody else
|
|
90
|
+
</Link>
|
|
91
|
+
}
|
|
92
|
+
>
|
|
93
|
+
<form onSubmit={submit} className="mt-5 space-y-3">
|
|
94
|
+
<TextField
|
|
95
|
+
label="Workspace name"
|
|
96
|
+
value={name}
|
|
97
|
+
onChange={changeName}
|
|
98
|
+
placeholder="Acme Labs"
|
|
99
|
+
autoComplete="organization"
|
|
100
|
+
required
|
|
101
|
+
/>
|
|
102
|
+
<TextField
|
|
103
|
+
label="Slug"
|
|
104
|
+
value={slug}
|
|
105
|
+
onChange={changeSlug}
|
|
106
|
+
hint="Lowercase letters, digits, and hyphens. It appears in URLs."
|
|
107
|
+
mono
|
|
108
|
+
required
|
|
109
|
+
/>
|
|
110
|
+
<p className="text-[11px] text-ink-faint">
|
|
111
|
+
Timezone: {timezone}. Change it later in Admin → Workspace.
|
|
112
|
+
</p>
|
|
113
|
+
{createWorkspace.error !== null && <ErrorPanel error={createWorkspace.error} />}
|
|
114
|
+
<SubmitButton
|
|
115
|
+
label="Continue"
|
|
116
|
+
pendingLabel="Creating…"
|
|
117
|
+
isPending={createWorkspace.isPending}
|
|
118
|
+
/>
|
|
119
|
+
</form>
|
|
120
|
+
</AuthLayout>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
import { useCompanies } from '../api/resources/companies.ts'
|
|
4
|
+
import { usePositions } from '../api/resources/positions.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Titles and company names for the people on screen, and head counts for the
|
|
8
|
+
* companies on screen.
|
|
9
|
+
*
|
|
10
|
+
* A list row needs data from three resources, and `api.md` has no
|
|
11
|
+
* include-expansion. What it does have is repeatable id filters, so each of
|
|
12
|
+
* these asks about exactly the rows the page is rendering: two extra requests
|
|
13
|
+
* for the page, not one per row, and no cap that a real workspace could grow
|
|
14
|
+
* past.
|
|
15
|
+
*
|
|
16
|
+
* Both hold their request back until the ids are known. Asking with the filter
|
|
17
|
+
* omitted would answer with every record in the workspace, which is the wrong
|
|
18
|
+
* answer rather than a slow one.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** `api.md`: `?limit=` and an id filter both max out at 200. */
|
|
22
|
+
const MAX_PAGE = 200
|
|
23
|
+
|
|
24
|
+
export interface PeopleDirectory {
|
|
25
|
+
/** The first title a person holds, matching the mockup's primary position. */
|
|
26
|
+
titleFor(personId: string): string | undefined
|
|
27
|
+
companyNamesFor(personId: string): readonly string[]
|
|
28
|
+
readonly isLoading: boolean
|
|
29
|
+
/**
|
|
30
|
+
* False when these people hold more positions than one page could return.
|
|
31
|
+
* Rare, and the page says so rather than leaving a cell blank, because a blank
|
|
32
|
+
* that means "we did not look" reads the same as one that means "no company".
|
|
33
|
+
*/
|
|
34
|
+
readonly isComplete: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function usePeopleDirectory(personIds: readonly string[]): PeopleDirectory {
|
|
38
|
+
const enabled = personIds.length > 0
|
|
39
|
+
const positions = usePositions({ personIds, limit: MAX_PAGE }, { enabled })
|
|
40
|
+
const companies = useCompanies({ personIds, limit: MAX_PAGE }, { enabled })
|
|
41
|
+
|
|
42
|
+
const companyNameById = useMemo(
|
|
43
|
+
() => new Map(companies.records.map((company) => [company.id, company.name])),
|
|
44
|
+
[companies.records],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const byPerson = useMemo(() => {
|
|
48
|
+
const grouped = new Map<string, { title: string; companyId: string }[]>()
|
|
49
|
+
|
|
50
|
+
for (const position of positions.records) {
|
|
51
|
+
const held = grouped.get(position.personId) ?? []
|
|
52
|
+
|
|
53
|
+
held.push({ title: position.title, companyId: position.companyId })
|
|
54
|
+
grouped.set(position.personId, held)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return grouped
|
|
58
|
+
}, [positions.records])
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
titleFor: (personId) => byPerson.get(personId)?.[0]?.title,
|
|
62
|
+
companyNamesFor: (personId) =>
|
|
63
|
+
(byPerson.get(personId) ?? [])
|
|
64
|
+
.map((held) => companyNameById.get(held.companyId))
|
|
65
|
+
.filter((name): name is string => name !== undefined),
|
|
66
|
+
isLoading: positions.isLoading || companies.isLoading,
|
|
67
|
+
isComplete: !positions.hasMore && !companies.hasMore,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface CompanyHeadcounts {
|
|
72
|
+
countFor(companyId: string): number
|
|
73
|
+
readonly isLoading: boolean
|
|
74
|
+
readonly isComplete: boolean
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function useCompanyHeadcounts(companyIds: readonly string[]): CompanyHeadcounts {
|
|
78
|
+
const positions = usePositions(
|
|
79
|
+
{ companyIds, limit: MAX_PAGE },
|
|
80
|
+
{ enabled: companyIds.length > 0 },
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
const countByCompany = useMemo(() => {
|
|
84
|
+
const counts = new Map<string, number>()
|
|
85
|
+
|
|
86
|
+
for (const position of positions.records) {
|
|
87
|
+
counts.set(position.companyId, (counts.get(position.companyId) ?? 0) + 1)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return counts
|
|
91
|
+
}, [positions.records])
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
countFor: (companyId) => countByCompany.get(companyId) ?? 0,
|
|
95
|
+
isLoading: positions.isLoading,
|
|
96
|
+
isComplete: !positions.hasMore,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { UiExtensionContext } from './context.ts'
|
|
4
|
+
import type { UiExtensions } from './registry.ts'
|
|
5
|
+
|
|
6
|
+
export interface UiExtensionProviderProps {
|
|
7
|
+
readonly extensions: UiExtensions
|
|
8
|
+
readonly children: ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Puts a built registry in reach of the tree below it. The assembly wraps its
|
|
13
|
+
* root in one of these; nothing else should need to.
|
|
14
|
+
*/
|
|
15
|
+
export function UiExtensionProvider({
|
|
16
|
+
extensions,
|
|
17
|
+
children,
|
|
18
|
+
}: UiExtensionProviderProps): React.JSX.Element {
|
|
19
|
+
return <UiExtensionContext value={extensions}>{children}</UiExtensionContext>
|
|
20
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
import type { ComponentType } from 'react'
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
DashboardCard,
|
|
6
|
+
ExtensibleRecordType,
|
|
7
|
+
IntegrationProvider,
|
|
8
|
+
NavItem,
|
|
9
|
+
NavSlot,
|
|
10
|
+
RecordSidebarCard,
|
|
11
|
+
RecordTab,
|
|
12
|
+
RouteContribution,
|
|
13
|
+
} from './contributions.ts'
|
|
14
|
+
import type { Overridable } from './overridable.ts'
|
|
15
|
+
import { NO_UI_MODULES } from './registry.ts'
|
|
16
|
+
import type { UiExtensions } from './registry.ts'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reading the registry from a component.
|
|
20
|
+
*
|
|
21
|
+
* The default is the empty registry rather than an error, so a component tree
|
|
22
|
+
* without a provider behaves exactly like an assembly with no modules: every
|
|
23
|
+
* slot empty, every overridable rendering core's own component. A page under
|
|
24
|
+
* test needs no setup to look right.
|
|
25
|
+
*
|
|
26
|
+
* The provider lives in its own file so Fast Refresh keeps working; a module
|
|
27
|
+
* that exports both a component and a hook loses it.
|
|
28
|
+
*/
|
|
29
|
+
export const UiExtensionContext = createContext<UiExtensions>(NO_UI_MODULES)
|
|
30
|
+
|
|
31
|
+
export function useUiExtensions(): UiExtensions {
|
|
32
|
+
return useContext(UiExtensionContext)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Module nav items for one slot, in order. The shell merges its own items in. */
|
|
36
|
+
export function useNavItems(slot: NavSlot): readonly NavItem[] {
|
|
37
|
+
return useUiExtensions().navItems(slot)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Whole pages modules contribute, mounted under the shell's chrome. */
|
|
41
|
+
export function useModuleRoutes(): readonly RouteContribution[] {
|
|
42
|
+
return useUiExtensions().routes()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function useRecordTabs(objectType: ExtensibleRecordType): readonly RecordTab[] {
|
|
46
|
+
return useUiExtensions().recordTabs(objectType)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function useRecordSidebarCards(objectType: ExtensibleRecordType): readonly RecordSidebarCard[] {
|
|
50
|
+
return useUiExtensions().recordSidebarCards(objectType)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useDashboardCards(): readonly DashboardCard[] {
|
|
54
|
+
return useUiExtensions().dashboardCards()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useIntegrationProviders(): readonly IntegrationProvider[] {
|
|
58
|
+
return useUiExtensions().integrationProviders()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The component to render for an overridable: a module's replacement, or core's
|
|
63
|
+
* own. Core calls this instead of rendering its component directly.
|
|
64
|
+
*/
|
|
65
|
+
export function useOverridable<Props>(token: Overridable<Props>): ComponentType<Props> {
|
|
66
|
+
return useUiExtensions().componentFor(token)
|
|
67
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ExtensibleRecordType } from '@kelpie/schemas'
|
|
2
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What a UI module may add to the shell, per the slot table in `modules.md`.
|
|
6
|
+
*
|
|
7
|
+
* Every slot renders nothing when empty. A core page has to look finished with
|
|
8
|
+
* no modules loaded at all, because the open-source assembly is exactly that.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const NAV_SLOTS = ['primary', 'admin', 'account'] as const
|
|
12
|
+
|
|
13
|
+
export type NavSlot = (typeof NAV_SLOTS)[number]
|
|
14
|
+
|
|
15
|
+
export interface NavItem {
|
|
16
|
+
readonly id: string
|
|
17
|
+
readonly label: string
|
|
18
|
+
/** Route path, the same string the shell's router matches. */
|
|
19
|
+
readonly to: string
|
|
20
|
+
/**
|
|
21
|
+
* Where it sits among the core items. Core numbers itself in hundreds, so a
|
|
22
|
+
* module can land between two of them without core renumbering.
|
|
23
|
+
*/
|
|
24
|
+
readonly order?: number
|
|
25
|
+
readonly icon?: ReactNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A whole page, mounted under the shell's chrome. */
|
|
29
|
+
export interface RouteContribution {
|
|
30
|
+
readonly path: string
|
|
31
|
+
readonly element: ReactNode
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The record types a detail page exists for, and therefore the ones a module can
|
|
36
|
+
* add a tab or a sidebar card to.
|
|
37
|
+
*
|
|
38
|
+
* Now in `@kelpie/schemas`, which is the shared-schema package this file's
|
|
39
|
+
* previous note was waiting for. Re-exported rather than moved outright: it is
|
|
40
|
+
* part of the contribution vocabulary, and a module author reading this file
|
|
41
|
+
* should not have to follow an import to learn what a record type is.
|
|
42
|
+
*/
|
|
43
|
+
export { EXTENSIBLE_RECORD_TYPES } from '@kelpie/schemas'
|
|
44
|
+
export type { ExtensibleRecordType }
|
|
45
|
+
|
|
46
|
+
/** What a tab or card is rendering against. Ids, not records: the contributor fetches its own data. */
|
|
47
|
+
export interface RecordContext {
|
|
48
|
+
readonly objectType: ExtensibleRecordType
|
|
49
|
+
readonly recordId: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface RecordTab {
|
|
53
|
+
readonly id: string
|
|
54
|
+
readonly label: string
|
|
55
|
+
readonly order?: number
|
|
56
|
+
readonly render: (context: RecordContext) => ReactNode
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface RecordSidebarCard {
|
|
60
|
+
readonly id: string
|
|
61
|
+
readonly order?: number
|
|
62
|
+
readonly render: (context: RecordContext) => ReactNode
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface DashboardCard {
|
|
66
|
+
readonly id: string
|
|
67
|
+
readonly order?: number
|
|
68
|
+
readonly render: () => ReactNode
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const INTEGRATION_CATEGORIES = [
|
|
72
|
+
'messaging',
|
|
73
|
+
'identity',
|
|
74
|
+
'enrichment',
|
|
75
|
+
'email',
|
|
76
|
+
'calendar',
|
|
77
|
+
] as const
|
|
78
|
+
|
|
79
|
+
export type IntegrationCategory = (typeof INTEGRATION_CATEGORIES)[number]
|
|
80
|
+
|
|
81
|
+
/** A catalog entry on the admin integrations page. Core owns the page and the connection lifecycle. */
|
|
82
|
+
export interface IntegrationProvider {
|
|
83
|
+
readonly id: string
|
|
84
|
+
readonly name: string
|
|
85
|
+
readonly category: IntegrationCategory
|
|
86
|
+
readonly description: string
|
|
87
|
+
/** Rendered on the provider's own settings panel once connected. */
|
|
88
|
+
readonly settings?: ComponentType<Record<string, never>>
|
|
89
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The override escape hatch from `modules.md`: a module replaces a core
|
|
5
|
+
* component outright. Prefer a slot; this is for the cases a slot cannot reach.
|
|
6
|
+
*
|
|
7
|
+
* A core component is replaceable only if it hands out a token, and the token
|
|
8
|
+
* carries its props type. So an override that does not accept the props core
|
|
9
|
+
* passes is a compile error, and a key nothing declared cannot be overridden at
|
|
10
|
+
* all: there is no token to name it with.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface Overridable<Props> {
|
|
14
|
+
readonly key: string
|
|
15
|
+
/** What renders when no module overrides it. Core's own component. */
|
|
16
|
+
readonly fallback: ComponentType<Props>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param key Stable across releases. It is the name a module writes down.
|
|
21
|
+
*/
|
|
22
|
+
export function defineOverridable<Props>(
|
|
23
|
+
key: string,
|
|
24
|
+
fallback: ComponentType<Props>,
|
|
25
|
+
): Overridable<Props> {
|
|
26
|
+
return { key, fallback }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface OverrideStore {
|
|
30
|
+
set<Props>(token: Overridable<Props>, component: ComponentType<Props>): void
|
|
31
|
+
/** @returns The override, or the token's own fallback when no module replaced it. */
|
|
32
|
+
get<Props>(token: Overridable<Props>): ComponentType<Props>
|
|
33
|
+
has(key: string): boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createOverrideStore(): OverrideStore {
|
|
37
|
+
// One map holding components with different props types, so the value type is
|
|
38
|
+
// the only one they share. `ComponentType<never>` looks like the tighter
|
|
39
|
+
// choice and is not: `ComponentClass.defaultProps` is covariant in Props, so
|
|
40
|
+
// nothing is assignable to it.
|
|
41
|
+
const byKey = new Map<string, unknown>()
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
set<Props>(token: Overridable<Props>, component: ComponentType<Props>): void {
|
|
45
|
+
byKey.set(token.key, component)
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
get<Props>(token: Overridable<Props>): ComponentType<Props> {
|
|
49
|
+
const override = byKey.get(token.key)
|
|
50
|
+
|
|
51
|
+
if (override === undefined) {
|
|
52
|
+
return token.fallback
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sound by construction: the only writer is `set`, which accepts nothing
|
|
56
|
+
// but a component for this token's own props.
|
|
57
|
+
return override as ComponentType<Props>
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
has: (key) => byKey.has(key),
|
|
61
|
+
}
|
|
62
|
+
}
|