@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.
Files changed (333) hide show
  1. package/dist/api/resources/attendances.d.ts +13 -0
  2. package/dist/api/resources/attendances.d.ts.map +1 -0
  3. package/dist/api/resources/attendances.js +34 -0
  4. package/dist/api/resources/attendances.js.map +1 -0
  5. package/dist/api/resources/eventAssociations.d.ts +12 -0
  6. package/dist/api/resources/eventAssociations.d.ts.map +1 -0
  7. package/dist/api/resources/eventAssociations.js +25 -0
  8. package/dist/api/resources/eventAssociations.js.map +1 -0
  9. package/dist/api/resources/events.d.ts +17 -0
  10. package/dist/api/resources/events.d.ts.map +1 -0
  11. package/dist/api/resources/events.js +40 -0
  12. package/dist/api/resources/events.js.map +1 -0
  13. package/dist/api/resources/handbookSeed.d.ts +11 -0
  14. package/dist/api/resources/handbookSeed.d.ts.map +1 -0
  15. package/dist/api/resources/handbookSeed.js +43 -0
  16. package/dist/api/resources/handbookSeed.js.map +1 -0
  17. package/dist/api/resources/moduleSettings.d.ts +10 -0
  18. package/dist/api/resources/moduleSettings.d.ts.map +1 -1
  19. package/dist/api/resources/moduleSettings.js +21 -0
  20. package/dist/api/resources/moduleSettings.js.map +1 -1
  21. package/dist/api/resources/planItems.d.ts +4 -4
  22. package/dist/api/resources/planItems.d.ts.map +1 -1
  23. package/dist/api/resources/planItems.js +1 -1
  24. package/dist/api/resources/planItems.js.map +1 -1
  25. package/dist/api/resources/publicConfig.js +3 -3
  26. package/dist/api/resources/sampleData.d.ts.map +1 -1
  27. package/dist/api/resources/sampleData.js +2 -0
  28. package/dist/api/resources/sampleData.js.map +1 -1
  29. package/dist/api/resources/session.d.ts +22 -1
  30. package/dist/api/resources/session.d.ts.map +1 -1
  31. package/dist/api/resources/session.js +43 -1
  32. package/dist/api/resources/session.js.map +1 -1
  33. package/dist/api/resources/workspace.d.ts.map +1 -1
  34. package/dist/api/resources/workspace.js +2 -3
  35. package/dist/api/resources/workspace.js.map +1 -1
  36. package/dist/app/KelpieApp.d.ts.map +1 -1
  37. package/dist/app/KelpieApp.js +5 -1
  38. package/dist/app/KelpieApp.js.map +1 -1
  39. package/dist/components/ActivitiesPanel.d.ts.map +1 -1
  40. package/dist/components/ActivitiesPanel.js +2 -0
  41. package/dist/components/ActivitiesPanel.js.map +1 -1
  42. package/dist/components/AddressesField.d.ts +23 -0
  43. package/dist/components/AddressesField.d.ts.map +1 -0
  44. package/dist/components/AddressesField.js +215 -0
  45. package/dist/components/AddressesField.js.map +1 -0
  46. package/dist/components/CountrySearch.d.ts +16 -0
  47. package/dist/components/CountrySearch.d.ts.map +1 -0
  48. package/dist/components/CountrySearch.js +30 -0
  49. package/dist/components/CountrySearch.js.map +1 -0
  50. package/dist/components/EnvironmentBanner.d.ts +0 -12
  51. package/dist/components/EnvironmentBanner.d.ts.map +1 -1
  52. package/dist/components/EnvironmentBanner.js +21 -6
  53. package/dist/components/EnvironmentBanner.js.map +1 -1
  54. package/dist/components/InlineEdit.d.ts +7 -1
  55. package/dist/components/InlineEdit.d.ts.map +1 -1
  56. package/dist/components/InlineEdit.js +8 -1
  57. package/dist/components/InlineEdit.js.map +1 -1
  58. package/dist/components/LinkedEventsPanel.d.ts +11 -0
  59. package/dist/components/LinkedEventsPanel.d.ts.map +1 -0
  60. package/dist/components/LinkedEventsPanel.js +18 -0
  61. package/dist/components/LinkedEventsPanel.js.map +1 -0
  62. package/dist/components/NotesPanel.d.ts +4 -6
  63. package/dist/components/NotesPanel.d.ts.map +1 -1
  64. package/dist/components/NotesPanel.js +39 -3
  65. package/dist/components/NotesPanel.js.map +1 -1
  66. package/dist/components/PlanAttention.d.ts.map +1 -1
  67. package/dist/components/PlanAttention.js +3 -2
  68. package/dist/components/PlanAttention.js.map +1 -1
  69. package/dist/components/PlanPanel.d.ts +3 -3
  70. package/dist/components/PlanPanel.d.ts.map +1 -1
  71. package/dist/components/Shell.d.ts.map +1 -1
  72. package/dist/components/Shell.js +8 -2
  73. package/dist/components/Shell.js.map +1 -1
  74. package/dist/components/SocialNetworkIcon.d.ts +17 -0
  75. package/dist/components/SocialNetworkIcon.d.ts.map +1 -0
  76. package/dist/components/SocialNetworkIcon.js +39 -0
  77. package/dist/components/SocialNetworkIcon.js.map +1 -0
  78. package/dist/components/SocialProfilesField.d.ts +8 -0
  79. package/dist/components/SocialProfilesField.d.ts.map +1 -1
  80. package/dist/components/SocialProfilesField.js +19 -5
  81. package/dist/components/SocialProfilesField.js.map +1 -1
  82. package/dist/components/TimezoneSearch.d.ts +21 -0
  83. package/dist/components/TimezoneSearch.d.ts.map +1 -0
  84. package/dist/components/TimezoneSearch.js +27 -0
  85. package/dist/components/TimezoneSearch.js.map +1 -0
  86. package/dist/components/WorkspaceSwitcher.d.ts +8 -0
  87. package/dist/components/WorkspaceSwitcher.d.ts.map +1 -0
  88. package/dist/components/WorkspaceSwitcher.js +67 -0
  89. package/dist/components/WorkspaceSwitcher.js.map +1 -0
  90. package/dist/components/environmentTone.d.ts +10 -0
  91. package/dist/components/environmentTone.d.ts.map +1 -0
  92. package/dist/components/environmentTone.js +35 -0
  93. package/dist/components/environmentTone.js.map +1 -0
  94. package/dist/index.d.ts +25 -6
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +17 -3
  97. package/dist/index.js.map +1 -1
  98. package/dist/lib/timezones.d.ts +17 -0
  99. package/dist/lib/timezones.d.ts.map +1 -0
  100. package/dist/lib/timezones.js +39 -0
  101. package/dist/lib/timezones.js.map +1 -0
  102. package/dist/pages/CompaniesPage.d.ts.map +1 -1
  103. package/dist/pages/CompaniesPage.js +12 -11
  104. package/dist/pages/CompaniesPage.js.map +1 -1
  105. package/dist/pages/CompanyDetail.d.ts.map +1 -1
  106. package/dist/pages/CompanyDetail.js +23 -12
  107. package/dist/pages/CompanyDetail.js.map +1 -1
  108. package/dist/pages/DealDetail.d.ts.map +1 -1
  109. package/dist/pages/DealDetail.js +5 -1
  110. package/dist/pages/DealDetail.js.map +1 -1
  111. package/dist/pages/DecisionsPage.d.ts.map +1 -1
  112. package/dist/pages/DecisionsPage.js +7 -0
  113. package/dist/pages/DecisionsPage.js.map +1 -1
  114. package/dist/pages/EnquiryDetail.d.ts.map +1 -1
  115. package/dist/pages/EnquiryDetail.js +5 -1
  116. package/dist/pages/EnquiryDetail.js.map +1 -1
  117. package/dist/pages/EventDetail.d.ts +2 -0
  118. package/dist/pages/EventDetail.d.ts.map +1 -0
  119. package/dist/pages/EventDetail.js +332 -0
  120. package/dist/pages/EventDetail.js.map +1 -0
  121. package/dist/pages/EventsPage.d.ts +2 -0
  122. package/dist/pages/EventsPage.d.ts.map +1 -0
  123. package/dist/pages/EventsPage.js +176 -0
  124. package/dist/pages/EventsPage.js.map +1 -0
  125. package/dist/pages/ListDetail.d.ts.map +1 -1
  126. package/dist/pages/ListDetail.js +17 -0
  127. package/dist/pages/ListDetail.js.map +1 -1
  128. package/dist/pages/OpportunityDetail.d.ts.map +1 -1
  129. package/dist/pages/OpportunityDetail.js +5 -1
  130. package/dist/pages/OpportunityDetail.js.map +1 -1
  131. package/dist/pages/PartnershipDetail.d.ts.map +1 -1
  132. package/dist/pages/PartnershipDetail.js +5 -1
  133. package/dist/pages/PartnershipDetail.js.map +1 -1
  134. package/dist/pages/PeoplePage.d.ts.map +1 -1
  135. package/dist/pages/PeoplePage.js +13 -5
  136. package/dist/pages/PeoplePage.js.map +1 -1
  137. package/dist/pages/PersonDetail.d.ts.map +1 -1
  138. package/dist/pages/PersonDetail.js +94 -28
  139. package/dist/pages/PersonDetail.js.map +1 -1
  140. package/dist/pages/PlanningPage.d.ts.map +1 -1
  141. package/dist/pages/PlanningPage.js +6 -3
  142. package/dist/pages/PlanningPage.js.map +1 -1
  143. package/dist/pages/RaiseDetail.d.ts.map +1 -1
  144. package/dist/pages/RaiseDetail.js +5 -1
  145. package/dist/pages/RaiseDetail.js.map +1 -1
  146. package/dist/pages/RoleDetail.d.ts.map +1 -1
  147. package/dist/pages/RoleDetail.js +4 -1
  148. package/dist/pages/RoleDetail.js.map +1 -1
  149. package/dist/pages/SearchPage.d.ts.map +1 -1
  150. package/dist/pages/SearchPage.js +2 -1
  151. package/dist/pages/SearchPage.js.map +1 -1
  152. package/dist/pages/account/PreferencesPage.d.ts.map +1 -1
  153. package/dist/pages/account/PreferencesPage.js +2 -28
  154. package/dist/pages/account/PreferencesPage.js.map +1 -1
  155. package/dist/pages/account/SecurityPage.js +2 -2
  156. package/dist/pages/account/SecurityPage.js.map +1 -1
  157. package/dist/pages/admin/DataPage.js +10 -9
  158. package/dist/pages/admin/DataPage.js.map +1 -1
  159. package/dist/pages/admin/FieldsPage.js +1 -1
  160. package/dist/pages/admin/FieldsPage.js.map +1 -1
  161. package/dist/pages/admin/ModulesPage.d.ts.map +1 -1
  162. package/dist/pages/admin/ModulesPage.js +1 -0
  163. package/dist/pages/admin/ModulesPage.js.map +1 -1
  164. package/dist/pages/admin/WorkspaceSettingsPage.d.ts +12 -0
  165. package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
  166. package/dist/pages/admin/WorkspaceSettingsPage.js +2 -21
  167. package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
  168. package/dist/pages/auth/AuthForm.d.ts +18 -0
  169. package/dist/pages/auth/AuthForm.d.ts.map +1 -1
  170. package/dist/pages/auth/AuthForm.js +17 -0
  171. package/dist/pages/auth/AuthForm.js.map +1 -1
  172. package/dist/pages/auth/AuthLayout.d.ts +5 -4
  173. package/dist/pages/auth/AuthLayout.d.ts.map +1 -1
  174. package/dist/pages/auth/AuthLayout.js +7 -5
  175. package/dist/pages/auth/AuthLayout.js.map +1 -1
  176. package/dist/pages/auth/AuthMethods.d.ts +12 -0
  177. package/dist/pages/auth/AuthMethods.d.ts.map +1 -0
  178. package/dist/pages/auth/AuthMethods.js +20 -0
  179. package/dist/pages/auth/AuthMethods.js.map +1 -0
  180. package/dist/pages/auth/RegionSwitcher.d.ts +10 -0
  181. package/dist/pages/auth/RegionSwitcher.d.ts.map +1 -0
  182. package/dist/pages/auth/RegionSwitcher.js +28 -0
  183. package/dist/pages/auth/RegionSwitcher.js.map +1 -0
  184. package/dist/pages/auth/SignInPage.d.ts +7 -0
  185. package/dist/pages/auth/SignInPage.d.ts.map +1 -1
  186. package/dist/pages/auth/SignInPage.js +5 -8
  187. package/dist/pages/auth/SignInPage.js.map +1 -1
  188. package/dist/pages/auth/SignUpPage.d.ts.map +1 -1
  189. package/dist/pages/auth/SignUpPage.js +9 -1
  190. package/dist/pages/auth/SignUpPage.js.map +1 -1
  191. package/dist/pages/auth/nextPath.d.ts +15 -0
  192. package/dist/pages/auth/nextPath.d.ts.map +1 -0
  193. package/dist/pages/auth/nextPath.js +17 -0
  194. package/dist/pages/auth/nextPath.js.map +1 -0
  195. package/dist/pages/auth/regionHref.d.ts +6 -0
  196. package/dist/pages/auth/regionHref.d.ts.map +1 -0
  197. package/dist/pages/auth/regionHref.js +8 -0
  198. package/dist/pages/auth/regionHref.js.map +1 -0
  199. package/dist/pages/dashboard/DashboardPage.d.ts +8 -0
  200. package/dist/pages/dashboard/DashboardPage.d.ts.map +1 -1
  201. package/dist/pages/dashboard/DashboardPage.js +5 -13
  202. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  203. package/dist/pages/dashboard/attention.d.ts +1 -8
  204. package/dist/pages/dashboard/attention.d.ts.map +1 -1
  205. package/dist/pages/dashboard/attention.js +21 -54
  206. package/dist/pages/dashboard/attention.js.map +1 -1
  207. package/dist/pages/dashboard/sections.d.ts.map +1 -1
  208. package/dist/pages/dashboard/sections.js +3 -2
  209. package/dist/pages/dashboard/sections.js.map +1 -1
  210. package/dist/pages/forms/FormActions.d.ts.map +1 -1
  211. package/dist/pages/forms/FormActions.js +7 -3
  212. package/dist/pages/forms/FormActions.js.map +1 -1
  213. package/dist/pages/onboarding/HandbookStepPage.d.ts +4 -8
  214. package/dist/pages/onboarding/HandbookStepPage.d.ts.map +1 -1
  215. package/dist/pages/onboarding/HandbookStepPage.js +13 -13
  216. package/dist/pages/onboarding/HandbookStepPage.js.map +1 -1
  217. package/dist/pages/onboarding/InvitesStepPage.d.ts.map +1 -1
  218. package/dist/pages/onboarding/InvitesStepPage.js +13 -7
  219. package/dist/pages/onboarding/InvitesStepPage.js.map +1 -1
  220. package/dist/pages/onboarding/ModulesStepPage.d.ts +11 -0
  221. package/dist/pages/onboarding/ModulesStepPage.d.ts.map +1 -0
  222. package/dist/pages/onboarding/ModulesStepPage.js +80 -0
  223. package/dist/pages/onboarding/ModulesStepPage.js.map +1 -0
  224. package/dist/pages/onboarding/OnboardingNav.d.ts +17 -0
  225. package/dist/pages/onboarding/OnboardingNav.d.ts.map +1 -0
  226. package/dist/pages/onboarding/OnboardingNav.js +37 -0
  227. package/dist/pages/onboarding/OnboardingNav.js.map +1 -0
  228. package/dist/pages/onboarding/OrganisationStepPage.d.ts +13 -0
  229. package/dist/pages/onboarding/OrganisationStepPage.d.ts.map +1 -0
  230. package/dist/pages/onboarding/OrganisationStepPage.js +59 -0
  231. package/dist/pages/onboarding/OrganisationStepPage.js.map +1 -0
  232. package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
  233. package/dist/pages/onboarding/WorkspaceStepPage.js +25 -20
  234. package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
  235. package/dist/pages/onboarding/handbookTemplates.d.ts +19 -0
  236. package/dist/pages/onboarding/handbookTemplates.d.ts.map +1 -0
  237. package/dist/pages/onboarding/handbookTemplates.js +57 -0
  238. package/dist/pages/onboarding/handbookTemplates.js.map +1 -0
  239. package/dist/pages/onboarding/onboardingModules.d.ts +23 -0
  240. package/dist/pages/onboarding/onboardingModules.d.ts.map +1 -0
  241. package/dist/pages/onboarding/onboardingModules.js +153 -0
  242. package/dist/pages/onboarding/onboardingModules.js.map +1 -0
  243. package/dist/pages/onboarding/onboardingRerun.d.ts +21 -0
  244. package/dist/pages/onboarding/onboardingRerun.d.ts.map +1 -0
  245. package/dist/pages/onboarding/onboardingRerun.js +33 -0
  246. package/dist/pages/onboarding/onboardingRerun.js.map +1 -0
  247. package/dist/registry/context.d.ts +3 -1
  248. package/dist/registry/context.d.ts.map +1 -1
  249. package/dist/registry/context.js +4 -0
  250. package/dist/registry/context.js.map +1 -1
  251. package/dist/registry/contributions.d.ts +24 -0
  252. package/dist/registry/contributions.d.ts.map +1 -1
  253. package/dist/registry/contributions.js.map +1 -1
  254. package/dist/registry/registry.d.ts +4 -1
  255. package/dist/registry/registry.d.ts.map +1 -1
  256. package/dist/registry/registry.js +6 -0
  257. package/dist/registry/registry.js.map +1 -1
  258. package/package.json +4 -3
  259. package/src/api/resources/attendances.ts +54 -0
  260. package/src/api/resources/eventAssociations.ts +43 -0
  261. package/src/api/resources/events.ts +70 -0
  262. package/src/api/resources/handbookSeed.ts +63 -0
  263. package/src/api/resources/moduleSettings.ts +25 -0
  264. package/src/api/resources/planItems.ts +5 -5
  265. package/src/api/resources/publicConfig.ts +3 -3
  266. package/src/api/resources/sampleData.ts +2 -0
  267. package/src/api/resources/session.ts +59 -0
  268. package/src/api/resources/workspace.ts +2 -3
  269. package/src/app/KelpieApp.tsx +11 -3
  270. package/src/components/ActivitiesPanel.tsx +2 -0
  271. package/src/components/AddressesField.tsx +502 -0
  272. package/src/components/CountrySearch.tsx +71 -0
  273. package/src/components/EnvironmentBanner.tsx +23 -6
  274. package/src/components/InlineEdit.tsx +35 -1
  275. package/src/components/LinkedEventsPanel.tsx +50 -0
  276. package/src/components/NotesPanel.tsx +132 -15
  277. package/src/components/PlanAttention.tsx +5 -4
  278. package/src/components/PlanPanel.tsx +3 -3
  279. package/src/components/Shell.tsx +16 -1
  280. package/src/components/SocialNetworkIcon.tsx +88 -0
  281. package/src/components/SocialProfilesField.tsx +57 -10
  282. package/src/components/TimezoneSearch.tsx +67 -0
  283. package/src/components/WorkspaceSwitcher.tsx +136 -0
  284. package/src/components/environmentTone.ts +40 -0
  285. package/src/index.ts +49 -3
  286. package/src/lib/timezones.ts +43 -0
  287. package/src/pages/CompaniesPage.tsx +14 -13
  288. package/src/pages/CompanyDetail.tsx +59 -27
  289. package/src/pages/DealDetail.tsx +5 -0
  290. package/src/pages/DecisionsPage.tsx +7 -0
  291. package/src/pages/EnquiryDetail.tsx +7 -0
  292. package/src/pages/EventDetail.tsx +794 -0
  293. package/src/pages/EventsPage.tsx +382 -0
  294. package/src/pages/ListDetail.tsx +24 -0
  295. package/src/pages/OpportunityDetail.tsx +7 -0
  296. package/src/pages/PartnershipDetail.tsx +7 -0
  297. package/src/pages/PeoplePage.tsx +18 -5
  298. package/src/pages/PersonDetail.tsx +300 -100
  299. package/src/pages/PlanningPage.tsx +17 -14
  300. package/src/pages/RaiseDetail.tsx +5 -0
  301. package/src/pages/RoleDetail.tsx +8 -0
  302. package/src/pages/SearchPage.tsx +2 -1
  303. package/src/pages/account/PreferencesPage.tsx +2 -37
  304. package/src/pages/account/SecurityPage.tsx +6 -1
  305. package/src/pages/admin/DataPage.tsx +63 -21
  306. package/src/pages/admin/FieldsPage.tsx +1 -1
  307. package/src/pages/admin/ModulesPage.tsx +1 -0
  308. package/src/pages/admin/WorkspaceSettingsPage.tsx +10 -33
  309. package/src/pages/auth/AuthForm.tsx +38 -0
  310. package/src/pages/auth/AuthLayout.tsx +8 -4
  311. package/src/pages/auth/AuthMethods.tsx +29 -0
  312. package/src/pages/auth/RegionSwitcher.tsx +56 -0
  313. package/src/pages/auth/SignInPage.tsx +13 -14
  314. package/src/pages/auth/SignUpPage.tsx +32 -0
  315. package/src/pages/auth/nextPath.ts +17 -0
  316. package/src/pages/auth/regionHref.ts +10 -0
  317. package/src/pages/dashboard/DashboardPage.tsx +16 -48
  318. package/src/pages/dashboard/attention.ts +24 -78
  319. package/src/pages/dashboard/sections.tsx +8 -2
  320. package/src/pages/forms/FormActions.tsx +14 -8
  321. package/src/pages/onboarding/HandbookStepPage.tsx +21 -24
  322. package/src/pages/onboarding/InvitesStepPage.tsx +27 -19
  323. package/src/pages/onboarding/ModulesStepPage.tsx +145 -0
  324. package/src/pages/onboarding/OnboardingNav.tsx +97 -0
  325. package/src/pages/onboarding/OrganisationStepPage.tsx +130 -0
  326. package/src/pages/onboarding/WorkspaceStepPage.tsx +53 -38
  327. package/src/pages/onboarding/handbookTemplates.ts +77 -0
  328. package/src/pages/onboarding/onboardingModules.ts +202 -0
  329. package/src/pages/onboarding/onboardingRerun.ts +41 -0
  330. package/src/registry/context.ts +6 -0
  331. package/src/registry/contributions.ts +26 -0
  332. package/src/registry/registry.ts +12 -0
  333. 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 { SubmitButton, TextField } from '../auth/AuthForm.tsx'
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 does the whole thing: the service seeds the starter handbook and
19
- * the pipeline stages in the same transaction and moves this session into the
20
- * new workspace. There is nothing to re-login for and nothing to create next.
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 { isSignedOut } = useSession()
51
+ const [searchParams] = useSearchParams()
52
+ const rerun = isOnboardingRerun(searchParams)
53
+ const { isSignedOut, session } = useSession()
51
54
  const createWorkspace = useCreateWorkspace()
52
- const installSampleData = useInstallSampleData()
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(async (workspace) => {
78
- if (seedSample) {
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
- {installSampleData.error !== null && <ErrorPanel error={installSampleData.error} />}
142
- <SubmitButton
143
- label="Continue"
144
- pendingLabel={installSampleData.isPending ? 'Installing sample data…' : 'Creating…'}
145
- isPending={createWorkspace.isPending || installSampleData.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
+ }