@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,412 @@
|
|
|
1
|
+
import {
|
|
2
|
+
keepPreviousData,
|
|
3
|
+
useInfiniteQuery,
|
|
4
|
+
useMutation,
|
|
5
|
+
useQuery,
|
|
6
|
+
useQueryClient,
|
|
7
|
+
} from '@tanstack/react-query'
|
|
8
|
+
import type { InfiniteData, QueryClient } from '@tanstack/react-query'
|
|
9
|
+
|
|
10
|
+
import { ApiError } from './client.ts'
|
|
11
|
+
import type { Decoder, Page, QueryParameters } from './client.ts'
|
|
12
|
+
import { useApiClient } from './context.ts'
|
|
13
|
+
import { toError } from './errors.ts'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The read and write hooks for one `/v1` collection.
|
|
17
|
+
*
|
|
18
|
+
* Every CRM resource is the same five operations over the same list envelope,
|
|
19
|
+
* so they are built once here and configured per resource. A resource module
|
|
20
|
+
* supplies its path, its decoder, and its two body builders; it writes no cache
|
|
21
|
+
* handling of its own.
|
|
22
|
+
*
|
|
23
|
+
* Pages see the three result types below and never import from
|
|
24
|
+
* `@tanstack/react-query` directly. That is deliberate: it keeps page code short
|
|
25
|
+
* and leaves the cache library replaceable without touching a page.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** A list, one page at a time. Nothing is truncated in silence: `hasMore` says so. */
|
|
29
|
+
export interface RecordListResult<TRecord> {
|
|
30
|
+
readonly records: readonly TRecord[]
|
|
31
|
+
readonly isLoading: boolean
|
|
32
|
+
readonly error: Error | null
|
|
33
|
+
readonly hasMore: boolean
|
|
34
|
+
readonly isLoadingMore: boolean
|
|
35
|
+
readonly loadMore: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RecordResult<TRecord> {
|
|
39
|
+
readonly record: TRecord | undefined
|
|
40
|
+
readonly isLoading: boolean
|
|
41
|
+
readonly error: Error | null
|
|
42
|
+
/** The record is not in this workspace, which `api.md` makes indistinguishable from not existing. */
|
|
43
|
+
readonly isNotFound: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface MutationResult<TInput, TOutput> {
|
|
47
|
+
/** Fire and forget. A failure lands in `error` rather than in an unhandled rejection. */
|
|
48
|
+
readonly run: (input: TInput) => void
|
|
49
|
+
/** Await the result, for a caller that navigates or chains on success. Rejects on failure. */
|
|
50
|
+
readonly runAsync: (input: TInput) => Promise<TOutput>
|
|
51
|
+
readonly isPending: boolean
|
|
52
|
+
readonly error: Error | null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface UpdateArguments<TUpdateInput> {
|
|
56
|
+
readonly id: string
|
|
57
|
+
readonly changes: TUpdateInput
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface PatchResult<TChanges> {
|
|
61
|
+
/** Fire and forget, bound to one record's id. A failure lands in `error`. */
|
|
62
|
+
readonly patch: (changes: TChanges) => void
|
|
63
|
+
readonly error: Error | null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ListOptions {
|
|
67
|
+
/**
|
|
68
|
+
* Set false to hold the request back. A list filtered by a set of ids has
|
|
69
|
+
* nothing to ask until those ids are known, and asking with the filter omitted
|
|
70
|
+
* would answer with every record instead of none.
|
|
71
|
+
*/
|
|
72
|
+
readonly enabled?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ResourceHooks<TRecord, TCreateInput, TUpdateInput>
|
|
76
|
+
extends ReadOnlyResourceHooks<TRecord> {
|
|
77
|
+
useCreate(): MutationResult<TCreateInput, TRecord>
|
|
78
|
+
useUpdate(): MutationResult<UpdateArguments<TUpdateInput>, TRecord>
|
|
79
|
+
useRemove(): MutationResult<string, void>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** What a collection with no write verbs offers. Activities are the case: the table is append-only. */
|
|
83
|
+
export interface ReadOnlyResourceHooks<TRecord> {
|
|
84
|
+
useList(query?: QueryParameters, options?: ListOptions): RecordListResult<TRecord>
|
|
85
|
+
useRecord(id: string | undefined): RecordResult<TRecord>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ReadOnlyResourceDefinition<TRecord> {
|
|
89
|
+
/** Cache key root. The plural resource name, e.g. `activities`. */
|
|
90
|
+
readonly name: string
|
|
91
|
+
/** Path under the API base, e.g. `/activities`. */
|
|
92
|
+
readonly path: string
|
|
93
|
+
readonly decode: Decoder<TRecord>
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ResourceDefinition<TRecord, TCreateInput, TUpdateInput>
|
|
97
|
+
extends ReadOnlyResourceDefinition<TRecord> {
|
|
98
|
+
readonly createBody: (input: TCreateInput) => unknown
|
|
99
|
+
readonly updateBody: (input: TUpdateInput) => unknown
|
|
100
|
+
/**
|
|
101
|
+
* Other resources whose cached lists a write here can invalidate.
|
|
102
|
+
*
|
|
103
|
+
* A join record is the case that needs it. `GET /v1/people?company_id=` is a
|
|
104
|
+
* list of people, so creating a Position changes its contents without
|
|
105
|
+
* touching a single person, and a company page that only refetched positions
|
|
106
|
+
* would render the new row against a name it never fetched.
|
|
107
|
+
*/
|
|
108
|
+
readonly alsoInvalidates?: readonly string[]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type ListData<TRecord> = InfiniteData<Page<TRecord>, string | null>
|
|
112
|
+
|
|
113
|
+
interface ResourceKeys {
|
|
114
|
+
readonly all: readonly unknown[]
|
|
115
|
+
readonly lists: readonly unknown[]
|
|
116
|
+
list(query: QueryParameters): readonly unknown[]
|
|
117
|
+
detail(id: string): readonly unknown[]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function keysFor(name: string): ResourceKeys {
|
|
121
|
+
return {
|
|
122
|
+
all: [name],
|
|
123
|
+
lists: [name, 'list'],
|
|
124
|
+
list: (query) => [name, 'list', query],
|
|
125
|
+
detail: (id) => [name, 'detail', id],
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Applies a partial update to a record.
|
|
131
|
+
*
|
|
132
|
+
* `undefined` means "not sent" and `null` means "clear this field", per
|
|
133
|
+
* `api.md`, so an undefined value must leave the existing one alone rather than
|
|
134
|
+
* overwrite it.
|
|
135
|
+
*/
|
|
136
|
+
function mergeDefined<TRecord extends object>(record: TRecord, changes: Partial<TRecord>): TRecord {
|
|
137
|
+
const merged = { ...record }
|
|
138
|
+
|
|
139
|
+
for (const [key, value] of Object.entries(changes)) {
|
|
140
|
+
if (value !== undefined) {
|
|
141
|
+
Object.assign(merged, { [key]: value })
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return merged
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Rewrites one record everywhere it is cached: its own entry, and every list page holding it. */
|
|
149
|
+
function writeRecordEverywhere<TRecord extends { readonly id: string }>(
|
|
150
|
+
queryClient: QueryClient,
|
|
151
|
+
keys: ResourceKeys,
|
|
152
|
+
record: TRecord,
|
|
153
|
+
): void {
|
|
154
|
+
queryClient.setQueryData(keys.detail(record.id), record)
|
|
155
|
+
queryClient.setQueriesData<ListData<TRecord>>({ queryKey: keys.lists }, (data) =>
|
|
156
|
+
data === undefined
|
|
157
|
+
? data
|
|
158
|
+
: {
|
|
159
|
+
...data,
|
|
160
|
+
pages: data.pages.map((page) => ({
|
|
161
|
+
...page,
|
|
162
|
+
items: page.items.map((item) => (item.id === record.id ? record : item)),
|
|
163
|
+
})),
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function removeRecordEverywhere<TRecord extends { readonly id: string }>(
|
|
169
|
+
queryClient: QueryClient,
|
|
170
|
+
keys: ResourceKeys,
|
|
171
|
+
id: string,
|
|
172
|
+
): void {
|
|
173
|
+
queryClient.removeQueries({ queryKey: keys.detail(id) })
|
|
174
|
+
queryClient.setQueriesData<ListData<TRecord>>({ queryKey: keys.lists }, (data) =>
|
|
175
|
+
data === undefined
|
|
176
|
+
? data
|
|
177
|
+
: {
|
|
178
|
+
...data,
|
|
179
|
+
pages: data.pages.map((page) => ({
|
|
180
|
+
...page,
|
|
181
|
+
items: page.items.filter((item) => item.id !== id),
|
|
182
|
+
})),
|
|
183
|
+
},
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The read half: `useList` and `useRecord`, and nothing that writes.
|
|
189
|
+
*
|
|
190
|
+
* Split out because a collection can be read-only. `createResourceHooks` builds
|
|
191
|
+
* on this rather than repeating it, so both kinds of resource page, cache, and
|
|
192
|
+
* report a 404 identically.
|
|
193
|
+
*/
|
|
194
|
+
export function createReadOnlyResourceHooks<TRecord>(
|
|
195
|
+
definition: ReadOnlyResourceDefinition<TRecord>,
|
|
196
|
+
): ReadOnlyResourceHooks<TRecord> {
|
|
197
|
+
const keys = keysFor(definition.name)
|
|
198
|
+
|
|
199
|
+
function useList(query: QueryParameters = {}, options: ListOptions = {}): RecordListResult<TRecord> {
|
|
200
|
+
const client = useApiClient()
|
|
201
|
+
const enabled = options.enabled ?? true
|
|
202
|
+
const result = useInfiniteQuery({
|
|
203
|
+
queryKey: keys.list(query),
|
|
204
|
+
queryFn: ({ pageParam }) =>
|
|
205
|
+
client.list(definition.path, definition.decode, {
|
|
206
|
+
...query,
|
|
207
|
+
...(pageParam === null ? {} : { cursor: pageParam }),
|
|
208
|
+
}),
|
|
209
|
+
initialPageParam: null as string | null,
|
|
210
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
211
|
+
// Without this a keystroke in the filter box blanks the table while the
|
|
212
|
+
// next query runs, which reads as "no results" for as long as it takes.
|
|
213
|
+
placeholderData: keepPreviousData,
|
|
214
|
+
enabled,
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
records: result.data?.pages.flatMap((page) => page.items) ?? [],
|
|
219
|
+
// A disabled query stays pending forever, which is not the same as loading.
|
|
220
|
+
isLoading: result.isPending && enabled,
|
|
221
|
+
error: toError(result.error),
|
|
222
|
+
hasMore: result.hasNextPage,
|
|
223
|
+
isLoadingMore: result.isFetchingNextPage,
|
|
224
|
+
loadMore: () => {
|
|
225
|
+
void result.fetchNextPage()
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function useRecord(id: string | undefined): RecordResult<TRecord> {
|
|
231
|
+
const client = useApiClient()
|
|
232
|
+
const result = useQuery({
|
|
233
|
+
queryKey: keys.detail(id ?? ''),
|
|
234
|
+
queryFn: () => client.get(`${definition.path}/${id ?? ''}`, definition.decode),
|
|
235
|
+
enabled: id !== undefined,
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
record: result.data,
|
|
240
|
+
isLoading: result.isPending && id !== undefined,
|
|
241
|
+
error: toError(result.error),
|
|
242
|
+
isNotFound: result.error instanceof ApiError && result.error.status === 404,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return { useList, useRecord }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function createResourceHooks<
|
|
250
|
+
TRecord extends { readonly id: string },
|
|
251
|
+
TCreateInput,
|
|
252
|
+
TUpdateInput extends Partial<TRecord>,
|
|
253
|
+
>(
|
|
254
|
+
definition: ResourceDefinition<TRecord, TCreateInput, TUpdateInput>,
|
|
255
|
+
): ResourceHooks<TRecord, TCreateInput, TUpdateInput> {
|
|
256
|
+
const keys = keysFor(definition.name)
|
|
257
|
+
const related = (definition.alsoInvalidates ?? []).map(keysFor)
|
|
258
|
+
const { useList, useRecord } = createReadOnlyResourceHooks(definition)
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Marks this resource's lists stale, and the related resources' with them.
|
|
262
|
+
* Applied to every write rather than only to create and delete: a resource
|
|
263
|
+
* that declares a relation has one, whichever verb reaches it.
|
|
264
|
+
*/
|
|
265
|
+
function invalidateLists(queryClient: QueryClient): void {
|
|
266
|
+
for (const target of [keys, ...related]) {
|
|
267
|
+
void queryClient.invalidateQueries({ queryKey: target.lists })
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function useCreate(): MutationResult<TCreateInput, TRecord> {
|
|
272
|
+
const client = useApiClient()
|
|
273
|
+
const queryClient = useQueryClient()
|
|
274
|
+
const mutation = useMutation({
|
|
275
|
+
mutationFn: (input: TCreateInput) =>
|
|
276
|
+
client.post(definition.path, definition.createBody(input), definition.decode),
|
|
277
|
+
// No optimistic insert: the id comes from the server, and the caller
|
|
278
|
+
// usually navigates straight to the new record. A placeholder id would be
|
|
279
|
+
// a route to a record that does not exist yet.
|
|
280
|
+
onSuccess: (record) => {
|
|
281
|
+
queryClient.setQueryData(keys.detail(record.id), record)
|
|
282
|
+
},
|
|
283
|
+
onSettled: () => {
|
|
284
|
+
invalidateLists(queryClient)
|
|
285
|
+
},
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
run: (input) => {
|
|
290
|
+
mutation.mutate(input)
|
|
291
|
+
},
|
|
292
|
+
runAsync: (input) => mutation.mutateAsync(input),
|
|
293
|
+
isPending: mutation.isPending,
|
|
294
|
+
error: toError(mutation.error),
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function useUpdate(): MutationResult<UpdateArguments<TUpdateInput>, TRecord> {
|
|
299
|
+
const client = useApiClient()
|
|
300
|
+
const queryClient = useQueryClient()
|
|
301
|
+
const mutation = useMutation({
|
|
302
|
+
mutationFn: ({ id, changes }: UpdateArguments<TUpdateInput>) =>
|
|
303
|
+
client.patch(`${definition.path}/${id}`, definition.updateBody(changes), definition.decode),
|
|
304
|
+
|
|
305
|
+
// Inline editing is the whole interaction on a detail page, so the field
|
|
306
|
+
// has to settle the moment it is committed. The snapshot is what makes
|
|
307
|
+
// that safe: a rejected PATCH puts the old value back.
|
|
308
|
+
onMutate: async ({ id, changes }) => {
|
|
309
|
+
await queryClient.cancelQueries({ queryKey: keys.detail(id) })
|
|
310
|
+
const previous = queryClient.getQueryData<TRecord>(keys.detail(id))
|
|
311
|
+
|
|
312
|
+
if (previous !== undefined) {
|
|
313
|
+
writeRecordEverywhere(queryClient, keys, mergeDefined(previous, changes))
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { previous }
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
onError: (_error, _arguments_, context) => {
|
|
320
|
+
if (context?.previous !== undefined) {
|
|
321
|
+
writeRecordEverywhere(queryClient, keys, context.previous)
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
onSuccess: (record) => {
|
|
326
|
+
writeRecordEverywhere(queryClient, keys, record)
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
onSettled: (_record, _error, { id }) => {
|
|
330
|
+
void queryClient.invalidateQueries({ queryKey: keys.detail(id) })
|
|
331
|
+
invalidateLists(queryClient)
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
run: (input) => {
|
|
337
|
+
mutation.mutate(input)
|
|
338
|
+
},
|
|
339
|
+
runAsync: (input) => mutation.mutateAsync(input),
|
|
340
|
+
isPending: mutation.isPending,
|
|
341
|
+
error: toError(mutation.error),
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function useRemove(): MutationResult<string, void> {
|
|
346
|
+
const client = useApiClient()
|
|
347
|
+
const queryClient = useQueryClient()
|
|
348
|
+
const mutation = useMutation({
|
|
349
|
+
mutationFn: (id: string) => client.delete(`${definition.path}/${id}`),
|
|
350
|
+
|
|
351
|
+
onMutate: async (id) => {
|
|
352
|
+
await queryClient.cancelQueries({ queryKey: keys.all })
|
|
353
|
+
const previous = queryClient.getQueryData<TRecord>(keys.detail(id))
|
|
354
|
+
const previousLists = queryClient.getQueriesData<ListData<TRecord>>({ queryKey: keys.lists })
|
|
355
|
+
|
|
356
|
+
removeRecordEverywhere<TRecord>(queryClient, keys, id)
|
|
357
|
+
|
|
358
|
+
return { previous, previousLists }
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
onError: (_error, id, context) => {
|
|
362
|
+
for (const [key, data] of context?.previousLists ?? []) {
|
|
363
|
+
queryClient.setQueryData(key, data)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (context?.previous !== undefined) {
|
|
367
|
+
queryClient.setQueryData(keys.detail(id), context.previous)
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
onSettled: () => {
|
|
372
|
+
invalidateLists(queryClient)
|
|
373
|
+
},
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
run: (id) => {
|
|
378
|
+
mutation.mutate(id)
|
|
379
|
+
},
|
|
380
|
+
runAsync: (id) => mutation.mutateAsync(id),
|
|
381
|
+
isPending: mutation.isPending,
|
|
382
|
+
error: toError(mutation.error),
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return { useList, useRecord, useCreate, useUpdate, useRemove }
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Binds a resource's `useUpdate` hook to one record's id, for a detail page's
|
|
391
|
+
* inline edits.
|
|
392
|
+
*
|
|
393
|
+
* Every detail page had its own three-line `use<X>Patch` doing exactly this,
|
|
394
|
+
* and every one of them discarded `update.error`. `InlineEdit`'s `onChange` has
|
|
395
|
+
* no return value to reject, so a failed `PATCH` — a duplicate email or domain,
|
|
396
|
+
* a dropped connection — had nowhere left to surface: the field just reverted
|
|
397
|
+
* to its old value with no explanation. Callers now get the error back and
|
|
398
|
+
* decide where to show it.
|
|
399
|
+
*/
|
|
400
|
+
export function usePatch<TRecord extends { readonly id: string }, TChanges>(
|
|
401
|
+
useUpdate: () => MutationResult<UpdateArguments<TChanges>, TRecord>,
|
|
402
|
+
record: TRecord,
|
|
403
|
+
): PatchResult<TChanges> {
|
|
404
|
+
const update = useUpdate()
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
patch: (changes) => {
|
|
408
|
+
update.run({ id: record.id, changes })
|
|
409
|
+
},
|
|
410
|
+
error: update.error,
|
|
411
|
+
}
|
|
412
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import {
|
|
2
|
+
accountPreferencesSchema,
|
|
3
|
+
accountSchema,
|
|
4
|
+
accountSessionSchema,
|
|
5
|
+
changePasswordBody,
|
|
6
|
+
updateAccountBody,
|
|
7
|
+
updateAccountPreferencesBody,
|
|
8
|
+
} from '@kelpie/schemas'
|
|
9
|
+
import type {
|
|
10
|
+
Account,
|
|
11
|
+
AccountPreferences,
|
|
12
|
+
AccountSession,
|
|
13
|
+
ChangePasswordInput,
|
|
14
|
+
ThemePreference,
|
|
15
|
+
UpdateAccountInput,
|
|
16
|
+
UpdateAccountPreferencesInput,
|
|
17
|
+
} from '@kelpie/schemas'
|
|
18
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
19
|
+
import { useEffect, useState } from 'react'
|
|
20
|
+
|
|
21
|
+
import { applyTheme, getStoredTheme, setStoredTheme, watchSystemTheme } from '../../lib/theme.ts'
|
|
22
|
+
import { useApiClient } from '../context.ts'
|
|
23
|
+
import { toError } from '../errors.ts'
|
|
24
|
+
import type { MutationResult } from '../resource.ts'
|
|
25
|
+
import { asMutationResult } from './mutation.ts'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The signed-in person, as the `/account/*` pages read and write them.
|
|
29
|
+
*
|
|
30
|
+
* Not CRM resources, so none of these go through `createResourceHooks`: there is
|
|
31
|
+
* no collection and no id in a path. They are also not workspace-scoped, which
|
|
32
|
+
* is why nothing here keys on the session's workspace: renaming an account
|
|
33
|
+
* renames it everywhere it is a member.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const ACCOUNT_KEY = ['account'] as const
|
|
37
|
+
const PREFERENCES_KEY = ['account', 'preferences'] as const
|
|
38
|
+
const SESSIONS_KEY = ['account', 'sessions'] as const
|
|
39
|
+
|
|
40
|
+
export interface AccountState {
|
|
41
|
+
readonly account: Account | undefined
|
|
42
|
+
readonly isLoading: boolean
|
|
43
|
+
readonly error: Error | null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function useAccount(): AccountState {
|
|
47
|
+
const client = useApiClient()
|
|
48
|
+
const result = useQuery({
|
|
49
|
+
queryKey: ACCOUNT_KEY,
|
|
50
|
+
queryFn: () => client.get('/account', accountSchema.parse),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return { account: result.data, isLoading: result.isPending, error: toError(result.error) }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function useUpdateAccount(): MutationResult<UpdateAccountInput, Account> {
|
|
57
|
+
const client = useApiClient()
|
|
58
|
+
const queryClient = useQueryClient()
|
|
59
|
+
const mutation = useMutation({
|
|
60
|
+
mutationFn: (input: UpdateAccountInput) =>
|
|
61
|
+
client.patch('/account', updateAccountBody(input), accountSchema.parse),
|
|
62
|
+
onSuccess: async (account) => {
|
|
63
|
+
queryClient.setQueryData(ACCOUNT_KEY, account)
|
|
64
|
+
// The team page lists this person by name and address out of the member
|
|
65
|
+
// list, which the account write knows nothing about.
|
|
66
|
+
await queryClient.invalidateQueries({ queryKey: ['members'] })
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return asMutationResult(mutation)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface AccountPreferencesState {
|
|
74
|
+
readonly preferences: AccountPreferences | undefined
|
|
75
|
+
readonly isLoading: boolean
|
|
76
|
+
readonly error: Error | null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useAccountPreferences(): AccountPreferencesState {
|
|
80
|
+
const client = useApiClient()
|
|
81
|
+
const result = useQuery({
|
|
82
|
+
queryKey: PREFERENCES_KEY,
|
|
83
|
+
queryFn: () => client.get('/account/preferences', accountPreferencesSchema.parse),
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return { preferences: result.data, isLoading: result.isPending, error: toError(result.error) }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function useUpdateAccountPreferences(): MutationResult<
|
|
90
|
+
UpdateAccountPreferencesInput,
|
|
91
|
+
AccountPreferences
|
|
92
|
+
> {
|
|
93
|
+
const client = useApiClient()
|
|
94
|
+
const queryClient = useQueryClient()
|
|
95
|
+
const mutation = useMutation({
|
|
96
|
+
mutationFn: (input: UpdateAccountPreferencesInput) =>
|
|
97
|
+
client.patch(
|
|
98
|
+
'/account/preferences',
|
|
99
|
+
updateAccountPreferencesBody(input),
|
|
100
|
+
accountPreferencesSchema.parse,
|
|
101
|
+
),
|
|
102
|
+
onSuccess: (preferences) => {
|
|
103
|
+
queryClient.setQueryData(PREFERENCES_KEY, preferences)
|
|
104
|
+
},
|
|
105
|
+
// A failed write leaves an optimistic theme on screen that the account does
|
|
106
|
+
// not hold. Ask the service what is true rather than guessing back.
|
|
107
|
+
onError: async () => {
|
|
108
|
+
await queryClient.invalidateQueries({ queryKey: PREFERENCES_KEY })
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return asMutationResult(mutation)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface AccountSessionsState {
|
|
116
|
+
readonly sessions: readonly AccountSession[]
|
|
117
|
+
readonly isLoading: boolean
|
|
118
|
+
readonly error: Error | null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Where this account is signed in. The caller's own session is marked `current`. */
|
|
122
|
+
export function useAccountSessions(): AccountSessionsState {
|
|
123
|
+
const client = useApiClient()
|
|
124
|
+
const result = useQuery({
|
|
125
|
+
queryKey: SESSIONS_KEY,
|
|
126
|
+
queryFn: () => client.list('/auth/sessions', accountSessionSchema.parse),
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
sessions: result.data?.items ?? [],
|
|
131
|
+
isLoading: result.isPending,
|
|
132
|
+
error: toError(result.error),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function useRevokeAccountSession(): MutationResult<string, void> {
|
|
137
|
+
const client = useApiClient()
|
|
138
|
+
const queryClient = useQueryClient()
|
|
139
|
+
const mutation = useMutation({
|
|
140
|
+
mutationFn: (sessionId: string) => client.delete(`/auth/sessions/${sessionId}`),
|
|
141
|
+
onSuccess: async () => {
|
|
142
|
+
await queryClient.invalidateQueries({ queryKey: SESSIONS_KEY })
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
return asMutationResult(mutation)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Changes the password and ends every other session, which is what the service
|
|
151
|
+
* does, so the session list on the same page is refetched rather than left
|
|
152
|
+
* showing devices that are no longer signed in.
|
|
153
|
+
*/
|
|
154
|
+
export function useChangePassword(): MutationResult<ChangePasswordInput, void> {
|
|
155
|
+
const client = useApiClient()
|
|
156
|
+
const queryClient = useQueryClient()
|
|
157
|
+
const mutation = useMutation({
|
|
158
|
+
mutationFn: (input: ChangePasswordInput) =>
|
|
159
|
+
client.patchEmpty('/auth/password', changePasswordBody(input)),
|
|
160
|
+
onSuccess: async () => {
|
|
161
|
+
await queryClient.invalidateQueries({ queryKey: SESSIONS_KEY })
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return asMutationResult(mutation)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface ThemeControl {
|
|
169
|
+
readonly theme: ThemePreference
|
|
170
|
+
readonly setTheme: (theme: ThemePreference) => void
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The theme, applied to the document and stored on the account.
|
|
175
|
+
*
|
|
176
|
+
* The account's preference is the source of truth, so the theme follows the
|
|
177
|
+
* person to a new browser. `localStorage` seeds the first paint only: waiting
|
|
178
|
+
* for the request would render the default theme and then correct itself on
|
|
179
|
+
* every cold load.
|
|
180
|
+
*
|
|
181
|
+
* The value comes from the query cache rather than component state, because
|
|
182
|
+
* more than one control changes it. The shell's toggle and the preferences page
|
|
183
|
+
* are separate components, and two `useState` copies of one preference disagree
|
|
184
|
+
* the moment either is clicked.
|
|
185
|
+
*/
|
|
186
|
+
export function useTheme(): ThemeControl {
|
|
187
|
+
const queryClient = useQueryClient()
|
|
188
|
+
const { preferences } = useAccountPreferences()
|
|
189
|
+
const update = useUpdateAccountPreferences()
|
|
190
|
+
const [firstPaintTheme] = useState<ThemePreference>(() => getStoredTheme())
|
|
191
|
+
const theme = preferences?.theme ?? firstPaintTheme
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
applyTheme(theme)
|
|
195
|
+
// Kept current so the next cold load in this browser paints the right theme
|
|
196
|
+
// before the request answers.
|
|
197
|
+
setStoredTheme(theme)
|
|
198
|
+
}, [theme])
|
|
199
|
+
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (theme !== 'system') {
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return watchSystemTheme(() => {
|
|
206
|
+
applyTheme('system')
|
|
207
|
+
})
|
|
208
|
+
}, [theme])
|
|
209
|
+
|
|
210
|
+
function setTheme(next: ThemePreference): void {
|
|
211
|
+
// Applied to the cache first: the click is about something visible, and the
|
|
212
|
+
// eye should not wait on a round trip. `onError` puts the truth back.
|
|
213
|
+
queryClient.setQueryData(PREFERENCES_KEY, (current: AccountPreferences | undefined) =>
|
|
214
|
+
current === undefined ? current : { ...current, theme: next },
|
|
215
|
+
)
|
|
216
|
+
update.run({ theme: next })
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { theme, setTheme }
|
|
220
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { activitySchema } from '@kelpie/schemas'
|
|
2
|
+
import type { Activity, RecordTargetType } from '@kelpie/schemas'
|
|
3
|
+
|
|
4
|
+
import { createReadOnlyResourceHooks } from '../resource.ts'
|
|
5
|
+
import type { RecordListResult } from '../resource.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `/v1/activities`, read-only.
|
|
9
|
+
*
|
|
10
|
+
* No write hooks, because the API has no write routes: activity is emitted by
|
|
11
|
+
* the service that made the change, in the same transaction, and the table is
|
|
12
|
+
* append-only.
|
|
13
|
+
*
|
|
14
|
+
* The roll-up a person's or company's timeline shows is resolved server-side.
|
|
15
|
+
* Nothing here fetches related deals to merge them in.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const activities = createReadOnlyResourceHooks<Activity>({
|
|
19
|
+
name: 'activities',
|
|
20
|
+
path: '/activities',
|
|
21
|
+
decode: activitySchema.parse,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export interface ActivityTimeline {
|
|
25
|
+
readonly targetType: RecordTargetType
|
|
26
|
+
readonly targetId: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function useActivities(timeline: ActivityTimeline): RecordListResult<Activity> {
|
|
30
|
+
return activities.useList({ target_type: timeline.targetType, target_id: timeline.targetId })
|
|
31
|
+
}
|