@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,224 @@
|
|
|
1
|
+
import { THEME_PREFERENCES } from '@kelpie/schemas'
|
|
2
|
+
import type { AccountPreferences, ThemePreference } from '@kelpie/schemas'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import type { FormEvent, ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
useAccountPreferences,
|
|
8
|
+
useTheme,
|
|
9
|
+
useUpdateAccountPreferences,
|
|
10
|
+
} from '../../api/resources/account.ts'
|
|
11
|
+
import { EntitySearch } from '../../components/EntitySearch.tsx'
|
|
12
|
+
import { PageHeader } from '../../components/PageHeader.tsx'
|
|
13
|
+
import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
|
|
14
|
+
import { Field } from './Field.tsx'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Appearance, timezone, and what to email.
|
|
18
|
+
*
|
|
19
|
+
* Ported from the mockup's Preferences page. Appearance is applied the moment it
|
|
20
|
+
* is clicked, because it is visible in the click; everything else waits for the
|
|
21
|
+
* save button, as the mockup does.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const THEME_LABELS: Readonly<Record<ThemePreference, string>> = {
|
|
25
|
+
system: 'System',
|
|
26
|
+
light: 'Light',
|
|
27
|
+
dark: 'Dark',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Every zone this runtime's ICU knows, matching what the server's
|
|
32
|
+
* `timezoneSchema` will accept. Falls back to the zones the mockup offered if
|
|
33
|
+
* `Intl.supportedValuesOf` is missing, for a browser old enough to lack it.
|
|
34
|
+
*
|
|
35
|
+
* `UTC` is prepended because `supportedValuesOf` omits it in some engines even
|
|
36
|
+
* though ECMA-402 guarantees `Intl.DateTimeFormat` accepts it as a zone name.
|
|
37
|
+
* Without this, a very common choice would be searchable only by accident of
|
|
38
|
+
* already being the stored value.
|
|
39
|
+
*/
|
|
40
|
+
const ALL_TIMEZONES: readonly string[] = (() => {
|
|
41
|
+
try {
|
|
42
|
+
return ['UTC', ...Intl.supportedValuesOf('timeZone')]
|
|
43
|
+
} catch {
|
|
44
|
+
return [
|
|
45
|
+
'UTC',
|
|
46
|
+
'Australia/Sydney',
|
|
47
|
+
'Australia/Melbourne',
|
|
48
|
+
'America/Los_Angeles',
|
|
49
|
+
'America/New_York',
|
|
50
|
+
'Europe/London',
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
})()
|
|
54
|
+
|
|
55
|
+
export function PreferencesPage(): React.JSX.Element {
|
|
56
|
+
const { preferences, isLoading, error } = useAccountPreferences()
|
|
57
|
+
|
|
58
|
+
if (error !== null) {
|
|
59
|
+
return <ErrorPanel error={error} />
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (isLoading || preferences === undefined) {
|
|
63
|
+
return <LoadingPanel />
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return <PreferencesForm preferences={preferences} />
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function PreferencesForm({
|
|
70
|
+
preferences,
|
|
71
|
+
}: {
|
|
72
|
+
readonly preferences: AccountPreferences
|
|
73
|
+
}): React.JSX.Element {
|
|
74
|
+
const { theme, setTheme } = useTheme()
|
|
75
|
+
const update = useUpdateAccountPreferences()
|
|
76
|
+
const [timezone, setTimezone] = useState(preferences.timezone)
|
|
77
|
+
const [emailDigest, setEmailDigest] = useState(preferences.emailDigest)
|
|
78
|
+
const [mentionEmails, setMentionEmails] = useState(preferences.mentionEmails)
|
|
79
|
+
const [productUpdates, setProductUpdates] = useState(preferences.productUpdates)
|
|
80
|
+
const [saved, setSaved] = useState(false)
|
|
81
|
+
|
|
82
|
+
const zones = ALL_TIMEZONES.includes(timezone) ? ALL_TIMEZONES : [timezone, ...ALL_TIMEZONES]
|
|
83
|
+
|
|
84
|
+
function save(event: FormEvent): void {
|
|
85
|
+
event.preventDefault()
|
|
86
|
+
setSaved(false)
|
|
87
|
+
|
|
88
|
+
update
|
|
89
|
+
// Theme is left out deliberately: it is already saved, by the control that
|
|
90
|
+
// changed it. Sending the current one back would undo a click made while
|
|
91
|
+
// this form sat open.
|
|
92
|
+
.runAsync({ timezone, emailDigest, mentionEmails, productUpdates })
|
|
93
|
+
.then(() => {
|
|
94
|
+
setSaved(true)
|
|
95
|
+
})
|
|
96
|
+
.catch(() => undefined)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div className="animate-slide-in space-y-6">
|
|
101
|
+
<PageHeader
|
|
102
|
+
title="Preferences"
|
|
103
|
+
description="Appearance, timezone, and email notifications."
|
|
104
|
+
/>
|
|
105
|
+
|
|
106
|
+
<form onSubmit={save} className="space-y-5">
|
|
107
|
+
<fieldset>
|
|
108
|
+
<legend className="mb-1.5 text-[12px] font-medium text-ink">Appearance</legend>
|
|
109
|
+
<div className="inline-flex rounded-md border border-border p-0.5">
|
|
110
|
+
{THEME_PREFERENCES.map((value) => (
|
|
111
|
+
<button
|
|
112
|
+
key={value}
|
|
113
|
+
type="button"
|
|
114
|
+
onClick={() => {
|
|
115
|
+
setTheme(value)
|
|
116
|
+
}}
|
|
117
|
+
className={[
|
|
118
|
+
'rounded px-2.5 py-1 text-[12px] transition',
|
|
119
|
+
theme === value
|
|
120
|
+
? 'bg-surface-sunken font-medium text-ink'
|
|
121
|
+
: 'text-ink-muted hover:text-ink',
|
|
122
|
+
].join(' ')}
|
|
123
|
+
>
|
|
124
|
+
{THEME_LABELS[value]}
|
|
125
|
+
</button>
|
|
126
|
+
))}
|
|
127
|
+
</div>
|
|
128
|
+
<p className="mt-1 text-[11px] text-ink-faint">
|
|
129
|
+
Saved to your account as you click, and applied in every browser you sign in to.
|
|
130
|
+
</p>
|
|
131
|
+
</fieldset>
|
|
132
|
+
|
|
133
|
+
<div className="max-w-md">
|
|
134
|
+
<Field
|
|
135
|
+
label="Timezone"
|
|
136
|
+
hint="Stored on your account. Kelpie does not format dates by it yet."
|
|
137
|
+
>
|
|
138
|
+
<EntitySearch
|
|
139
|
+
options={zones.map((zone) => ({ id: zone, label: zone }))}
|
|
140
|
+
value={timezone}
|
|
141
|
+
onChange={setTimezone}
|
|
142
|
+
placeholder="Search time zones…"
|
|
143
|
+
limit={20}
|
|
144
|
+
required
|
|
145
|
+
size="md"
|
|
146
|
+
/>
|
|
147
|
+
</Field>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<fieldset className="space-y-2">
|
|
151
|
+
<legend className="text-[12px] font-medium text-ink">Email notifications</legend>
|
|
152
|
+
{/* Said plainly rather than left to be inferred from a toggle that
|
|
153
|
+
looks live. Kelpie sends no notification email yet; these record
|
|
154
|
+
the answer for when it does. */}
|
|
155
|
+
<p className="max-w-lg text-[11px] text-ink-faint">
|
|
156
|
+
Kelpie does not send these emails yet. Your choices are saved and will apply when it
|
|
157
|
+
does.
|
|
158
|
+
</p>
|
|
159
|
+
<Toggle
|
|
160
|
+
checked={emailDigest}
|
|
161
|
+
onChange={setEmailDigest}
|
|
162
|
+
label="Weekly digest"
|
|
163
|
+
description="Summary of deals, decisions, and activity."
|
|
164
|
+
/>
|
|
165
|
+
<Toggle
|
|
166
|
+
checked={mentionEmails}
|
|
167
|
+
onChange={setMentionEmails}
|
|
168
|
+
label="Mentions and assignments"
|
|
169
|
+
description="When someone mentions you or assigns a Plan item."
|
|
170
|
+
/>
|
|
171
|
+
<Toggle
|
|
172
|
+
checked={productUpdates}
|
|
173
|
+
onChange={setProductUpdates}
|
|
174
|
+
label="Product updates"
|
|
175
|
+
description="Occasional notes about Kelpie releases."
|
|
176
|
+
/>
|
|
177
|
+
</fieldset>
|
|
178
|
+
|
|
179
|
+
{update.error !== null && (
|
|
180
|
+
<div className="max-w-lg">
|
|
181
|
+
<ErrorPanel error={update.error} />
|
|
182
|
+
</div>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
<button
|
|
186
|
+
type="submit"
|
|
187
|
+
disabled={update.isPending}
|
|
188
|
+
className="rounded-md bg-accent px-3 py-1.5 text-[12px] font-medium text-accent-fg transition hover:bg-accent-hover disabled:opacity-50"
|
|
189
|
+
>
|
|
190
|
+
{update.isPending ? 'Saving…' : saved ? 'Saved' : 'Save preferences'}
|
|
191
|
+
</button>
|
|
192
|
+
</form>
|
|
193
|
+
</div>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function Toggle({
|
|
198
|
+
checked,
|
|
199
|
+
onChange,
|
|
200
|
+
label,
|
|
201
|
+
description,
|
|
202
|
+
}: {
|
|
203
|
+
readonly checked: boolean
|
|
204
|
+
readonly onChange: (value: boolean) => void
|
|
205
|
+
readonly label: string
|
|
206
|
+
readonly description: string
|
|
207
|
+
}): ReactNode {
|
|
208
|
+
return (
|
|
209
|
+
<label className="flex max-w-lg cursor-pointer items-start gap-3 rounded-md border border-border px-3 py-2.5">
|
|
210
|
+
<input
|
|
211
|
+
type="checkbox"
|
|
212
|
+
checked={checked}
|
|
213
|
+
onChange={(event) => {
|
|
214
|
+
onChange(event.target.checked)
|
|
215
|
+
}}
|
|
216
|
+
className="mt-0.5 h-4 w-4 rounded border-border text-accent focus:ring-accent/20"
|
|
217
|
+
/>
|
|
218
|
+
<span>
|
|
219
|
+
<span className="block text-[13px] font-medium text-ink">{label}</span>
|
|
220
|
+
<span className="mt-0.5 block text-[12px] text-ink-muted">{description}</span>
|
|
221
|
+
</span>
|
|
222
|
+
</label>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Account } from '@kelpie/schemas'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import type { FormEvent } from 'react'
|
|
4
|
+
|
|
5
|
+
import { useAccount, useUpdateAccount } from '../../api/resources/account.ts'
|
|
6
|
+
import { PageHeader } from '../../components/PageHeader.tsx'
|
|
7
|
+
import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
|
|
8
|
+
import { initialsOf } from '../../lib/names.ts'
|
|
9
|
+
import { Field } from './Field.tsx'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The account's name and address.
|
|
13
|
+
*
|
|
14
|
+
* Ported from the mockup's Profile page. Two differences now that there is an
|
|
15
|
+
* API behind it: the address is unique across Kelpie, so a save can be refused,
|
|
16
|
+
* and changing it changes what this person signs in with, so the service (and
|
|
17
|
+
* this form) asks for the current password before it takes effect.
|
|
18
|
+
*
|
|
19
|
+
* The form saves explicitly rather than per keystroke, like workspace settings:
|
|
20
|
+
* a half-typed address is a different account, and committing one on every
|
|
21
|
+
* character would be a stream of them.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export function ProfilePage(): React.JSX.Element {
|
|
25
|
+
const { account, isLoading, error } = useAccount()
|
|
26
|
+
|
|
27
|
+
if (error !== null) {
|
|
28
|
+
return <ErrorPanel error={error} />
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isLoading || account === undefined) {
|
|
32
|
+
return <LoadingPanel />
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return <ProfileForm key={account.id} account={account} />
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ProfileForm({ account }: { readonly account: Account }): React.JSX.Element {
|
|
39
|
+
const update = useUpdateAccount()
|
|
40
|
+
const [name, setName] = useState(account.name)
|
|
41
|
+
const [email, setEmail] = useState(account.email)
|
|
42
|
+
const [currentPassword, setCurrentPassword] = useState('')
|
|
43
|
+
const [saved, setSaved] = useState(false)
|
|
44
|
+
const [localError, setLocalError] = useState<string | null>(null)
|
|
45
|
+
|
|
46
|
+
const trimmedEmail = email.trim()
|
|
47
|
+
const emailChanged = trimmedEmail !== account.email
|
|
48
|
+
|
|
49
|
+
function save(event: FormEvent): void {
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
setSaved(false)
|
|
52
|
+
|
|
53
|
+
if (emailChanged && currentPassword.length === 0) {
|
|
54
|
+
setLocalError('Enter your current password to change your email.')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setLocalError(null)
|
|
59
|
+
|
|
60
|
+
const trimmedName = name.trim()
|
|
61
|
+
|
|
62
|
+
update
|
|
63
|
+
.runAsync({
|
|
64
|
+
...(trimmedName === account.name ? {} : { name: trimmedName }),
|
|
65
|
+
...(emailChanged ? { email: trimmedEmail, currentPassword } : {}),
|
|
66
|
+
})
|
|
67
|
+
.then((updated) => {
|
|
68
|
+
// The service lowercases the address it stored. Showing what was typed
|
|
69
|
+
// instead would leave the field disagreeing with the account.
|
|
70
|
+
setEmail(updated.email)
|
|
71
|
+
setName(updated.name)
|
|
72
|
+
setCurrentPassword('')
|
|
73
|
+
setSaved(true)
|
|
74
|
+
})
|
|
75
|
+
.catch(() => undefined)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="animate-slide-in space-y-6">
|
|
80
|
+
<PageHeader title="Profile" description="Your name and email across every workspace." />
|
|
81
|
+
|
|
82
|
+
<form onSubmit={save} className="space-y-4">
|
|
83
|
+
<div className="flex items-center gap-3">
|
|
84
|
+
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-[13px] font-medium text-accent-fg">
|
|
85
|
+
{initialsOf(name)}
|
|
86
|
+
</div>
|
|
87
|
+
<p className="text-[12px] text-ink-muted">Initials are derived from your name.</p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<Field label="Name">
|
|
91
|
+
<input
|
|
92
|
+
value={name}
|
|
93
|
+
onChange={(event) => {
|
|
94
|
+
setName(event.target.value)
|
|
95
|
+
}}
|
|
96
|
+
required
|
|
97
|
+
className="w-full max-w-md rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent/20"
|
|
98
|
+
/>
|
|
99
|
+
</Field>
|
|
100
|
+
|
|
101
|
+
<Field label="Email" hint="This is the address you sign in with.">
|
|
102
|
+
<input
|
|
103
|
+
type="email"
|
|
104
|
+
value={email}
|
|
105
|
+
onChange={(event) => {
|
|
106
|
+
setEmail(event.target.value)
|
|
107
|
+
}}
|
|
108
|
+
required
|
|
109
|
+
autoComplete="email"
|
|
110
|
+
className="w-full max-w-md rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent/20"
|
|
111
|
+
/>
|
|
112
|
+
</Field>
|
|
113
|
+
|
|
114
|
+
{emailChanged && (
|
|
115
|
+
<Field label="Current password" hint="Required to change the address you sign in with.">
|
|
116
|
+
<input
|
|
117
|
+
type="password"
|
|
118
|
+
value={currentPassword}
|
|
119
|
+
onChange={(event) => {
|
|
120
|
+
setCurrentPassword(event.target.value)
|
|
121
|
+
}}
|
|
122
|
+
autoComplete="current-password"
|
|
123
|
+
required
|
|
124
|
+
className="w-full max-w-md rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent/20"
|
|
125
|
+
/>
|
|
126
|
+
</Field>
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
{localError !== null && <p className="text-[12px] text-danger">{localError}</p>}
|
|
130
|
+
|
|
131
|
+
{update.error !== null && (
|
|
132
|
+
<div className="max-w-xl">
|
|
133
|
+
<ErrorPanel error={update.error} />
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
<button
|
|
138
|
+
type="submit"
|
|
139
|
+
disabled={update.isPending}
|
|
140
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover disabled:opacity-50"
|
|
141
|
+
>
|
|
142
|
+
{update.isPending ? 'Saving…' : saved ? 'Saved' : 'Save changes'}
|
|
143
|
+
</button>
|
|
144
|
+
</form>
|
|
145
|
+
</div>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { MINIMUM_PASSWORD_LENGTH } from '@kelpie/schemas'
|
|
2
|
+
import type { AccountSession } from '@kelpie/schemas'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import type { FormEvent } from 'react'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
useAccountSessions,
|
|
8
|
+
useChangePassword,
|
|
9
|
+
useRevokeAccountSession,
|
|
10
|
+
} from '../../api/resources/account.ts'
|
|
11
|
+
import { PageHeader } from '../../components/PageHeader.tsx'
|
|
12
|
+
import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
|
|
13
|
+
import { formatDateTime } from '../../lib/dates.ts'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Password and signed-in devices.
|
|
17
|
+
*
|
|
18
|
+
* Ported from the mockup's Security page against the endpoints that already
|
|
19
|
+
* existed. Two things the mockup could not show: changing the password really
|
|
20
|
+
* does end every other session, so the list below refetches, and the caller's
|
|
21
|
+
* own row cannot be revoked because signing yourself out from here is what the
|
|
22
|
+
* account menu is for.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export function SecurityPage(): React.JSX.Element {
|
|
26
|
+
return (
|
|
27
|
+
<div className="animate-slide-in space-y-8">
|
|
28
|
+
<PageHeader title="Security" description="Your password and where you are signed in." />
|
|
29
|
+
<ChangePasswordForm />
|
|
30
|
+
<SessionList />
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const inputClass =
|
|
36
|
+
'w-full rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent/20'
|
|
37
|
+
|
|
38
|
+
function ChangePasswordForm(): React.JSX.Element {
|
|
39
|
+
const change = useChangePassword()
|
|
40
|
+
const [currentPassword, setCurrentPassword] = useState('')
|
|
41
|
+
const [newPassword, setNewPassword] = useState('')
|
|
42
|
+
const [confirmation, setConfirmation] = useState('')
|
|
43
|
+
const [localError, setLocalError] = useState<string | null>(null)
|
|
44
|
+
const [changed, setChanged] = useState(false)
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The two rules the browser can decide on its own. Length is checked here as
|
|
48
|
+
* well as by the service so the reader learns it before submitting; the
|
|
49
|
+
* confirmation never reaches the API, which has nothing to compare it to.
|
|
50
|
+
*/
|
|
51
|
+
function localProblem(): string | null {
|
|
52
|
+
if (currentPassword.length === 0 || newPassword.length === 0) {
|
|
53
|
+
return 'Enter your current and new password.'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (newPassword.length < MINIMUM_PASSWORD_LENGTH) {
|
|
57
|
+
return `New password must be at least ${String(MINIMUM_PASSWORD_LENGTH)} characters.`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return newPassword === confirmation ? null : 'New password and confirmation do not match.'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function submit(event: FormEvent): void {
|
|
64
|
+
event.preventDefault()
|
|
65
|
+
setChanged(false)
|
|
66
|
+
|
|
67
|
+
const problem = localProblem()
|
|
68
|
+
|
|
69
|
+
setLocalError(problem)
|
|
70
|
+
|
|
71
|
+
if (problem !== null) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
change
|
|
76
|
+
.runAsync({ currentPassword, newPassword })
|
|
77
|
+
.then(() => {
|
|
78
|
+
setCurrentPassword('')
|
|
79
|
+
setNewPassword('')
|
|
80
|
+
setConfirmation('')
|
|
81
|
+
setChanged(true)
|
|
82
|
+
})
|
|
83
|
+
.catch(() => undefined)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<form onSubmit={submit} className="space-y-3">
|
|
88
|
+
<h2 className="text-[14px] font-semibold text-ink">Change password</h2>
|
|
89
|
+
<p className="max-w-md text-[12px] text-ink-muted">
|
|
90
|
+
Changing your password signs you out everywhere except this browser.
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
<label className="block max-w-md">
|
|
94
|
+
<span className="mb-1.5 block text-[12px] font-medium text-ink">Current password</span>
|
|
95
|
+
<input
|
|
96
|
+
type="password"
|
|
97
|
+
value={currentPassword}
|
|
98
|
+
onChange={(event) => {
|
|
99
|
+
setCurrentPassword(event.target.value)
|
|
100
|
+
}}
|
|
101
|
+
autoComplete="current-password"
|
|
102
|
+
className={inputClass}
|
|
103
|
+
/>
|
|
104
|
+
</label>
|
|
105
|
+
|
|
106
|
+
<label className="block max-w-md">
|
|
107
|
+
<span className="mb-1.5 block text-[12px] font-medium text-ink">New password</span>
|
|
108
|
+
<input
|
|
109
|
+
type="password"
|
|
110
|
+
value={newPassword}
|
|
111
|
+
onChange={(event) => {
|
|
112
|
+
setNewPassword(event.target.value)
|
|
113
|
+
}}
|
|
114
|
+
autoComplete="new-password"
|
|
115
|
+
className={inputClass}
|
|
116
|
+
/>
|
|
117
|
+
<span className="mt-1 block text-[11px] text-ink-faint">
|
|
118
|
+
At least {MINIMUM_PASSWORD_LENGTH} characters.
|
|
119
|
+
</span>
|
|
120
|
+
</label>
|
|
121
|
+
|
|
122
|
+
<label className="block max-w-md">
|
|
123
|
+
<span className="mb-1.5 block text-[12px] font-medium text-ink">Confirm new password</span>
|
|
124
|
+
<input
|
|
125
|
+
type="password"
|
|
126
|
+
value={confirmation}
|
|
127
|
+
onChange={(event) => {
|
|
128
|
+
setConfirmation(event.target.value)
|
|
129
|
+
}}
|
|
130
|
+
autoComplete="new-password"
|
|
131
|
+
className={inputClass}
|
|
132
|
+
/>
|
|
133
|
+
</label>
|
|
134
|
+
|
|
135
|
+
{localError !== null && <p className="text-[12px] text-danger">{localError}</p>}
|
|
136
|
+
{change.error !== null && (
|
|
137
|
+
<div className="max-w-md">
|
|
138
|
+
<ErrorPanel error={change.error} />
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
|
|
142
|
+
<button
|
|
143
|
+
type="submit"
|
|
144
|
+
disabled={change.isPending}
|
|
145
|
+
className="rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover disabled:opacity-50"
|
|
146
|
+
>
|
|
147
|
+
{change.isPending ? 'Updating…' : changed ? 'Password updated' : 'Update password'}
|
|
148
|
+
</button>
|
|
149
|
+
</form>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function SessionList(): React.JSX.Element {
|
|
154
|
+
const { sessions, isLoading, error } = useAccountSessions()
|
|
155
|
+
const revoke = useRevokeAccountSession()
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<section>
|
|
159
|
+
<h2 className="mb-3 text-[14px] font-semibold text-ink">Sessions</h2>
|
|
160
|
+
|
|
161
|
+
{error !== null && <ErrorPanel error={error} />}
|
|
162
|
+
{error === null && isLoading && <LoadingPanel label="Loading sessions…" />}
|
|
163
|
+
|
|
164
|
+
{/* The wrapper scrolls rather than clips, like the team tables: a narrow
|
|
165
|
+
window must not hide the column with the Revoke button in it. */}
|
|
166
|
+
{error === null && !isLoading && (
|
|
167
|
+
<div className="overflow-x-auto rounded-md border border-border">
|
|
168
|
+
<table className="w-full text-left text-[13px]">
|
|
169
|
+
<thead>
|
|
170
|
+
<tr className="border-b border-border bg-surface text-[11px] font-semibold tracking-wide text-ink-muted uppercase">
|
|
171
|
+
<th className="px-4 py-2.5">Device</th>
|
|
172
|
+
<th className="px-4 py-2.5">Location</th>
|
|
173
|
+
<th className="px-4 py-2.5">Last active</th>
|
|
174
|
+
<th className="px-4 py-2.5 text-right">Actions</th>
|
|
175
|
+
</tr>
|
|
176
|
+
</thead>
|
|
177
|
+
<tbody>
|
|
178
|
+
{sessions.map((session) => (
|
|
179
|
+
<SessionRow
|
|
180
|
+
key={session.id}
|
|
181
|
+
session={session}
|
|
182
|
+
onRevoke={() => {
|
|
183
|
+
revoke.run(session.id)
|
|
184
|
+
}}
|
|
185
|
+
isRevoking={revoke.isPending}
|
|
186
|
+
/>
|
|
187
|
+
))}
|
|
188
|
+
</tbody>
|
|
189
|
+
</table>
|
|
190
|
+
</div>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
{revoke.error !== null && (
|
|
194
|
+
<div className="mt-3 max-w-xl">
|
|
195
|
+
<ErrorPanel error={revoke.error} />
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</section>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function SessionRow({
|
|
203
|
+
session,
|
|
204
|
+
onRevoke,
|
|
205
|
+
isRevoking,
|
|
206
|
+
}: {
|
|
207
|
+
readonly session: AccountSession
|
|
208
|
+
readonly onRevoke: () => void
|
|
209
|
+
readonly isRevoking: boolean
|
|
210
|
+
}): React.JSX.Element {
|
|
211
|
+
return (
|
|
212
|
+
<tr className="border-b border-border last:border-0">
|
|
213
|
+
<td className="px-4 py-3">
|
|
214
|
+
<div className="font-medium break-all text-ink">{session.device ?? 'Unknown device'}</div>
|
|
215
|
+
{session.current && <span className="text-[11px] font-medium text-success">This device</span>}
|
|
216
|
+
</td>
|
|
217
|
+
{/* Always unknown today: the service records the User-Agent and nothing
|
|
218
|
+
derives a location from the request. The column stays because the field
|
|
219
|
+
is on the wire and a session says where it is from once something can
|
|
220
|
+
tell. */}
|
|
221
|
+
<td className="px-4 py-3 text-ink-muted">{session.location ?? 'Unknown'}</td>
|
|
222
|
+
<td className="px-4 py-3 text-ink-muted">{formatDateTime(session.lastActiveAt)}</td>
|
|
223
|
+
<td className="px-4 py-3 text-right">
|
|
224
|
+
{!session.current && (
|
|
225
|
+
<button
|
|
226
|
+
type="button"
|
|
227
|
+
onClick={onRevoke}
|
|
228
|
+
disabled={isRevoking}
|
|
229
|
+
className="text-[12px] font-medium text-danger hover:underline disabled:opacity-50"
|
|
230
|
+
>
|
|
231
|
+
Revoke
|
|
232
|
+
</button>
|
|
233
|
+
)}
|
|
234
|
+
</td>
|
|
235
|
+
</tr>
|
|
236
|
+
)
|
|
237
|
+
}
|