@kelpie/ui 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/resources/attendances.d.ts +13 -0
- package/dist/api/resources/attendances.d.ts.map +1 -0
- package/dist/api/resources/attendances.js +34 -0
- package/dist/api/resources/attendances.js.map +1 -0
- package/dist/api/resources/eventAssociations.d.ts +12 -0
- package/dist/api/resources/eventAssociations.d.ts.map +1 -0
- package/dist/api/resources/eventAssociations.js +25 -0
- package/dist/api/resources/eventAssociations.js.map +1 -0
- package/dist/api/resources/events.d.ts +17 -0
- package/dist/api/resources/events.d.ts.map +1 -0
- package/dist/api/resources/events.js +40 -0
- package/dist/api/resources/events.js.map +1 -0
- package/dist/api/resources/handbookSeed.d.ts +11 -0
- package/dist/api/resources/handbookSeed.d.ts.map +1 -0
- package/dist/api/resources/handbookSeed.js +43 -0
- package/dist/api/resources/handbookSeed.js.map +1 -0
- package/dist/api/resources/moduleSettings.d.ts +10 -0
- package/dist/api/resources/moduleSettings.d.ts.map +1 -1
- package/dist/api/resources/moduleSettings.js +21 -0
- package/dist/api/resources/moduleSettings.js.map +1 -1
- package/dist/api/resources/planItems.d.ts +4 -4
- package/dist/api/resources/planItems.d.ts.map +1 -1
- package/dist/api/resources/planItems.js +1 -1
- package/dist/api/resources/planItems.js.map +1 -1
- package/dist/api/resources/publicConfig.js +3 -3
- package/dist/api/resources/sampleData.d.ts.map +1 -1
- package/dist/api/resources/sampleData.js +2 -0
- package/dist/api/resources/sampleData.js.map +1 -1
- package/dist/api/resources/session.d.ts +22 -1
- package/dist/api/resources/session.d.ts.map +1 -1
- package/dist/api/resources/session.js +43 -1
- package/dist/api/resources/session.js.map +1 -1
- package/dist/api/resources/workspace.d.ts.map +1 -1
- package/dist/api/resources/workspace.js +2 -3
- package/dist/api/resources/workspace.js.map +1 -1
- package/dist/app/KelpieApp.d.ts.map +1 -1
- package/dist/app/KelpieApp.js +5 -1
- package/dist/app/KelpieApp.js.map +1 -1
- package/dist/components/ActivitiesPanel.d.ts.map +1 -1
- package/dist/components/ActivitiesPanel.js +2 -0
- package/dist/components/ActivitiesPanel.js.map +1 -1
- package/dist/components/AddressesField.d.ts +23 -0
- package/dist/components/AddressesField.d.ts.map +1 -0
- package/dist/components/AddressesField.js +215 -0
- package/dist/components/AddressesField.js.map +1 -0
- package/dist/components/CountrySearch.d.ts +16 -0
- package/dist/components/CountrySearch.d.ts.map +1 -0
- package/dist/components/CountrySearch.js +30 -0
- package/dist/components/CountrySearch.js.map +1 -0
- package/dist/components/EnvironmentBanner.d.ts +0 -12
- package/dist/components/EnvironmentBanner.d.ts.map +1 -1
- package/dist/components/EnvironmentBanner.js +21 -6
- package/dist/components/EnvironmentBanner.js.map +1 -1
- package/dist/components/InlineEdit.d.ts +7 -1
- package/dist/components/InlineEdit.d.ts.map +1 -1
- package/dist/components/InlineEdit.js +8 -1
- package/dist/components/InlineEdit.js.map +1 -1
- package/dist/components/LinkedEventsPanel.d.ts +11 -0
- package/dist/components/LinkedEventsPanel.d.ts.map +1 -0
- package/dist/components/LinkedEventsPanel.js +18 -0
- package/dist/components/LinkedEventsPanel.js.map +1 -0
- package/dist/components/NotesPanel.d.ts +4 -6
- package/dist/components/NotesPanel.d.ts.map +1 -1
- package/dist/components/NotesPanel.js +39 -3
- package/dist/components/NotesPanel.js.map +1 -1
- package/dist/components/PlanAttention.d.ts.map +1 -1
- package/dist/components/PlanAttention.js +3 -2
- package/dist/components/PlanAttention.js.map +1 -1
- package/dist/components/PlanPanel.d.ts +3 -3
- package/dist/components/PlanPanel.d.ts.map +1 -1
- package/dist/components/Shell.d.ts.map +1 -1
- package/dist/components/Shell.js +8 -2
- package/dist/components/Shell.js.map +1 -1
- package/dist/components/SocialNetworkIcon.d.ts +17 -0
- package/dist/components/SocialNetworkIcon.d.ts.map +1 -0
- package/dist/components/SocialNetworkIcon.js +39 -0
- package/dist/components/SocialNetworkIcon.js.map +1 -0
- package/dist/components/SocialProfilesField.d.ts +8 -0
- package/dist/components/SocialProfilesField.d.ts.map +1 -1
- package/dist/components/SocialProfilesField.js +19 -5
- package/dist/components/SocialProfilesField.js.map +1 -1
- package/dist/components/TimezoneSearch.d.ts +21 -0
- package/dist/components/TimezoneSearch.d.ts.map +1 -0
- package/dist/components/TimezoneSearch.js +27 -0
- package/dist/components/TimezoneSearch.js.map +1 -0
- package/dist/components/WorkspaceSwitcher.d.ts +8 -0
- package/dist/components/WorkspaceSwitcher.d.ts.map +1 -0
- package/dist/components/WorkspaceSwitcher.js +67 -0
- package/dist/components/WorkspaceSwitcher.js.map +1 -0
- package/dist/components/environmentTone.d.ts +10 -0
- package/dist/components/environmentTone.d.ts.map +1 -0
- package/dist/components/environmentTone.js +35 -0
- package/dist/components/environmentTone.js.map +1 -0
- package/dist/index.d.ts +25 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/timezones.d.ts +17 -0
- package/dist/lib/timezones.d.ts.map +1 -0
- package/dist/lib/timezones.js +39 -0
- package/dist/lib/timezones.js.map +1 -0
- package/dist/pages/CompaniesPage.d.ts.map +1 -1
- package/dist/pages/CompaniesPage.js +12 -11
- package/dist/pages/CompaniesPage.js.map +1 -1
- package/dist/pages/CompanyDetail.d.ts.map +1 -1
- package/dist/pages/CompanyDetail.js +23 -12
- package/dist/pages/CompanyDetail.js.map +1 -1
- package/dist/pages/DealDetail.d.ts.map +1 -1
- package/dist/pages/DealDetail.js +5 -1
- package/dist/pages/DealDetail.js.map +1 -1
- package/dist/pages/DecisionsPage.d.ts.map +1 -1
- package/dist/pages/DecisionsPage.js +7 -0
- package/dist/pages/DecisionsPage.js.map +1 -1
- package/dist/pages/EnquiryDetail.d.ts.map +1 -1
- package/dist/pages/EnquiryDetail.js +5 -1
- package/dist/pages/EnquiryDetail.js.map +1 -1
- package/dist/pages/EventDetail.d.ts +2 -0
- package/dist/pages/EventDetail.d.ts.map +1 -0
- package/dist/pages/EventDetail.js +332 -0
- package/dist/pages/EventDetail.js.map +1 -0
- package/dist/pages/EventsPage.d.ts +2 -0
- package/dist/pages/EventsPage.d.ts.map +1 -0
- package/dist/pages/EventsPage.js +176 -0
- package/dist/pages/EventsPage.js.map +1 -0
- package/dist/pages/ListDetail.d.ts.map +1 -1
- package/dist/pages/ListDetail.js +17 -0
- package/dist/pages/ListDetail.js.map +1 -1
- package/dist/pages/OpportunityDetail.d.ts.map +1 -1
- package/dist/pages/OpportunityDetail.js +5 -1
- package/dist/pages/OpportunityDetail.js.map +1 -1
- package/dist/pages/PartnershipDetail.d.ts.map +1 -1
- package/dist/pages/PartnershipDetail.js +5 -1
- package/dist/pages/PartnershipDetail.js.map +1 -1
- package/dist/pages/PeoplePage.d.ts.map +1 -1
- package/dist/pages/PeoplePage.js +13 -5
- package/dist/pages/PeoplePage.js.map +1 -1
- package/dist/pages/PersonDetail.d.ts.map +1 -1
- package/dist/pages/PersonDetail.js +94 -28
- package/dist/pages/PersonDetail.js.map +1 -1
- package/dist/pages/PlanningPage.d.ts.map +1 -1
- package/dist/pages/PlanningPage.js +6 -3
- package/dist/pages/PlanningPage.js.map +1 -1
- package/dist/pages/RaiseDetail.d.ts.map +1 -1
- package/dist/pages/RaiseDetail.js +5 -1
- package/dist/pages/RaiseDetail.js.map +1 -1
- package/dist/pages/RoleDetail.d.ts.map +1 -1
- package/dist/pages/RoleDetail.js +4 -1
- package/dist/pages/RoleDetail.js.map +1 -1
- package/dist/pages/SearchPage.d.ts.map +1 -1
- package/dist/pages/SearchPage.js +2 -1
- package/dist/pages/SearchPage.js.map +1 -1
- package/dist/pages/account/PreferencesPage.d.ts.map +1 -1
- package/dist/pages/account/PreferencesPage.js +2 -28
- package/dist/pages/account/PreferencesPage.js.map +1 -1
- package/dist/pages/account/SecurityPage.js +2 -2
- package/dist/pages/account/SecurityPage.js.map +1 -1
- package/dist/pages/admin/DataPage.js +10 -9
- package/dist/pages/admin/DataPage.js.map +1 -1
- package/dist/pages/admin/FieldsPage.js +1 -1
- package/dist/pages/admin/FieldsPage.js.map +1 -1
- package/dist/pages/admin/ModulesPage.d.ts.map +1 -1
- package/dist/pages/admin/ModulesPage.js +1 -0
- package/dist/pages/admin/ModulesPage.js.map +1 -1
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts +12 -0
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
- package/dist/pages/admin/WorkspaceSettingsPage.js +2 -21
- package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
- package/dist/pages/auth/AuthForm.d.ts +18 -0
- package/dist/pages/auth/AuthForm.d.ts.map +1 -1
- package/dist/pages/auth/AuthForm.js +17 -0
- package/dist/pages/auth/AuthForm.js.map +1 -1
- package/dist/pages/auth/AuthLayout.d.ts +5 -4
- package/dist/pages/auth/AuthLayout.d.ts.map +1 -1
- package/dist/pages/auth/AuthLayout.js +7 -5
- package/dist/pages/auth/AuthLayout.js.map +1 -1
- package/dist/pages/auth/AuthMethods.d.ts +12 -0
- package/dist/pages/auth/AuthMethods.d.ts.map +1 -0
- package/dist/pages/auth/AuthMethods.js +20 -0
- package/dist/pages/auth/AuthMethods.js.map +1 -0
- package/dist/pages/auth/RegionSwitcher.d.ts +10 -0
- package/dist/pages/auth/RegionSwitcher.d.ts.map +1 -0
- package/dist/pages/auth/RegionSwitcher.js +28 -0
- package/dist/pages/auth/RegionSwitcher.js.map +1 -0
- package/dist/pages/auth/SignInPage.d.ts +7 -0
- package/dist/pages/auth/SignInPage.d.ts.map +1 -1
- package/dist/pages/auth/SignInPage.js +5 -8
- package/dist/pages/auth/SignInPage.js.map +1 -1
- package/dist/pages/auth/SignUpPage.d.ts.map +1 -1
- package/dist/pages/auth/SignUpPage.js +9 -1
- package/dist/pages/auth/SignUpPage.js.map +1 -1
- package/dist/pages/auth/nextPath.d.ts +15 -0
- package/dist/pages/auth/nextPath.d.ts.map +1 -0
- package/dist/pages/auth/nextPath.js +17 -0
- package/dist/pages/auth/nextPath.js.map +1 -0
- package/dist/pages/auth/regionHref.d.ts +6 -0
- package/dist/pages/auth/regionHref.d.ts.map +1 -0
- package/dist/pages/auth/regionHref.js +8 -0
- package/dist/pages/auth/regionHref.js.map +1 -0
- package/dist/pages/dashboard/DashboardPage.d.ts +8 -0
- package/dist/pages/dashboard/DashboardPage.d.ts.map +1 -1
- package/dist/pages/dashboard/DashboardPage.js +5 -13
- package/dist/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/pages/dashboard/attention.d.ts +1 -8
- package/dist/pages/dashboard/attention.d.ts.map +1 -1
- package/dist/pages/dashboard/attention.js +21 -54
- package/dist/pages/dashboard/attention.js.map +1 -1
- package/dist/pages/dashboard/sections.d.ts.map +1 -1
- package/dist/pages/dashboard/sections.js +3 -2
- package/dist/pages/dashboard/sections.js.map +1 -1
- package/dist/pages/forms/FormActions.d.ts.map +1 -1
- package/dist/pages/forms/FormActions.js +7 -3
- package/dist/pages/forms/FormActions.js.map +1 -1
- package/dist/pages/onboarding/HandbookStepPage.d.ts +4 -8
- package/dist/pages/onboarding/HandbookStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/HandbookStepPage.js +13 -13
- package/dist/pages/onboarding/HandbookStepPage.js.map +1 -1
- package/dist/pages/onboarding/InvitesStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/InvitesStepPage.js +13 -7
- package/dist/pages/onboarding/InvitesStepPage.js.map +1 -1
- package/dist/pages/onboarding/ModulesStepPage.d.ts +11 -0
- package/dist/pages/onboarding/ModulesStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/ModulesStepPage.js +80 -0
- package/dist/pages/onboarding/ModulesStepPage.js.map +1 -0
- package/dist/pages/onboarding/OnboardingNav.d.ts +17 -0
- package/dist/pages/onboarding/OnboardingNav.d.ts.map +1 -0
- package/dist/pages/onboarding/OnboardingNav.js +37 -0
- package/dist/pages/onboarding/OnboardingNav.js.map +1 -0
- package/dist/pages/onboarding/OrganisationStepPage.d.ts +13 -0
- package/dist/pages/onboarding/OrganisationStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/OrganisationStepPage.js +59 -0
- package/dist/pages/onboarding/OrganisationStepPage.js.map +1 -0
- package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/WorkspaceStepPage.js +25 -20
- package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
- package/dist/pages/onboarding/handbookTemplates.d.ts +19 -0
- package/dist/pages/onboarding/handbookTemplates.d.ts.map +1 -0
- package/dist/pages/onboarding/handbookTemplates.js +57 -0
- package/dist/pages/onboarding/handbookTemplates.js.map +1 -0
- package/dist/pages/onboarding/onboardingModules.d.ts +23 -0
- package/dist/pages/onboarding/onboardingModules.d.ts.map +1 -0
- package/dist/pages/onboarding/onboardingModules.js +153 -0
- package/dist/pages/onboarding/onboardingModules.js.map +1 -0
- package/dist/pages/onboarding/onboardingRerun.d.ts +21 -0
- package/dist/pages/onboarding/onboardingRerun.d.ts.map +1 -0
- package/dist/pages/onboarding/onboardingRerun.js +33 -0
- package/dist/pages/onboarding/onboardingRerun.js.map +1 -0
- package/dist/registry/context.d.ts +3 -1
- package/dist/registry/context.d.ts.map +1 -1
- package/dist/registry/context.js +4 -0
- package/dist/registry/context.js.map +1 -1
- package/dist/registry/contributions.d.ts +24 -0
- package/dist/registry/contributions.d.ts.map +1 -1
- package/dist/registry/contributions.js.map +1 -1
- package/dist/registry/registry.d.ts +4 -1
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +6 -0
- package/dist/registry/registry.js.map +1 -1
- package/package.json +4 -3
- package/src/api/resources/attendances.ts +54 -0
- package/src/api/resources/eventAssociations.ts +43 -0
- package/src/api/resources/events.ts +70 -0
- package/src/api/resources/handbookSeed.ts +63 -0
- package/src/api/resources/moduleSettings.ts +25 -0
- package/src/api/resources/planItems.ts +5 -5
- package/src/api/resources/publicConfig.ts +3 -3
- package/src/api/resources/sampleData.ts +2 -0
- package/src/api/resources/session.ts +59 -0
- package/src/api/resources/workspace.ts +2 -3
- package/src/app/KelpieApp.tsx +11 -3
- package/src/components/ActivitiesPanel.tsx +2 -0
- package/src/components/AddressesField.tsx +502 -0
- package/src/components/CountrySearch.tsx +71 -0
- package/src/components/EnvironmentBanner.tsx +23 -6
- package/src/components/InlineEdit.tsx +35 -1
- package/src/components/LinkedEventsPanel.tsx +50 -0
- package/src/components/NotesPanel.tsx +132 -15
- package/src/components/PlanAttention.tsx +5 -4
- package/src/components/PlanPanel.tsx +3 -3
- package/src/components/Shell.tsx +16 -1
- package/src/components/SocialNetworkIcon.tsx +88 -0
- package/src/components/SocialProfilesField.tsx +57 -10
- package/src/components/TimezoneSearch.tsx +67 -0
- package/src/components/WorkspaceSwitcher.tsx +136 -0
- package/src/components/environmentTone.ts +40 -0
- package/src/index.ts +49 -3
- package/src/lib/timezones.ts +43 -0
- package/src/pages/CompaniesPage.tsx +14 -13
- package/src/pages/CompanyDetail.tsx +59 -27
- package/src/pages/DealDetail.tsx +5 -0
- package/src/pages/DecisionsPage.tsx +7 -0
- package/src/pages/EnquiryDetail.tsx +7 -0
- package/src/pages/EventDetail.tsx +794 -0
- package/src/pages/EventsPage.tsx +382 -0
- package/src/pages/ListDetail.tsx +24 -0
- package/src/pages/OpportunityDetail.tsx +7 -0
- package/src/pages/PartnershipDetail.tsx +7 -0
- package/src/pages/PeoplePage.tsx +18 -5
- package/src/pages/PersonDetail.tsx +300 -100
- package/src/pages/PlanningPage.tsx +17 -14
- package/src/pages/RaiseDetail.tsx +5 -0
- package/src/pages/RoleDetail.tsx +8 -0
- package/src/pages/SearchPage.tsx +2 -1
- package/src/pages/account/PreferencesPage.tsx +2 -37
- package/src/pages/account/SecurityPage.tsx +6 -1
- package/src/pages/admin/DataPage.tsx +63 -21
- package/src/pages/admin/FieldsPage.tsx +1 -1
- package/src/pages/admin/ModulesPage.tsx +1 -0
- package/src/pages/admin/WorkspaceSettingsPage.tsx +10 -33
- package/src/pages/auth/AuthForm.tsx +38 -0
- package/src/pages/auth/AuthLayout.tsx +8 -4
- package/src/pages/auth/AuthMethods.tsx +29 -0
- package/src/pages/auth/RegionSwitcher.tsx +56 -0
- package/src/pages/auth/SignInPage.tsx +13 -14
- package/src/pages/auth/SignUpPage.tsx +32 -0
- package/src/pages/auth/nextPath.ts +17 -0
- package/src/pages/auth/regionHref.ts +10 -0
- package/src/pages/dashboard/DashboardPage.tsx +16 -48
- package/src/pages/dashboard/attention.ts +24 -78
- package/src/pages/dashboard/sections.tsx +8 -2
- package/src/pages/forms/FormActions.tsx +14 -8
- package/src/pages/onboarding/HandbookStepPage.tsx +21 -24
- package/src/pages/onboarding/InvitesStepPage.tsx +27 -19
- package/src/pages/onboarding/ModulesStepPage.tsx +145 -0
- package/src/pages/onboarding/OnboardingNav.tsx +97 -0
- package/src/pages/onboarding/OrganisationStepPage.tsx +130 -0
- package/src/pages/onboarding/WorkspaceStepPage.tsx +53 -38
- package/src/pages/onboarding/handbookTemplates.ts +77 -0
- package/src/pages/onboarding/onboardingModules.ts +202 -0
- package/src/pages/onboarding/onboardingRerun.ts +41 -0
- package/src/registry/context.ts +6 -0
- package/src/registry/contributions.ts +26 -0
- package/src/registry/registry.ts +12 -0
- package/src/styles.css +30 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { FormEvent } from 'react'
|
|
3
|
+
import { useNavigate, useSearchParams } from 'react-router'
|
|
4
|
+
|
|
5
|
+
import { useSetModuleEnabled } from '../../api/resources/moduleSettings.ts'
|
|
6
|
+
import { useInstallSampleData } from '../../api/resources/sampleData.ts'
|
|
7
|
+
import { useSession } from '../../api/resources/session.ts'
|
|
8
|
+
import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
|
|
9
|
+
import { AuthLayout } from '../auth/AuthLayout.tsx'
|
|
10
|
+
import { organisationChoiceFromParam, organisationChoiceLabel } from './handbookTemplates.ts'
|
|
11
|
+
import type { OrganisationChoiceId } from './handbookTemplates.ts'
|
|
12
|
+
import { OnboardingNav } from './OnboardingNav.tsx'
|
|
13
|
+
import { defaultOnboardingModuleState, onboardingModulesForChoice } from './onboardingModules.ts'
|
|
14
|
+
import { isOnboardingRerun, ONBOARDING_ORG_PARAM, onboardingPath } from './onboardingRerun.ts'
|
|
15
|
+
|
|
16
|
+
function modulesStepDescription(org: OrganisationChoiceId): string {
|
|
17
|
+
if (org === 'startup' || org === 'later') {
|
|
18
|
+
return 'Pick the parts of Kelpie your team needs today. Turn the rest on whenever you like — nothing here is permanent.'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return `Suggested for ${organisationChoiceLabel(org)}. Turn the rest on whenever you like — nothing here is permanent.`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Onboarding step 3: which optional modules this workspace runs.
|
|
26
|
+
*
|
|
27
|
+
* The workspace already exists. This step writes each of the six choices
|
|
28
|
+
* through `PATCH /v1/workspaces/:id/modules/:moduleId`, then optionally
|
|
29
|
+
* installs sample data, then moves on to invites. Defaults and explanations
|
|
30
|
+
* follow the organisation type on `?org=`. Other toggleable modules stay
|
|
31
|
+
* on; Admin → Modules is where they change later.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export function ModulesStepPage(): React.JSX.Element {
|
|
35
|
+
const navigate = useNavigate()
|
|
36
|
+
const [searchParams] = useSearchParams()
|
|
37
|
+
const rerun = isOnboardingRerun(searchParams)
|
|
38
|
+
const org = organisationChoiceFromParam(searchParams.get(ONBOARDING_ORG_PARAM))
|
|
39
|
+
const modules = onboardingModulesForChoice(org)
|
|
40
|
+
const { session, isLoading } = useSession()
|
|
41
|
+
const setEnabled = useSetModuleEnabled()
|
|
42
|
+
const installSampleData = useInstallSampleData()
|
|
43
|
+
const [enabled, setEnabledState] = useState(() => defaultOnboardingModuleState(org))
|
|
44
|
+
const [seedSample, setSeedSample] = useState(false)
|
|
45
|
+
const [saving, setSaving] = useState(false)
|
|
46
|
+
|
|
47
|
+
function toggle(moduleId: (typeof modules)[number]['id'], next: boolean): void {
|
|
48
|
+
setEnabledState((previous) => ({ ...previous, [moduleId]: next }))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function submit(event: FormEvent): void {
|
|
52
|
+
event.preventDefault()
|
|
53
|
+
|
|
54
|
+
const workspaceId = session?.workspaceId
|
|
55
|
+
|
|
56
|
+
if (workspaceId === null || workspaceId === undefined) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
setSaving(true)
|
|
61
|
+
|
|
62
|
+
persistChoices()
|
|
63
|
+
.then(async () => {
|
|
64
|
+
if (seedSample) {
|
|
65
|
+
await installSampleData.runAsync({ workspaceId })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
navigate(onboardingPath('/onboarding/invites', rerun, org), { replace: true })
|
|
69
|
+
})
|
|
70
|
+
.catch(() => undefined)
|
|
71
|
+
.finally(() => {
|
|
72
|
+
setSaving(false)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function persistChoices(): Promise<void> {
|
|
77
|
+
for (const choice of modules) {
|
|
78
|
+
await setEnabled.runAsync({
|
|
79
|
+
moduleId: choice.id,
|
|
80
|
+
enabled: enabled[choice.id] === true,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const isPending = saving || setEnabled.isPending || installSampleData.isPending
|
|
86
|
+
const workspaceId = session?.workspaceId
|
|
87
|
+
const description = modulesStepDescription(org)
|
|
88
|
+
|
|
89
|
+
if (isLoading || workspaceId === null || workspaceId === undefined) {
|
|
90
|
+
return (
|
|
91
|
+
<AuthLayout step={3} title="What do you want to use?" description={description}>
|
|
92
|
+
<div className="mt-5">
|
|
93
|
+
<LoadingPanel label="Loading…" />
|
|
94
|
+
</div>
|
|
95
|
+
</AuthLayout>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<AuthLayout step={3} title="What do you want to use?" description={description}>
|
|
101
|
+
<form onSubmit={submit} className="mt-5 space-y-3">
|
|
102
|
+
<ul className="space-y-3">
|
|
103
|
+
{modules.map((choice) => (
|
|
104
|
+
<li key={choice.id}>
|
|
105
|
+
<label className="flex items-start gap-2 text-[12px] text-ink">
|
|
106
|
+
<input
|
|
107
|
+
type="checkbox"
|
|
108
|
+
checked={enabled[choice.id] === true}
|
|
109
|
+
onChange={(event) => {
|
|
110
|
+
toggle(choice.id, event.target.checked)
|
|
111
|
+
}}
|
|
112
|
+
className="mt-0.5 h-4 w-4 rounded border-border text-accent focus:ring-accent"
|
|
113
|
+
/>
|
|
114
|
+
<span>
|
|
115
|
+
<span className="font-medium">{choice.label}</span>
|
|
116
|
+
<span className="mt-0.5 block text-[11px] text-ink-muted">{choice.description}</span>
|
|
117
|
+
</span>
|
|
118
|
+
</label>
|
|
119
|
+
</li>
|
|
120
|
+
))}
|
|
121
|
+
</ul>
|
|
122
|
+
<label className="flex items-start gap-2 text-[12px] text-ink">
|
|
123
|
+
<input
|
|
124
|
+
type="checkbox"
|
|
125
|
+
checked={seedSample}
|
|
126
|
+
onChange={(event) => setSeedSample(event.target.checked)}
|
|
127
|
+
className="mt-0.5 h-4 w-4 rounded border-border text-accent focus:ring-accent"
|
|
128
|
+
/>
|
|
129
|
+
<span>
|
|
130
|
+
Fill it with sample data — a few companies, people, and records for the parts you
|
|
131
|
+
turned on, so you are not staring at an empty screen. Clear it out any time.
|
|
132
|
+
</span>
|
|
133
|
+
</label>
|
|
134
|
+
{setEnabled.error !== null && <ErrorPanel error={setEnabled.error} />}
|
|
135
|
+
{installSampleData.error !== null && <ErrorPanel error={installSampleData.error} />}
|
|
136
|
+
<OnboardingNav
|
|
137
|
+
step={3}
|
|
138
|
+
nextLabel="Next"
|
|
139
|
+
nextPendingLabel={installSampleData.isPending ? 'Installing sample data…' : 'Saving…'}
|
|
140
|
+
isPending={isPending}
|
|
141
|
+
/>
|
|
142
|
+
</form>
|
|
143
|
+
</AuthLayout>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { useNavigate, useSearchParams } from 'react-router'
|
|
3
|
+
|
|
4
|
+
import type { OnboardingStep } from '../auth/AuthLayout.tsx'
|
|
5
|
+
import { isOnboardingRerun, ONBOARDING_ORG_PARAM, onboardingPath } from './onboardingRerun.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The Previous / Next row every onboarding step shares.
|
|
9
|
+
*
|
|
10
|
+
* Forward still commits on the step that owns the form. This only moves
|
|
11
|
+
* between the five routes. Step 1 has no previous onboarding page, so
|
|
12
|
+
* Previous stays off there. The `rerun` and `org` queries are kept on
|
|
13
|
+
* Previous so a finished workspace that walks the wizard again still skips
|
|
14
|
+
* handbook seeding, and the modules step still sees the organisation type.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const ONBOARDING_PATHS = {
|
|
18
|
+
1: '/onboarding/workspace',
|
|
19
|
+
2: '/onboarding/organisation',
|
|
20
|
+
3: '/onboarding/modules',
|
|
21
|
+
4: '/onboarding/invites',
|
|
22
|
+
5: '/onboarding/handbook',
|
|
23
|
+
} as const
|
|
24
|
+
|
|
25
|
+
const PRIMARY_CLASS =
|
|
26
|
+
'flex-1 rounded-md bg-accent px-3.5 py-2 text-[12px] font-semibold text-accent-fg transition hover:bg-accent-hover disabled:opacity-50'
|
|
27
|
+
|
|
28
|
+
const SECONDARY_CLASS =
|
|
29
|
+
'flex-1 rounded-md border border-border bg-surface px-3.5 py-2 text-[12px] font-medium text-ink-muted transition hover:bg-surface-raised disabled:opacity-50'
|
|
30
|
+
|
|
31
|
+
export interface OnboardingNavProps {
|
|
32
|
+
readonly step: OnboardingStep
|
|
33
|
+
readonly nextLabel: string
|
|
34
|
+
/** Shown while the step's request is in flight. */
|
|
35
|
+
readonly nextPendingLabel?: string
|
|
36
|
+
readonly isPending?: boolean
|
|
37
|
+
/** `submit` inside a form; `button` when the step only navigates. */
|
|
38
|
+
readonly nextType?: 'submit' | 'button'
|
|
39
|
+
readonly onNext?: () => void
|
|
40
|
+
/** A third action under the row, such as Skip on invites. */
|
|
41
|
+
readonly extra?: ReactNode
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function previousOnboardingPath(
|
|
45
|
+
step: OnboardingStep,
|
|
46
|
+
rerun = false,
|
|
47
|
+
org?: string | null,
|
|
48
|
+
): string | undefined {
|
|
49
|
+
if (step === 1) {
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return onboardingPath(ONBOARDING_PATHS[(step - 1) as OnboardingStep], rerun, org)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function OnboardingNav({
|
|
57
|
+
step,
|
|
58
|
+
nextLabel,
|
|
59
|
+
nextPendingLabel,
|
|
60
|
+
isPending = false,
|
|
61
|
+
nextType = 'submit',
|
|
62
|
+
onNext,
|
|
63
|
+
extra,
|
|
64
|
+
}: OnboardingNavProps): React.JSX.Element {
|
|
65
|
+
const navigate = useNavigate()
|
|
66
|
+
const [searchParams] = useSearchParams()
|
|
67
|
+
const rerun = isOnboardingRerun(searchParams)
|
|
68
|
+
const previousTo = previousOnboardingPath(step, rerun, searchParams.get(ONBOARDING_ORG_PARAM))
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div className="flex flex-col gap-2 pt-1">
|
|
72
|
+
<div className="flex gap-2">
|
|
73
|
+
{previousTo !== undefined && (
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
disabled={isPending}
|
|
77
|
+
onClick={() => {
|
|
78
|
+
navigate(previousTo, { replace: true })
|
|
79
|
+
}}
|
|
80
|
+
className={SECONDARY_CLASS}
|
|
81
|
+
>
|
|
82
|
+
Previous
|
|
83
|
+
</button>
|
|
84
|
+
)}
|
|
85
|
+
<button
|
|
86
|
+
type={nextType}
|
|
87
|
+
disabled={isPending}
|
|
88
|
+
onClick={onNext}
|
|
89
|
+
className={PRIMARY_CLASS}
|
|
90
|
+
>
|
|
91
|
+
{isPending && nextPendingLabel !== undefined ? nextPendingLabel : nextLabel}
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
{extra}
|
|
95
|
+
</div>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { FormEvent } from 'react'
|
|
3
|
+
import { useNavigate, useSearchParams } from 'react-router'
|
|
4
|
+
|
|
5
|
+
import { useHandbookPages } from '../../api/resources/handbookPages.ts'
|
|
6
|
+
import { useSeedHandbookForWorkspace } from '../../api/resources/handbookSeed.ts'
|
|
7
|
+
import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
|
|
8
|
+
import { AuthLayout } from '../auth/AuthLayout.tsx'
|
|
9
|
+
import {
|
|
10
|
+
handbookTemplateForChoice,
|
|
11
|
+
ORGANISATION_CHOICES,
|
|
12
|
+
organisationChoiceFromParam,
|
|
13
|
+
} from './handbookTemplates.ts'
|
|
14
|
+
import type { OrganisationChoiceId } from './handbookTemplates.ts'
|
|
15
|
+
import { OnboardingNav } from './OnboardingNav.tsx'
|
|
16
|
+
import { isOnboardingRerun, ONBOARDING_ORG_PARAM, onboardingPath } from './onboardingRerun.ts'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Onboarding step 2: organisation type, then seed the starter handbook.
|
|
20
|
+
*
|
|
21
|
+
* The workspace already exists. This step writes pages through
|
|
22
|
+
* `POST /v1/workspaces/:id/handbook/seed`. "Choose later" seeds the original
|
|
23
|
+
* startup handbook. Changing the choice (via Previous) replaces pages only
|
|
24
|
+
* while they are still unedited starter stubs.
|
|
25
|
+
*
|
|
26
|
+
* A rerun still shows the choices, but does not seed or replace handbook
|
|
27
|
+
* pages. Next moves on to modules with those pages left alone.
|
|
28
|
+
*/
|
|
29
|
+
export function OrganisationStepPage(): React.JSX.Element {
|
|
30
|
+
const navigate = useNavigate()
|
|
31
|
+
const [searchParams] = useSearchParams()
|
|
32
|
+
const rerun = isOnboardingRerun(searchParams)
|
|
33
|
+
const seedHandbook = useSeedHandbookForWorkspace()
|
|
34
|
+
const { records: pages, isLoading } = useHandbookPages()
|
|
35
|
+
const [choice, setChoice] = useState<OrganisationChoiceId>(() =>
|
|
36
|
+
organisationChoiceFromParam(searchParams.get(ONBOARDING_ORG_PARAM)),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
function goToModules(): void {
|
|
40
|
+
navigate(onboardingPath('/onboarding/modules', rerun, choice), { replace: true })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function submit(event: FormEvent): void {
|
|
44
|
+
event.preventDefault()
|
|
45
|
+
|
|
46
|
+
if (rerun) {
|
|
47
|
+
goToModules()
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
seedHandbook
|
|
52
|
+
.runAsync({
|
|
53
|
+
handbookTemplate: handbookTemplateForChoice(choice),
|
|
54
|
+
replace: pages.length > 0,
|
|
55
|
+
})
|
|
56
|
+
.then(() => {
|
|
57
|
+
goToModules()
|
|
58
|
+
})
|
|
59
|
+
.catch(() => undefined)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!rerun && isLoading) {
|
|
63
|
+
return (
|
|
64
|
+
<AuthLayout
|
|
65
|
+
step={2}
|
|
66
|
+
title="What kind of organisation is this?"
|
|
67
|
+
description="This picks the starter handbook pages agents read to learn what your company is."
|
|
68
|
+
>
|
|
69
|
+
<div className="mt-5">
|
|
70
|
+
<LoadingPanel label="Loading…" />
|
|
71
|
+
</div>
|
|
72
|
+
</AuthLayout>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<AuthLayout
|
|
78
|
+
step={2}
|
|
79
|
+
title="What kind of organisation is this?"
|
|
80
|
+
description={
|
|
81
|
+
rerun
|
|
82
|
+
? 'Pick a type to continue.'
|
|
83
|
+
: 'This picks the starter handbook pages agents read to learn what your company is.'
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
<form onSubmit={submit} className="mt-5 space-y-3">
|
|
87
|
+
<ul className="grid gap-2 sm:grid-cols-2">
|
|
88
|
+
{ORGANISATION_CHOICES.map((option) => (
|
|
89
|
+
<li
|
|
90
|
+
key={option.id}
|
|
91
|
+
className={option.id === 'later' ? 'h-full sm:col-span-2' : 'h-full'}
|
|
92
|
+
>
|
|
93
|
+
<label
|
|
94
|
+
className={`flex h-full cursor-pointer items-start gap-2 rounded-md border px-3 py-2 text-[12px] ${
|
|
95
|
+
choice === option.id
|
|
96
|
+
? 'border-accent bg-accent/5 text-ink'
|
|
97
|
+
: 'border-border bg-surface text-ink'
|
|
98
|
+
}`}
|
|
99
|
+
>
|
|
100
|
+
<input
|
|
101
|
+
type="radio"
|
|
102
|
+
name="organisation-type"
|
|
103
|
+
value={option.id}
|
|
104
|
+
checked={choice === option.id}
|
|
105
|
+
disabled={seedHandbook.isPending}
|
|
106
|
+
onChange={() => {
|
|
107
|
+
setChoice(option.id)
|
|
108
|
+
}}
|
|
109
|
+
className="mt-0.5 h-4 w-4 border-border text-accent focus:ring-accent disabled:opacity-50"
|
|
110
|
+
/>
|
|
111
|
+
<span>
|
|
112
|
+
<span className="font-medium">{option.label}</span>
|
|
113
|
+
<span className="mt-0.5 block text-[11px] text-ink-muted">{option.description}</span>
|
|
114
|
+
</span>
|
|
115
|
+
</label>
|
|
116
|
+
</li>
|
|
117
|
+
))}
|
|
118
|
+
</ul>
|
|
119
|
+
|
|
120
|
+
{seedHandbook.error !== null && <ErrorPanel error={seedHandbook.error} />}
|
|
121
|
+
<OnboardingNav
|
|
122
|
+
step={2}
|
|
123
|
+
nextLabel="Next"
|
|
124
|
+
nextPendingLabel="Preparing handbook…"
|
|
125
|
+
isPending={seedHandbook.isPending}
|
|
126
|
+
/>
|
|
127
|
+
</form>
|
|
128
|
+
</AuthLayout>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
import type { FormEvent } from 'react'
|
|
3
|
-
import { Link, Navigate, useNavigate } from 'react-router'
|
|
3
|
+
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router'
|
|
4
4
|
|
|
5
|
-
import { useInstallSampleData } from '../../api/resources/sampleData.ts'
|
|
6
5
|
import { useCreateWorkspace, useSession } from '../../api/resources/session.ts'
|
|
7
6
|
import { ErrorPanel } from '../../components/QueryState.tsx'
|
|
8
|
-
import {
|
|
7
|
+
import { TextField } from '../auth/AuthForm.tsx'
|
|
9
8
|
import { AuthLayout } from '../auth/AuthLayout.tsx'
|
|
9
|
+
import { OnboardingNav } from './OnboardingNav.tsx'
|
|
10
|
+
import { isOnboardingRerun, ONBOARDING_ORG_PARAM, onboardingPath } from './onboardingRerun.ts'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Onboarding step 1: the workspace, against `POST /v1/workspaces`.
|
|
@@ -15,9 +16,9 @@ import { AuthLayout } from '../auth/AuthLayout.tsx'
|
|
|
15
16
|
* no workspace, so until this succeeds every CRM endpoint answers `403`, and
|
|
16
17
|
* `SessionGate` sends anyone in that state here.
|
|
17
18
|
*
|
|
18
|
-
* One request
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* One request creates the workspace, seeds pipeline stages, and moves this
|
|
20
|
+
* session into it. The starter handbook is seeded on step 4 once the reader
|
|
21
|
+
* picks an organisation type.
|
|
21
22
|
*
|
|
22
23
|
* It sits outside `SessionGate`, because being sent here *is* what the gate does
|
|
23
24
|
* with an account that has no workspace, so gating it would loop. That leaves
|
|
@@ -47,14 +48,15 @@ function browserTimezone(): string {
|
|
|
47
48
|
|
|
48
49
|
export function WorkspaceStepPage(): React.JSX.Element {
|
|
49
50
|
const navigate = useNavigate()
|
|
50
|
-
const
|
|
51
|
+
const [searchParams] = useSearchParams()
|
|
52
|
+
const rerun = isOnboardingRerun(searchParams)
|
|
53
|
+
const { isSignedOut, session } = useSession()
|
|
51
54
|
const createWorkspace = useCreateWorkspace()
|
|
52
|
-
const
|
|
55
|
+
const hasWorkspace = session?.workspaceId !== null && session?.workspaceId !== undefined
|
|
53
56
|
const [name, setName] = useState('')
|
|
54
57
|
const [slug, setSlug] = useState('')
|
|
55
58
|
const [slugEdited, setSlugEdited] = useState(false)
|
|
56
59
|
const [timezone] = useState(browserTimezone)
|
|
57
|
-
const [seedSample, setSeedSample] = useState(false)
|
|
58
60
|
|
|
59
61
|
function changeName(value: string): void {
|
|
60
62
|
setName(value)
|
|
@@ -74,17 +76,8 @@ export function WorkspaceStepPage(): React.JSX.Element {
|
|
|
74
76
|
|
|
75
77
|
createWorkspace
|
|
76
78
|
.runAsync({ name: name.trim(), slug: slug.trim(), timezone })
|
|
77
|
-
.then(
|
|
78
|
-
|
|
79
|
-
// A refusal here does not block the wizard: the workspace itself is
|
|
80
|
-
// already created, and the invites step is what comes next either
|
|
81
|
-
// way. The error stays on screen so the reader sees what failed.
|
|
82
|
-
await installSampleData
|
|
83
|
-
.runAsync({ workspaceId: workspace.id })
|
|
84
|
-
.catch(() => undefined)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
navigate('/onboarding/invites', { replace: true })
|
|
79
|
+
.then(() => {
|
|
80
|
+
navigate('/onboarding/organisation', { replace: true })
|
|
88
81
|
})
|
|
89
82
|
.catch(() => undefined)
|
|
90
83
|
}
|
|
@@ -93,6 +86,41 @@ export function WorkspaceStepPage(): React.JSX.Element {
|
|
|
93
86
|
return <Navigate to="/login" replace />
|
|
94
87
|
}
|
|
95
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Previous on a later step lands here after the workspace already exists.
|
|
91
|
+
* Showing the create form again would offer a second POST that this account
|
|
92
|
+
* cannot use. Next is the only action that remains. A rerun keeps the flag
|
|
93
|
+
* so organisation does not seed handbook pages and the review step is skipped.
|
|
94
|
+
*/
|
|
95
|
+
if (hasWorkspace) {
|
|
96
|
+
return (
|
|
97
|
+
<AuthLayout
|
|
98
|
+
step={1}
|
|
99
|
+
title="Your workspace is ready"
|
|
100
|
+
description="Use Next to choose your organisation type and starter handbook."
|
|
101
|
+
footer={
|
|
102
|
+
<Link to="/login" className="font-medium text-accent hover:underline">
|
|
103
|
+
Sign in as somebody else
|
|
104
|
+
</Link>
|
|
105
|
+
}
|
|
106
|
+
>
|
|
107
|
+
<div className="mt-5">
|
|
108
|
+
<OnboardingNav
|
|
109
|
+
step={1}
|
|
110
|
+
nextLabel="Next"
|
|
111
|
+
nextType="button"
|
|
112
|
+
onNext={() => {
|
|
113
|
+
navigate(
|
|
114
|
+
onboardingPath('/onboarding/organisation', rerun, searchParams.get(ONBOARDING_ORG_PARAM)),
|
|
115
|
+
{ replace: true },
|
|
116
|
+
)
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
</AuthLayout>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
96
124
|
return (
|
|
97
125
|
<AuthLayout
|
|
98
126
|
step={1}
|
|
@@ -124,25 +152,12 @@ export function WorkspaceStepPage(): React.JSX.Element {
|
|
|
124
152
|
<p className="text-[11px] text-ink-faint">
|
|
125
153
|
Timezone: {timezone}. Change it later in Admin → Workspace.
|
|
126
154
|
</p>
|
|
127
|
-
<label className="flex items-start gap-2 text-[12px] text-ink">
|
|
128
|
-
<input
|
|
129
|
-
type="checkbox"
|
|
130
|
-
checked={seedSample}
|
|
131
|
-
onChange={(event) => setSeedSample(event.target.checked)}
|
|
132
|
-
className="mt-0.5 h-4 w-4 rounded border-border text-accent focus:ring-accent"
|
|
133
|
-
/>
|
|
134
|
-
<span>
|
|
135
|
-
Install sample data — a small set of companies, people, deals, enquiries, opportunities,
|
|
136
|
-
fundraising, partnerships, and hiring so the workspace has something to look at.
|
|
137
|
-
You can delete it later.
|
|
138
|
-
</span>
|
|
139
|
-
</label>
|
|
140
155
|
{createWorkspace.error !== null && <ErrorPanel error={createWorkspace.error} />}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
isPending={createWorkspace.isPending
|
|
156
|
+
<OnboardingNav
|
|
157
|
+
step={1}
|
|
158
|
+
nextLabel="Next"
|
|
159
|
+
nextPendingLabel="Creating…"
|
|
160
|
+
isPending={createWorkspace.isPending}
|
|
146
161
|
/>
|
|
147
162
|
</form>
|
|
148
163
|
</AuthLayout>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { HandbookTemplateId } from '@kelpie/schemas'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Labels and descriptions for the organisation-type step. Page lists live
|
|
5
|
+
* server-side; `later` maps to the startup (original) handbook template.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type OrganisationChoiceId = HandbookTemplateId | 'later'
|
|
9
|
+
|
|
10
|
+
export interface OrganisationChoice {
|
|
11
|
+
readonly id: OrganisationChoiceId
|
|
12
|
+
readonly label: string
|
|
13
|
+
readonly description: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ORGANISATION_CHOICES: readonly OrganisationChoice[] = [
|
|
17
|
+
{
|
|
18
|
+
id: 'startup',
|
|
19
|
+
label: 'Startup',
|
|
20
|
+
description: 'Product company, SaaS, or tech startup.',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'agency',
|
|
24
|
+
label: 'Agency',
|
|
25
|
+
description: 'Consultancy, design shop, dev shop, or marketing agency.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'nonprofit',
|
|
29
|
+
label: 'Nonprofit',
|
|
30
|
+
description: 'Charity, foundation, or social enterprise.',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'community',
|
|
34
|
+
label: 'Community',
|
|
35
|
+
description: 'Club, association, co-op, or membership organisation.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'professional-services',
|
|
39
|
+
label: 'Professional services',
|
|
40
|
+
description: 'Law, accounting, architecture, or advisory practice.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'creator',
|
|
44
|
+
label: 'Creator',
|
|
45
|
+
description: 'Solo creator, freelancer, or personal brand.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'later',
|
|
49
|
+
label: 'Choose later',
|
|
50
|
+
description: 'Use the standard startup handbook for now. Edit pages any time.',
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
export const DEFAULT_ORGANISATION_CHOICE: OrganisationChoiceId = 'startup'
|
|
55
|
+
|
|
56
|
+
export function isOrganisationChoiceId(value: string): value is OrganisationChoiceId {
|
|
57
|
+
return ORGANISATION_CHOICES.some((option) => option.id === value)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function organisationChoiceFromParam(value: string | null): OrganisationChoiceId {
|
|
61
|
+
return value !== null && isOrganisationChoiceId(value) ? value : DEFAULT_ORGANISATION_CHOICE
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function organisationChoiceLabel(choice: OrganisationChoiceId): string {
|
|
65
|
+
for (const option of ORGANISATION_CHOICES) {
|
|
66
|
+
if (option.id === choice) {
|
|
67
|
+
return option.label
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return 'Startup'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** `later` seeds the original startup pages. */
|
|
75
|
+
export function handbookTemplateForChoice(choice: OrganisationChoiceId): HandbookTemplateId {
|
|
76
|
+
return choice === 'later' ? 'startup' : choice
|
|
77
|
+
}
|