@kelpie/ui 0.12.0 → 0.14.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 (300) 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 +21 -4
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +12 -2
  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/admin/DataPage.js +10 -9
  156. package/dist/pages/admin/DataPage.js.map +1 -1
  157. package/dist/pages/admin/FieldsPage.js +1 -1
  158. package/dist/pages/admin/FieldsPage.js.map +1 -1
  159. package/dist/pages/admin/ModulesPage.d.ts.map +1 -1
  160. package/dist/pages/admin/ModulesPage.js +1 -0
  161. package/dist/pages/admin/ModulesPage.js.map +1 -1
  162. package/dist/pages/admin/WorkspaceSettingsPage.d.ts +12 -0
  163. package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
  164. package/dist/pages/admin/WorkspaceSettingsPage.js +2 -21
  165. package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
  166. package/dist/pages/auth/AuthLayout.d.ts +5 -4
  167. package/dist/pages/auth/AuthLayout.d.ts.map +1 -1
  168. package/dist/pages/auth/AuthLayout.js +7 -5
  169. package/dist/pages/auth/AuthLayout.js.map +1 -1
  170. package/dist/pages/auth/RegionSwitcher.d.ts +10 -0
  171. package/dist/pages/auth/RegionSwitcher.d.ts.map +1 -0
  172. package/dist/pages/auth/RegionSwitcher.js +28 -0
  173. package/dist/pages/auth/RegionSwitcher.js.map +1 -0
  174. package/dist/pages/auth/SignInPage.d.ts.map +1 -1
  175. package/dist/pages/auth/SignInPage.js +3 -1
  176. package/dist/pages/auth/SignInPage.js.map +1 -1
  177. package/dist/pages/auth/SignUpPage.d.ts.map +1 -1
  178. package/dist/pages/auth/SignUpPage.js +7 -0
  179. package/dist/pages/auth/SignUpPage.js.map +1 -1
  180. package/dist/pages/auth/regionHref.d.ts +6 -0
  181. package/dist/pages/auth/regionHref.d.ts.map +1 -0
  182. package/dist/pages/auth/regionHref.js +8 -0
  183. package/dist/pages/auth/regionHref.js.map +1 -0
  184. package/dist/pages/dashboard/DashboardPage.d.ts +8 -0
  185. package/dist/pages/dashboard/DashboardPage.d.ts.map +1 -1
  186. package/dist/pages/dashboard/DashboardPage.js +5 -13
  187. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  188. package/dist/pages/dashboard/attention.d.ts +1 -8
  189. package/dist/pages/dashboard/attention.d.ts.map +1 -1
  190. package/dist/pages/dashboard/attention.js +21 -54
  191. package/dist/pages/dashboard/attention.js.map +1 -1
  192. package/dist/pages/dashboard/sections.d.ts.map +1 -1
  193. package/dist/pages/dashboard/sections.js +3 -2
  194. package/dist/pages/dashboard/sections.js.map +1 -1
  195. package/dist/pages/forms/FormActions.d.ts.map +1 -1
  196. package/dist/pages/forms/FormActions.js +7 -3
  197. package/dist/pages/forms/FormActions.js.map +1 -1
  198. package/dist/pages/onboarding/HandbookStepPage.d.ts +4 -8
  199. package/dist/pages/onboarding/HandbookStepPage.d.ts.map +1 -1
  200. package/dist/pages/onboarding/HandbookStepPage.js +13 -13
  201. package/dist/pages/onboarding/HandbookStepPage.js.map +1 -1
  202. package/dist/pages/onboarding/InvitesStepPage.d.ts.map +1 -1
  203. package/dist/pages/onboarding/InvitesStepPage.js +13 -7
  204. package/dist/pages/onboarding/InvitesStepPage.js.map +1 -1
  205. package/dist/pages/onboarding/ModulesStepPage.d.ts +11 -0
  206. package/dist/pages/onboarding/ModulesStepPage.d.ts.map +1 -0
  207. package/dist/pages/onboarding/ModulesStepPage.js +80 -0
  208. package/dist/pages/onboarding/ModulesStepPage.js.map +1 -0
  209. package/dist/pages/onboarding/OnboardingNav.d.ts +17 -0
  210. package/dist/pages/onboarding/OnboardingNav.d.ts.map +1 -0
  211. package/dist/pages/onboarding/OnboardingNav.js +37 -0
  212. package/dist/pages/onboarding/OnboardingNav.js.map +1 -0
  213. package/dist/pages/onboarding/OrganisationStepPage.d.ts +13 -0
  214. package/dist/pages/onboarding/OrganisationStepPage.d.ts.map +1 -0
  215. package/dist/pages/onboarding/OrganisationStepPage.js +59 -0
  216. package/dist/pages/onboarding/OrganisationStepPage.js.map +1 -0
  217. package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
  218. package/dist/pages/onboarding/WorkspaceStepPage.js +25 -20
  219. package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
  220. package/dist/pages/onboarding/handbookTemplates.d.ts +19 -0
  221. package/dist/pages/onboarding/handbookTemplates.d.ts.map +1 -0
  222. package/dist/pages/onboarding/handbookTemplates.js +57 -0
  223. package/dist/pages/onboarding/handbookTemplates.js.map +1 -0
  224. package/dist/pages/onboarding/onboardingModules.d.ts +23 -0
  225. package/dist/pages/onboarding/onboardingModules.d.ts.map +1 -0
  226. package/dist/pages/onboarding/onboardingModules.js +153 -0
  227. package/dist/pages/onboarding/onboardingModules.js.map +1 -0
  228. package/dist/pages/onboarding/onboardingRerun.d.ts +21 -0
  229. package/dist/pages/onboarding/onboardingRerun.d.ts.map +1 -0
  230. package/dist/pages/onboarding/onboardingRerun.js +33 -0
  231. package/dist/pages/onboarding/onboardingRerun.js.map +1 -0
  232. package/package.json +4 -3
  233. package/src/api/resources/attendances.ts +54 -0
  234. package/src/api/resources/eventAssociations.ts +43 -0
  235. package/src/api/resources/events.ts +70 -0
  236. package/src/api/resources/handbookSeed.ts +63 -0
  237. package/src/api/resources/moduleSettings.ts +25 -0
  238. package/src/api/resources/planItems.ts +5 -5
  239. package/src/api/resources/publicConfig.ts +3 -3
  240. package/src/api/resources/sampleData.ts +2 -0
  241. package/src/api/resources/session.ts +59 -0
  242. package/src/api/resources/workspace.ts +2 -3
  243. package/src/app/KelpieApp.tsx +11 -3
  244. package/src/components/ActivitiesPanel.tsx +2 -0
  245. package/src/components/AddressesField.tsx +502 -0
  246. package/src/components/CountrySearch.tsx +71 -0
  247. package/src/components/EnvironmentBanner.tsx +23 -6
  248. package/src/components/InlineEdit.tsx +35 -1
  249. package/src/components/LinkedEventsPanel.tsx +50 -0
  250. package/src/components/NotesPanel.tsx +132 -15
  251. package/src/components/PlanAttention.tsx +5 -4
  252. package/src/components/PlanPanel.tsx +3 -3
  253. package/src/components/Shell.tsx +16 -1
  254. package/src/components/SocialNetworkIcon.tsx +88 -0
  255. package/src/components/SocialProfilesField.tsx +57 -10
  256. package/src/components/TimezoneSearch.tsx +67 -0
  257. package/src/components/WorkspaceSwitcher.tsx +136 -0
  258. package/src/components/environmentTone.ts +40 -0
  259. package/src/index.ts +42 -3
  260. package/src/lib/timezones.ts +43 -0
  261. package/src/pages/CompaniesPage.tsx +14 -13
  262. package/src/pages/CompanyDetail.tsx +59 -27
  263. package/src/pages/DealDetail.tsx +5 -0
  264. package/src/pages/DecisionsPage.tsx +7 -0
  265. package/src/pages/EnquiryDetail.tsx +7 -0
  266. package/src/pages/EventDetail.tsx +794 -0
  267. package/src/pages/EventsPage.tsx +382 -0
  268. package/src/pages/ListDetail.tsx +24 -0
  269. package/src/pages/OpportunityDetail.tsx +7 -0
  270. package/src/pages/PartnershipDetail.tsx +7 -0
  271. package/src/pages/PeoplePage.tsx +18 -5
  272. package/src/pages/PersonDetail.tsx +300 -100
  273. package/src/pages/PlanningPage.tsx +17 -14
  274. package/src/pages/RaiseDetail.tsx +5 -0
  275. package/src/pages/RoleDetail.tsx +8 -0
  276. package/src/pages/SearchPage.tsx +2 -1
  277. package/src/pages/account/PreferencesPage.tsx +2 -37
  278. package/src/pages/admin/DataPage.tsx +63 -21
  279. package/src/pages/admin/FieldsPage.tsx +1 -1
  280. package/src/pages/admin/ModulesPage.tsx +1 -0
  281. package/src/pages/admin/WorkspaceSettingsPage.tsx +10 -33
  282. package/src/pages/auth/AuthLayout.tsx +8 -4
  283. package/src/pages/auth/RegionSwitcher.tsx +56 -0
  284. package/src/pages/auth/SignInPage.tsx +10 -6
  285. package/src/pages/auth/SignUpPage.tsx +25 -0
  286. package/src/pages/auth/regionHref.ts +10 -0
  287. package/src/pages/dashboard/DashboardPage.tsx +16 -48
  288. package/src/pages/dashboard/attention.ts +24 -78
  289. package/src/pages/dashboard/sections.tsx +8 -2
  290. package/src/pages/forms/FormActions.tsx +14 -8
  291. package/src/pages/onboarding/HandbookStepPage.tsx +21 -24
  292. package/src/pages/onboarding/InvitesStepPage.tsx +27 -19
  293. package/src/pages/onboarding/ModulesStepPage.tsx +145 -0
  294. package/src/pages/onboarding/OnboardingNav.tsx +97 -0
  295. package/src/pages/onboarding/OrganisationStepPage.tsx +130 -0
  296. package/src/pages/onboarding/WorkspaceStepPage.tsx +53 -38
  297. package/src/pages/onboarding/handbookTemplates.ts +77 -0
  298. package/src/pages/onboarding/onboardingModules.ts +202 -0
  299. package/src/pages/onboarding/onboardingRerun.ts +41 -0
  300. package/src/styles.css +30 -0
@@ -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
+ }
@@ -0,0 +1,202 @@
1
+ import type { OrganisationChoiceId } from './handbookTemplates.ts'
2
+
3
+ /**
4
+ * The six optional modules the setup wizard offers. Other toggleable modules
5
+ * stay on; Admin → Modules is where they change later.
6
+ *
7
+ * Ids match the server module catalog (`deals`, `raises`, …). The wizard is
8
+ * what writes the first `workspace_module_settings` rows for these six.
9
+ *
10
+ * Labels stay the domain names. Defaults and explanations follow the
11
+ * organisation type chosen on the previous step (`onboarding.md`).
12
+ */
13
+
14
+ export type OnboardingModuleId =
15
+ | 'deals'
16
+ | 'opportunities'
17
+ | 'raises'
18
+ | 'partnerships'
19
+ | 'events'
20
+ | 'forms'
21
+
22
+ export interface OnboardingModuleChoice {
23
+ readonly id: OnboardingModuleId
24
+ readonly label: string
25
+ readonly description: string
26
+ readonly defaultEnabled: boolean
27
+ }
28
+
29
+ interface ModulePreset {
30
+ readonly enabled: Readonly<Record<OnboardingModuleId, boolean>>
31
+ readonly descriptions: Readonly<Record<OnboardingModuleId, string>>
32
+ }
33
+
34
+ const LABELS: Readonly<Record<OnboardingModuleId, string>> = {
35
+ deals: 'Deals',
36
+ opportunities: 'Opportunities',
37
+ raises: 'Fundraising',
38
+ partnerships: 'Partnerships',
39
+ events: 'Events',
40
+ forms: 'Forms',
41
+ }
42
+
43
+ const MODULE_ORDER: readonly OnboardingModuleId[] = [
44
+ 'deals',
45
+ 'opportunities',
46
+ 'raises',
47
+ 'partnerships',
48
+ 'events',
49
+ 'forms',
50
+ ]
51
+
52
+ /** Startup / Choose later — every optional module starts on. */
53
+ const STARTUP: ModulePreset = {
54
+ enabled: {
55
+ deals: true,
56
+ opportunities: true,
57
+ raises: true,
58
+ partnerships: true,
59
+ events: true,
60
+ forms: true,
61
+ },
62
+ descriptions: {
63
+ deals: 'Track sales you are trying to close, from first chat to won or lost.',
64
+ opportunities: 'Chase things that are not sales — grants, awards, press, a speaking slot.',
65
+ raises: 'Run a funding round: who you are talking to, and where each one is up to.',
66
+ partnerships: 'Keep tabs on the people you work with long-term, and when to check in next.',
67
+ events: 'Plan meetups, dinners, and webinars, and see who is coming.',
68
+ forms: 'Put a form on your site. New sign-ups land in Kelpie on their own.',
69
+ },
70
+ }
71
+
72
+ const AGENCY: ModulePreset = {
73
+ enabled: {
74
+ deals: true,
75
+ opportunities: false,
76
+ raises: false,
77
+ partnerships: true,
78
+ events: true,
79
+ forms: true,
80
+ },
81
+ descriptions: {
82
+ deals: 'Track pitches, retainers, and projects you are trying to close.',
83
+ opportunities: 'Chase RFPs, awards, press, and speaking slots that are not billed work.',
84
+ raises: 'Run a funding round: who you are talking to, and where each one is up to.',
85
+ partnerships: 'Keep tabs on subcontractors, channel partners, and when to check in next.',
86
+ events: 'Plan workshops, client dinners, and webinars, and see who is coming.',
87
+ forms: 'Put a form on your site. New enquiries land in Kelpie on their own.',
88
+ },
89
+ }
90
+
91
+ const NONPROFIT: ModulePreset = {
92
+ enabled: {
93
+ deals: false,
94
+ opportunities: true,
95
+ raises: true,
96
+ partnerships: true,
97
+ events: true,
98
+ forms: true,
99
+ },
100
+ descriptions: {
101
+ deals: 'Track sponsorships or earned-income work you are trying to close.',
102
+ opportunities: 'Chase grants, awards, press, and speaking slots.',
103
+ raises: 'Track who you are raising from, and where each conversation is up to.',
104
+ partnerships: 'Keep tabs on the organisations you work with long-term, and when to check in next.',
105
+ events: 'Plan fundraisers, volunteer days, and community gatherings, and see who is coming.',
106
+ forms: 'Put a form on your site. New supporters land in Kelpie on their own.',
107
+ },
108
+ }
109
+
110
+ const COMMUNITY: ModulePreset = {
111
+ enabled: {
112
+ deals: false,
113
+ opportunities: true,
114
+ raises: true,
115
+ partnerships: true,
116
+ events: true,
117
+ forms: true,
118
+ },
119
+ descriptions: {
120
+ deals: 'Track sponsorships or paid programmes you are trying to close.',
121
+ opportunities: 'Chase grants, press, and speaking slots that are not membership sales.',
122
+ raises: 'Track who you are raising from, and where each conversation is up to.',
123
+ partnerships: 'Keep tabs on sister organisations and long-term collaborators, and when to check in next.',
124
+ events: 'Plan meetups, dinners, and member gatherings, and see who is coming.',
125
+ forms: 'Put a join or contact form on your site. New sign-ups land in Kelpie on their own.',
126
+ },
127
+ }
128
+
129
+ const PROFESSIONAL_SERVICES: ModulePreset = {
130
+ enabled: {
131
+ deals: true,
132
+ opportunities: false,
133
+ raises: false,
134
+ partnerships: false,
135
+ events: true,
136
+ forms: true,
137
+ },
138
+ descriptions: {
139
+ deals: 'Track engagements from first conversation to won or lost.',
140
+ opportunities: 'Chase tenders, panels, press, and speaking slots.',
141
+ raises: 'Run a funding round: who you are talking to, and where each one is up to.',
142
+ partnerships: 'Keep tabs on referral sources and when to check in next.',
143
+ events: 'Plan client seminars, dinners, and webinars, and see who is coming.',
144
+ forms: 'Put a form on your site. New enquiries land in Kelpie on their own.',
145
+ },
146
+ }
147
+
148
+ const CREATOR: ModulePreset = {
149
+ enabled: {
150
+ deals: true,
151
+ opportunities: true,
152
+ raises: false,
153
+ partnerships: false,
154
+ events: false,
155
+ forms: true,
156
+ },
157
+ descriptions: {
158
+ deals: 'Track brand deals and client work from first chat to won or lost.',
159
+ opportunities: 'Chase press, speaking, and other chances that are not billed work.',
160
+ raises: 'Run a funding round: who you are talking to, and where each one is up to.',
161
+ partnerships: 'Keep tabs on the brands you work with long-term, and when to check in next.',
162
+ events: 'Plan meetups, launches, and webinars, and see who is coming.',
163
+ forms: 'Put a form on your site. New enquiries land in Kelpie on their own.',
164
+ },
165
+ }
166
+
167
+ const PRESETS: Readonly<Record<Exclude<OrganisationChoiceId, 'later'>, ModulePreset>> = {
168
+ startup: STARTUP,
169
+ agency: AGENCY,
170
+ nonprofit: NONPROFIT,
171
+ community: COMMUNITY,
172
+ 'professional-services': PROFESSIONAL_SERVICES,
173
+ creator: CREATOR,
174
+ }
175
+
176
+ function presetForChoice(choice: OrganisationChoiceId): ModulePreset {
177
+ return choice === 'later' ? STARTUP : PRESETS[choice]
178
+ }
179
+
180
+ /** Startup catalog. Prefer `onboardingModulesForChoice` when a type is known. */
181
+ export const ONBOARDING_MODULES: readonly OnboardingModuleChoice[] = modulesFromPreset(STARTUP)
182
+
183
+ export function onboardingModulesForChoice(
184
+ choice: OrganisationChoiceId,
185
+ ): readonly OnboardingModuleChoice[] {
186
+ return modulesFromPreset(presetForChoice(choice))
187
+ }
188
+
189
+ export function defaultOnboardingModuleState(
190
+ choice: OrganisationChoiceId = 'startup',
191
+ ): Record<OnboardingModuleId, boolean> {
192
+ return { ...presetForChoice(choice).enabled }
193
+ }
194
+
195
+ function modulesFromPreset(preset: ModulePreset): readonly OnboardingModuleChoice[] {
196
+ return MODULE_ORDER.map((id) => ({
197
+ id,
198
+ label: LABELS[id],
199
+ description: preset.descriptions[id],
200
+ defaultEnabled: preset.enabled[id],
201
+ }))
202
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * A finished workspace can walk the wizard again from the account menu.
3
+ *
4
+ * `?rerun=1` marks that path. Organisation still runs so the reader can pick
5
+ * a type, but handbook pages are not seeded or replaced. The handbook review
6
+ * step is skipped: workspace → organisation → modules → invites → dashboard.
7
+ */
8
+
9
+ export const ONBOARDING_RERUN_PARAM = 'rerun'
10
+ export const ONBOARDING_RERUN_VALUE = '1'
11
+
12
+ /** Organisation type chosen on step 2 — drives module defaults on step 3. */
13
+ export const ONBOARDING_ORG_PARAM = 'org'
14
+
15
+ /** Entry URL the account menu opens for a rerun. */
16
+ export const ONBOARDING_RERUN_ENTRY = `/onboarding/workspace?${ONBOARDING_RERUN_PARAM}=${ONBOARDING_RERUN_VALUE}`
17
+
18
+ export function isOnboardingRerun(searchParams: URLSearchParams): boolean {
19
+ return searchParams.get(ONBOARDING_RERUN_PARAM) === ONBOARDING_RERUN_VALUE
20
+ }
21
+
22
+ /**
23
+ * Keep the rerun flag and the organisation type when moving between onboarding
24
+ * routes. `org` is how the modules step knows which defaults to offer; nothing
25
+ * else is held in the browser (`onboarding.md`).
26
+ */
27
+ export function onboardingPath(path: string, rerun: boolean, org?: string | null): string {
28
+ const params = new URLSearchParams()
29
+
30
+ if (rerun) {
31
+ params.set(ONBOARDING_RERUN_PARAM, ONBOARDING_RERUN_VALUE)
32
+ }
33
+
34
+ if (org !== undefined && org !== null && org !== '') {
35
+ params.set(ONBOARDING_ORG_PARAM, org)
36
+ }
37
+
38
+ const query = params.toString()
39
+
40
+ return query === '' ? path : `${path}?${query}`
41
+ }
package/src/styles.css CHANGED
@@ -41,6 +41,16 @@
41
41
  --color-success-soft: var(--success-soft);
42
42
  --color-warning: var(--warning);
43
43
  --color-warning-soft: var(--warning-soft);
44
+ --color-env-dev: var(--env-dev);
45
+ --color-env-dev-fg: var(--env-dev-fg);
46
+ --color-env-demo: var(--env-demo);
47
+ --color-env-demo-fg: var(--env-demo-fg);
48
+ --color-env-staging: var(--env-staging);
49
+ --color-env-staging-fg: var(--env-staging-fg);
50
+ --color-env-test: var(--env-test);
51
+ --color-env-test-fg: var(--env-test-fg);
52
+ --color-env-cloud: var(--env-cloud);
53
+ --color-env-cloud-fg: var(--env-cloud-fg);
44
54
  --color-sidebar: var(--sidebar);
45
55
  --color-sidebar-ink: var(--sidebar-ink);
46
56
  --color-sidebar-muted: var(--sidebar-muted);
@@ -73,6 +83,16 @@
73
83
  --success-soft: #ecfdf5;
74
84
  --warning: #d97706;
75
85
  --warning-soft: #fffbeb;
86
+ --env-dev: #d97706;
87
+ --env-dev-fg: #18181b;
88
+ --env-demo: var(--accent);
89
+ --env-demo-fg: var(--accent-fg);
90
+ --env-staging: #6d28d9;
91
+ --env-staging-fg: #ffffff;
92
+ --env-test: #475569;
93
+ --env-test-fg: #ffffff;
94
+ --env-cloud: #1d4ed8;
95
+ --env-cloud-fg: #ffffff;
76
96
  --sidebar: #fafafa;
77
97
  --sidebar-ink: #18181b;
78
98
  --sidebar-muted: #52525b;
@@ -105,6 +125,16 @@
105
125
  --success-soft: #064e3b;
106
126
  --warning: #fbbf24;
107
127
  --warning-soft: #451a03;
128
+ --env-dev: #fbbf24;
129
+ --env-dev-fg: #451a03;
130
+ --env-demo: var(--accent);
131
+ --env-demo-fg: var(--accent-fg);
132
+ --env-staging: #a78bfa;
133
+ --env-staging-fg: #1e1b4b;
134
+ --env-test: #94a3b8;
135
+ --env-test-fg: #0f172a;
136
+ --env-cloud: #60a5fa;
137
+ --env-cloud-fg: #1e3a8a;
108
138
  --sidebar: #0c0c0d;
109
139
  --sidebar-ink: #fafafa;
110
140
  --sidebar-muted: #a1a1aa;