@kelpie/ui 0.6.0 → 0.8.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 (243) hide show
  1. package/dist/api/resource.d.ts +58 -5
  2. package/dist/api/resource.d.ts.map +1 -1
  3. package/dist/api/resource.js +106 -24
  4. package/dist/api/resource.js.map +1 -1
  5. package/dist/api/resources/companies.d.ts +2 -0
  6. package/dist/api/resources/companies.d.ts.map +1 -1
  7. package/dist/api/resources/companies.js +5 -1
  8. package/dist/api/resources/companies.js.map +1 -1
  9. package/dist/api/resources/decisions.d.ts +2 -0
  10. package/dist/api/resources/decisions.d.ts.map +1 -1
  11. package/dist/api/resources/decisions.js +1 -0
  12. package/dist/api/resources/decisions.js.map +1 -1
  13. package/dist/api/resources/emailDomainLinker.d.ts +16 -0
  14. package/dist/api/resources/emailDomainLinker.d.ts.map +1 -0
  15. package/dist/api/resources/emailDomainLinker.js +16 -0
  16. package/dist/api/resources/emailDomainLinker.js.map +1 -0
  17. package/dist/api/resources/forms.d.ts.map +1 -1
  18. package/dist/api/resources/forms.js +6 -20
  19. package/dist/api/resources/forms.js.map +1 -1
  20. package/dist/api/resources/handbookPages.d.ts +1 -1
  21. package/dist/api/resources/handbookPages.js +1 -1
  22. package/dist/api/resources/importJobs.d.ts +3 -1
  23. package/dist/api/resources/importJobs.d.ts.map +1 -1
  24. package/dist/api/resources/importJobs.js +1 -0
  25. package/dist/api/resources/importJobs.js.map +1 -1
  26. package/dist/api/resources/listMembers.d.ts +27 -0
  27. package/dist/api/resources/listMembers.d.ts.map +1 -0
  28. package/dist/api/resources/listMembers.js +108 -0
  29. package/dist/api/resources/listMembers.js.map +1 -0
  30. package/dist/api/resources/lists.d.ts +14 -0
  31. package/dist/api/resources/lists.d.ts.map +1 -0
  32. package/dist/api/resources/lists.js +34 -0
  33. package/dist/api/resources/lists.js.map +1 -0
  34. package/dist/api/resources/people.d.ts.map +1 -1
  35. package/dist/api/resources/people.js +4 -1
  36. package/dist/api/resources/people.js.map +1 -1
  37. package/dist/api/resources/publicConfig.d.ts +8 -0
  38. package/dist/api/resources/publicConfig.d.ts.map +1 -0
  39. package/dist/api/resources/publicConfig.js +22 -0
  40. package/dist/api/resources/publicConfig.js.map +1 -0
  41. package/dist/api/resources/roles.d.ts +2 -0
  42. package/dist/api/resources/roles.d.ts.map +1 -1
  43. package/dist/api/resources/roles.js +1 -1
  44. package/dist/api/resources/roles.js.map +1 -1
  45. package/dist/api/resources/sampleData.d.ts +17 -0
  46. package/dist/api/resources/sampleData.d.ts.map +1 -0
  47. package/dist/api/resources/sampleData.js +30 -0
  48. package/dist/api/resources/sampleData.js.map +1 -0
  49. package/dist/api/resources/webhooks.d.ts.map +1 -1
  50. package/dist/api/resources/webhooks.js +7 -22
  51. package/dist/api/resources/webhooks.js.map +1 -1
  52. package/dist/app/KelpieApp.d.ts.map +1 -1
  53. package/dist/app/KelpieApp.js +5 -2
  54. package/dist/app/KelpieApp.js.map +1 -1
  55. package/dist/components/ActivitiesPanel.d.ts.map +1 -1
  56. package/dist/components/ActivitiesPanel.js +2 -1
  57. package/dist/components/ActivitiesPanel.js.map +1 -1
  58. package/dist/components/ColumnPicker.d.ts +24 -0
  59. package/dist/components/ColumnPicker.d.ts.map +1 -0
  60. package/dist/components/ColumnPicker.js +56 -0
  61. package/dist/components/ColumnPicker.js.map +1 -0
  62. package/dist/components/DataTable.d.ts +16 -1
  63. package/dist/components/DataTable.d.ts.map +1 -1
  64. package/dist/components/DataTable.js +51 -10
  65. package/dist/components/DataTable.js.map +1 -1
  66. package/dist/components/DecisionsPanel.d.ts.map +1 -1
  67. package/dist/components/DecisionsPanel.js +4 -3
  68. package/dist/components/DecisionsPanel.js.map +1 -1
  69. package/dist/components/EntitySearch.d.ts.map +1 -1
  70. package/dist/components/EntitySearch.js +11 -5
  71. package/dist/components/EntitySearch.js.map +1 -1
  72. package/dist/components/EnvironmentBanner.d.ts +14 -0
  73. package/dist/components/EnvironmentBanner.d.ts.map +1 -0
  74. package/dist/components/EnvironmentBanner.js +23 -0
  75. package/dist/components/EnvironmentBanner.js.map +1 -0
  76. package/dist/components/ListsPanel.d.ts +14 -0
  77. package/dist/components/ListsPanel.d.ts.map +1 -0
  78. package/dist/components/ListsPanel.js +56 -0
  79. package/dist/components/ListsPanel.js.map +1 -0
  80. package/dist/components/NotesPanel.d.ts.map +1 -1
  81. package/dist/components/NotesPanel.js +6 -5
  82. package/dist/components/NotesPanel.js.map +1 -1
  83. package/dist/components/Paginator.d.ts +26 -0
  84. package/dist/components/Paginator.d.ts.map +1 -0
  85. package/dist/components/Paginator.js +16 -0
  86. package/dist/components/Paginator.js.map +1 -0
  87. package/dist/components/PlanPanel.d.ts.map +1 -1
  88. package/dist/components/PlanPanel.js +2 -1
  89. package/dist/components/PlanPanel.js.map +1 -1
  90. package/dist/components/Shell.d.ts.map +1 -1
  91. package/dist/components/Shell.js +1 -0
  92. package/dist/components/Shell.js.map +1 -1
  93. package/dist/index.d.ts +8 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +4 -1
  96. package/dist/index.js.map +1 -1
  97. package/dist/lib/listView.d.ts +15 -0
  98. package/dist/lib/listView.d.ts.map +1 -0
  99. package/dist/lib/listView.js +66 -0
  100. package/dist/lib/listView.js.map +1 -0
  101. package/dist/lib/sort.d.ts +43 -0
  102. package/dist/lib/sort.d.ts.map +1 -0
  103. package/dist/lib/sort.js +102 -0
  104. package/dist/lib/sort.js.map +1 -0
  105. package/dist/pages/CompaniesPage.d.ts.map +1 -1
  106. package/dist/pages/CompaniesPage.js +88 -3
  107. package/dist/pages/CompaniesPage.js.map +1 -1
  108. package/dist/pages/CompanyDetail.d.ts.map +1 -1
  109. package/dist/pages/CompanyDetail.js +17 -14
  110. package/dist/pages/CompanyDetail.js.map +1 -1
  111. package/dist/pages/DealDetail.d.ts.map +1 -1
  112. package/dist/pages/DealDetail.js +3 -1
  113. package/dist/pages/DealDetail.js.map +1 -1
  114. package/dist/pages/DealsPage.d.ts.map +1 -1
  115. package/dist/pages/DealsPage.js +134 -14
  116. package/dist/pages/DealsPage.js.map +1 -1
  117. package/dist/pages/DecisionsPage.d.ts.map +1 -1
  118. package/dist/pages/DecisionsPage.js +50 -8
  119. package/dist/pages/DecisionsPage.js.map +1 -1
  120. package/dist/pages/FormsPage.d.ts.map +1 -1
  121. package/dist/pages/FormsPage.js +53 -2
  122. package/dist/pages/FormsPage.js.map +1 -1
  123. package/dist/pages/FundraisingPage.d.ts.map +1 -1
  124. package/dist/pages/FundraisingPage.js +116 -14
  125. package/dist/pages/FundraisingPage.js.map +1 -1
  126. package/dist/pages/HiringPage.d.ts.map +1 -1
  127. package/dist/pages/HiringPage.js +27 -4
  128. package/dist/pages/HiringPage.js.map +1 -1
  129. package/dist/pages/ListDetail.d.ts +11 -0
  130. package/dist/pages/ListDetail.d.ts.map +1 -0
  131. package/dist/pages/ListDetail.js +188 -0
  132. package/dist/pages/ListDetail.js.map +1 -0
  133. package/dist/pages/ListsPage.d.ts +9 -0
  134. package/dist/pages/ListsPage.d.ts.map +1 -0
  135. package/dist/pages/ListsPage.js +125 -0
  136. package/dist/pages/ListsPage.js.map +1 -0
  137. package/dist/pages/OpportunitiesPage.d.ts.map +1 -1
  138. package/dist/pages/OpportunitiesPage.js +97 -14
  139. package/dist/pages/OpportunitiesPage.js.map +1 -1
  140. package/dist/pages/OpportunityDetail.d.ts.map +1 -1
  141. package/dist/pages/OpportunityDetail.js +3 -1
  142. package/dist/pages/OpportunityDetail.js.map +1 -1
  143. package/dist/pages/PartnershipDetail.d.ts.map +1 -1
  144. package/dist/pages/PartnershipDetail.js +3 -1
  145. package/dist/pages/PartnershipDetail.js.map +1 -1
  146. package/dist/pages/PartnershipsPage.d.ts.map +1 -1
  147. package/dist/pages/PartnershipsPage.js +111 -14
  148. package/dist/pages/PartnershipsPage.js.map +1 -1
  149. package/dist/pages/PeoplePage.d.ts.map +1 -1
  150. package/dist/pages/PeoplePage.js +102 -3
  151. package/dist/pages/PeoplePage.js.map +1 -1
  152. package/dist/pages/PersonDetail.d.ts.map +1 -1
  153. package/dist/pages/PersonDetail.js +5 -3
  154. package/dist/pages/PersonDetail.js.map +1 -1
  155. package/dist/pages/PlanningPage.d.ts.map +1 -1
  156. package/dist/pages/PlanningPage.js +85 -17
  157. package/dist/pages/PlanningPage.js.map +1 -1
  158. package/dist/pages/RaiseDetail.d.ts.map +1 -1
  159. package/dist/pages/RaiseDetail.js +3 -1
  160. package/dist/pages/RaiseDetail.js.map +1 -1
  161. package/dist/pages/RoleDetail.d.ts.map +1 -1
  162. package/dist/pages/RoleDetail.js +5 -4
  163. package/dist/pages/RoleDetail.js.map +1 -1
  164. package/dist/pages/admin/DataPage.d.ts.map +1 -1
  165. package/dist/pages/admin/DataPage.js +126 -16
  166. package/dist/pages/admin/DataPage.js.map +1 -1
  167. package/dist/pages/admin/WebhookDeliveries.d.ts.map +1 -1
  168. package/dist/pages/admin/WebhookDeliveries.js +3 -2
  169. package/dist/pages/admin/WebhookDeliveries.js.map +1 -1
  170. package/dist/pages/admin/WebhooksPage.d.ts.map +1 -1
  171. package/dist/pages/admin/WebhooksPage.js +3 -2
  172. package/dist/pages/admin/WebhooksPage.js.map +1 -1
  173. package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
  174. package/dist/pages/admin/WorkspaceSettingsPage.js +45 -2
  175. package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
  176. package/dist/pages/forms/SubmissionsTable.d.ts.map +1 -1
  177. package/dist/pages/forms/SubmissionsTable.js +2 -1
  178. package/dist/pages/forms/SubmissionsTable.js.map +1 -1
  179. package/dist/pages/handbook/HandbookPage.js +1 -1
  180. package/dist/pages/hiringDirectory.js +2 -2
  181. package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
  182. package/dist/pages/onboarding/WorkspaceStepPage.js +15 -2
  183. package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
  184. package/dist/pages/positionDirectory.js +2 -2
  185. package/package.json +2 -2
  186. package/src/api/resource.ts +149 -31
  187. package/src/api/resources/companies.ts +7 -1
  188. package/src/api/resources/decisions.ts +3 -0
  189. package/src/api/resources/emailDomainLinker.ts +43 -0
  190. package/src/api/resources/forms.ts +5 -23
  191. package/src/api/resources/handbookPages.ts +1 -1
  192. package/src/api/resources/importJobs.ts +4 -0
  193. package/src/api/resources/listMembers.ts +173 -0
  194. package/src/api/resources/lists.ts +61 -0
  195. package/src/api/resources/people.ts +4 -1
  196. package/src/api/resources/publicConfig.ts +33 -0
  197. package/src/api/resources/roles.ts +3 -1
  198. package/src/api/resources/sampleData.ts +56 -0
  199. package/src/api/resources/webhooks.ts +8 -24
  200. package/src/app/KelpieApp.tsx +11 -0
  201. package/src/components/ActivitiesPanel.tsx +2 -10
  202. package/src/components/ColumnPicker.tsx +124 -0
  203. package/src/components/DataTable.tsx +89 -10
  204. package/src/components/DecisionsPanel.tsx +4 -12
  205. package/src/components/EntitySearch.tsx +12 -5
  206. package/src/components/EnvironmentBanner.tsx +30 -0
  207. package/src/components/ListsPanel.tsx +176 -0
  208. package/src/components/NotesPanel.tsx +6 -14
  209. package/src/components/Paginator.tsx +78 -0
  210. package/src/components/PlanPanel.tsx +2 -10
  211. package/src/components/Shell.tsx +1 -0
  212. package/src/index.ts +10 -1
  213. package/src/lib/listView.ts +149 -0
  214. package/src/lib/sort.ts +143 -0
  215. package/src/pages/CompaniesPage.tsx +125 -11
  216. package/src/pages/CompanyDetail.tsx +62 -62
  217. package/src/pages/DealDetail.tsx +3 -0
  218. package/src/pages/DealsPage.tsx +178 -24
  219. package/src/pages/DecisionsPage.tsx +73 -18
  220. package/src/pages/FormsPage.tsx +81 -17
  221. package/src/pages/FundraisingPage.tsx +152 -24
  222. package/src/pages/HiringPage.tsx +39 -11
  223. package/src/pages/ListDetail.tsx +408 -0
  224. package/src/pages/ListsPage.tsx +281 -0
  225. package/src/pages/OpportunitiesPage.tsx +130 -24
  226. package/src/pages/OpportunityDetail.tsx +3 -0
  227. package/src/pages/PartnershipDetail.tsx +3 -0
  228. package/src/pages/PartnershipsPage.tsx +149 -24
  229. package/src/pages/PeoplePage.tsx +137 -11
  230. package/src/pages/PersonDetail.tsx +5 -3
  231. package/src/pages/PlanningPage.tsx +256 -29
  232. package/src/pages/RaiseDetail.tsx +3 -0
  233. package/src/pages/RoleDetail.tsx +21 -26
  234. package/src/pages/admin/DataPage.tsx +359 -36
  235. package/src/pages/admin/WebhookDeliveries.tsx +10 -15
  236. package/src/pages/admin/WebhooksPage.tsx +14 -19
  237. package/src/pages/admin/WorkspaceSettingsPage.tsx +134 -2
  238. package/src/pages/forms/SubmissionsTable.tsx +3 -10
  239. package/src/pages/handbook/HandbookPage.tsx +1 -1
  240. package/src/pages/hiringDirectory.ts +2 -2
  241. package/src/pages/onboarding/WorkspaceStepPage.tsx +31 -3
  242. package/src/pages/positionDirectory.ts +2 -2
  243. package/src/styles.css +3 -3
@@ -1,14 +1,21 @@
1
- import type { Workspace } from '@kelpie/schemas'
2
- import { useState } from 'react'
1
+ import type { Company, Workspace } from '@kelpie/schemas'
2
+ import { useMemo, useState } from 'react'
3
3
  import type { FormEvent, ReactNode } from 'react'
4
4
  import { useNavigate } from 'react-router'
5
5
 
6
+ import {
7
+ useCompanies,
8
+ useCreateCompany,
9
+ useUpdateCompany,
10
+ } from '../../api/resources/companies.ts'
6
11
  import { useSession } from '../../api/resources/session.ts'
7
12
  import {
8
13
  useDeleteWorkspace,
9
14
  useUpdateWorkspace,
10
15
  useWorkspace,
11
16
  } from '../../api/resources/workspace.ts'
17
+ import { EntitySearch } from '../../components/EntitySearch.tsx'
18
+ import type { SearchOption } from '../../components/EntitySearch.tsx'
12
19
  import { PageHeader } from '../../components/PageHeader.tsx'
13
20
  import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
14
21
 
@@ -190,11 +197,136 @@ function WorkspaceSettingsForm({
190
197
  )}
191
198
  </form>
192
199
 
200
+ <OwnCompanySection canEdit={canEdit} />
201
+
193
202
  {session?.role === 'owner' && <DangerZone slug={workspace.slug} />}
194
203
  </div>
195
204
  )
196
205
  }
197
206
 
207
+ /**
208
+ * Mark one or more Companies as "us" — the workspace's own organisation.
209
+ *
210
+ * Backed by `companies.is_own`. Zero or many rows may carry it (a parent and a
211
+ * subsidiary is the intended case), so the surface is a list plus an add
212
+ * picker rather than a single-select. Picking a name that does not exist
213
+ * creates the Company with `is_own: true` in one call.
214
+ */
215
+ function OwnCompanySection({ canEdit }: { readonly canEdit: boolean }): React.JSX.Element {
216
+ const own = useCompanies({ isOwn: true })
217
+ const [search, setSearch] = useState('')
218
+ // The picker's options: workspace companies that are NOT already marked.
219
+ // Server-side search on the term the user types.
220
+ const searchable = useCompanies({
221
+ isOwn: false,
222
+ term: search.trim().length > 0 ? search.trim() : undefined,
223
+ })
224
+ const update = useUpdateCompany()
225
+ const create = useCreateCompany()
226
+
227
+ const options: SearchOption[] = useMemo(
228
+ () =>
229
+ searchable.records.map((company) => ({
230
+ id: company.id,
231
+ label: company.name,
232
+ meta: company.domain ?? undefined,
233
+ })),
234
+ [searchable.records],
235
+ )
236
+
237
+ function mark(companyId: string): void {
238
+ update.run({ id: companyId, changes: { isOwn: true } })
239
+ }
240
+
241
+ function unmark(companyId: string): void {
242
+ update.run({ id: companyId, changes: { isOwn: false } })
243
+ }
244
+
245
+ function createAndMark(name: string): void {
246
+ // The API defaults every other field; `is_own: true` lands the row in the
247
+ // list this section shows and nowhere else.
248
+ create.run({ name, isOwn: true })
249
+ setSearch('')
250
+ }
251
+
252
+ const busy = update.isPending || create.isPending
253
+
254
+ return (
255
+ <section className="space-y-3 rounded-lg border border-border bg-surface p-4">
256
+ <div>
257
+ <h2 className="text-[13px] font-semibold text-ink">This company</h2>
258
+ <p className="mt-1 max-w-xl text-[12px] text-ink-muted">
259
+ Mark the company (or companies) in the CRM that represent this workspace itself. Used
260
+ later to separate own records from prospects.
261
+ </p>
262
+ </div>
263
+
264
+ {own.isLoading ? (
265
+ <p className="text-[12px] text-ink-faint">Loading…</p>
266
+ ) : own.records.length === 0 ? (
267
+ <p className="text-[12px] text-ink-faint">Nothing marked yet.</p>
268
+ ) : (
269
+ <ul className="divide-y divide-border rounded-md border border-border">
270
+ {own.records.map((company: Company) => (
271
+ <li
272
+ key={company.id}
273
+ className="flex items-center justify-between gap-3 px-3 py-2.5"
274
+ >
275
+ <div className="min-w-0">
276
+ <div className="truncate text-[13px] font-medium text-ink">{company.name}</div>
277
+ {company.domain !== null && company.domain.length > 0 && (
278
+ <div className="truncate text-[11px] text-ink-faint">{company.domain}</div>
279
+ )}
280
+ </div>
281
+ {canEdit && (
282
+ <button
283
+ type="button"
284
+ onClick={() => {
285
+ unmark(company.id)
286
+ }}
287
+ disabled={busy}
288
+ className="shrink-0 rounded-md border border-border bg-surface-raised px-2.5 py-1 text-[11px] font-medium text-ink hover:border-danger hover:text-danger disabled:opacity-50"
289
+ >
290
+ Unmark
291
+ </button>
292
+ )}
293
+ </li>
294
+ ))}
295
+ </ul>
296
+ )}
297
+
298
+ {canEdit && (
299
+ <div className="max-w-md">
300
+ <label className="mb-1.5 block text-[12px] font-medium text-ink">
301
+ {own.records.length === 0 ? 'Pick or create' : 'Add another'}
302
+ </label>
303
+ <EntitySearch
304
+ options={options}
305
+ value=""
306
+ onChange={mark}
307
+ onQueryChange={setSearch}
308
+ onCreate={createAndMark}
309
+ createLabel={(query) => `Create “${query}” and mark it`}
310
+ placeholder="Search companies…"
311
+ emptyMessage="No matches — type a new name to create one"
312
+ />
313
+ </div>
314
+ )}
315
+
316
+ {update.error !== null && (
317
+ <div className="max-w-xl">
318
+ <ErrorPanel error={update.error} />
319
+ </div>
320
+ )}
321
+ {create.error !== null && (
322
+ <div className="max-w-xl">
323
+ <ErrorPanel error={create.error} />
324
+ </div>
325
+ )}
326
+ </section>
327
+ )
328
+ }
329
+
198
330
  /**
199
331
  * Deleting the workspace, behind the workspace's own name.
200
332
  *
@@ -8,6 +8,7 @@ import { useCompanies } from '../../api/resources/companies.ts'
8
8
  import { usePeople } from '../../api/resources/people.ts'
9
9
  import { DataTable } from '../../components/DataTable.tsx'
10
10
  import type { Column } from '../../components/DataTable.tsx'
11
+ import { Paginator } from '../../components/Paginator.tsx'
11
12
  import { ErrorPanel, LoadingPanel } from '../../components/QueryState.tsx'
12
13
  import { SectionHeader } from '../../components/SectionHeader.tsx'
13
14
  import { formatDateTime } from '../../lib/dates.ts'
@@ -100,22 +101,14 @@ export function SubmissionsTable({ form, submissions }: SubmissionsTableProps):
100
101
  title="Submissions"
101
102
  description="Inbound answers, and the records each one created or matched."
102
103
  />
104
+ <Paginator list={submissions} placement="top" />
103
105
  <DataTable
104
106
  columns={columns}
105
107
  rows={submissions.records}
106
108
  getRowId={(submission) => submission.id}
107
109
  emptyMessage="No submissions yet"
108
110
  />
109
- {submissions.hasMore && (
110
- <button
111
- type="button"
112
- onClick={submissions.loadMore}
113
- disabled={submissions.isLoadingMore}
114
- className="mt-3 rounded-md border border-border px-3 py-1.5 text-[12px] font-medium text-ink transition hover:border-border-strong hover:bg-surface-sunken disabled:opacity-50"
115
- >
116
- {submissions.isLoadingMore ? 'Loading…' : 'Load more'}
117
- </button>
118
- )}
111
+ <Paginator list={submissions} />
119
112
  </div>
120
113
  )
121
114
  }
@@ -234,7 +234,7 @@ export function HandbookLayout(): React.JSX.Element {
234
234
  {flat.length === 0 && (
235
235
  <p className="px-2 py-6 text-center text-[12px] text-ink-faint">No pages yet.</p>
236
236
  )}
237
- {pages.hasMore && (
237
+ {pages.hasNext && (
238
238
  <p className="px-2 py-3 text-center text-[11px] text-ink-faint">
239
239
  This handbook has more pages than one request returns, so the tree below is
240
240
  incomplete.
@@ -45,7 +45,7 @@ export function useRoleCandidateCounts(roleIds: readonly string[]): RoleCandidat
45
45
  return {
46
46
  countFor: (roleId) => countByRole.get(roleId) ?? 0,
47
47
  isLoading: candidates.isLoading,
48
- isComplete: !candidates.hasMore,
48
+ isComplete: !candidates.hasNext,
49
49
  }
50
50
  }
51
51
 
@@ -95,7 +95,7 @@ export function useCandidateNotes(candidateIds: readonly string[]): CandidateNot
95
95
  return {
96
96
  noteFor: (candidateId) => newestByCandidate.get(candidateId),
97
97
  isLoading: notes.isLoading,
98
- isComplete: !notes.hasMore,
98
+ isComplete: !notes.hasNext,
99
99
  }
100
100
  }
101
101
 
@@ -2,6 +2,7 @@ import { useState } from 'react'
2
2
  import type { FormEvent } from 'react'
3
3
  import { Link, Navigate, useNavigate } from 'react-router'
4
4
 
5
+ import { useInstallSampleData } from '../../api/resources/sampleData.ts'
5
6
  import { useCreateWorkspace, useSession } from '../../api/resources/session.ts'
6
7
  import { ErrorPanel } from '../../components/QueryState.tsx'
7
8
  import { SubmitButton, TextField } from '../auth/AuthForm.tsx'
@@ -48,10 +49,12 @@ export function WorkspaceStepPage(): React.JSX.Element {
48
49
  const navigate = useNavigate()
49
50
  const { isSignedOut } = useSession()
50
51
  const createWorkspace = useCreateWorkspace()
52
+ const installSampleData = useInstallSampleData()
51
53
  const [name, setName] = useState('')
52
54
  const [slug, setSlug] = useState('')
53
55
  const [slugEdited, setSlugEdited] = useState(false)
54
56
  const [timezone] = useState(browserTimezone)
57
+ const [seedSample, setSeedSample] = useState(false)
55
58
 
56
59
  function changeName(value: string): void {
57
60
  setName(value)
@@ -71,7 +74,18 @@ export function WorkspaceStepPage(): React.JSX.Element {
71
74
 
72
75
  createWorkspace
73
76
  .runAsync({ name: name.trim(), slug: slug.trim(), timezone })
74
- .then(() => navigate('/onboarding/invites', { replace: true }))
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 })
88
+ })
75
89
  .catch(() => undefined)
76
90
  }
77
91
 
@@ -110,11 +124,25 @@ export function WorkspaceStepPage(): React.JSX.Element {
110
124
  <p className="text-[11px] text-ink-faint">
111
125
  Timezone: {timezone}. Change it later in Admin → Workspace.
112
126
  </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, opportunities,
136
+ fundraising, partnerships, and hiring so the workspace has something to look at.
137
+ You can delete it later.
138
+ </span>
139
+ </label>
113
140
  {createWorkspace.error !== null && <ErrorPanel error={createWorkspace.error} />}
141
+ {installSampleData.error !== null && <ErrorPanel error={installSampleData.error} />}
114
142
  <SubmitButton
115
143
  label="Continue"
116
- pendingLabel="Creating…"
117
- isPending={createWorkspace.isPending}
144
+ pendingLabel={installSampleData.isPending ? 'Installing sample data…' : 'Creating…'}
145
+ isPending={createWorkspace.isPending || installSampleData.isPending}
118
146
  />
119
147
  </form>
120
148
  </AuthLayout>
@@ -64,7 +64,7 @@ export function usePeopleDirectory(personIds: readonly string[]): PeopleDirector
64
64
  .map((held) => companyNameById.get(held.companyId))
65
65
  .filter((name): name is string => name !== undefined),
66
66
  isLoading: positions.isLoading || companies.isLoading,
67
- isComplete: !positions.hasMore && !companies.hasMore,
67
+ isComplete: !positions.hasNext && !companies.hasNext,
68
68
  }
69
69
  }
70
70
 
@@ -93,6 +93,6 @@ export function useCompanyHeadcounts(companyIds: readonly string[]): CompanyHead
93
93
  return {
94
94
  countFor: (companyId) => countByCompany.get(companyId) ?? 0,
95
95
  isLoading: positions.isLoading,
96
- isComplete: !positions.hasMore,
96
+ isComplete: !positions.hasNext,
97
97
  }
98
98
  }
package/src/styles.css CHANGED
@@ -58,8 +58,8 @@
58
58
  --surface-raised: #ffffff;
59
59
  --surface-sunken: #f4f4f5;
60
60
  --ink: #18181b;
61
- --ink-muted: #71717a;
62
- --ink-faint: #a1a1aa;
61
+ --ink-muted: #52525b;
62
+ --ink-faint: #71717a;
63
63
  --border: #e4e4e7;
64
64
  --border-strong: #d4d4d8;
65
65
  --accent: #0f766e;
@@ -75,7 +75,7 @@
75
75
  --warning-soft: #fffbeb;
76
76
  --sidebar: #fafafa;
77
77
  --sidebar-ink: #18181b;
78
- --sidebar-muted: #71717a;
78
+ --sidebar-muted: #52525b;
79
79
  --sidebar-hover: #f4f4f5;
80
80
  --sidebar-active: #ececef;
81
81
  --sidebar-border: #e4e4e7;