@kelpie/ui 0.7.0 → 0.9.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 (349) hide show
  1. package/dist/api/resource.d.ts +59 -6
  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/consentPurposes.d.ts +13 -0
  10. package/dist/api/resources/consentPurposes.d.ts.map +1 -0
  11. package/dist/api/resources/consentPurposes.js +39 -0
  12. package/dist/api/resources/consentPurposes.js.map +1 -0
  13. package/dist/api/resources/customFields.d.ts +15 -0
  14. package/dist/api/resources/customFields.d.ts.map +1 -0
  15. package/dist/api/resources/customFields.js +41 -0
  16. package/dist/api/resources/customFields.js.map +1 -0
  17. package/dist/api/resources/decisions.d.ts +2 -0
  18. package/dist/api/resources/decisions.d.ts.map +1 -1
  19. package/dist/api/resources/decisions.js +1 -0
  20. package/dist/api/resources/decisions.js.map +1 -1
  21. package/dist/api/resources/emailDomainLinker.d.ts +16 -0
  22. package/dist/api/resources/emailDomainLinker.d.ts.map +1 -0
  23. package/dist/api/resources/emailDomainLinker.js +16 -0
  24. package/dist/api/resources/emailDomainLinker.js.map +1 -0
  25. package/dist/api/resources/enquiries.d.ts +39 -0
  26. package/dist/api/resources/enquiries.d.ts.map +1 -0
  27. package/dist/api/resources/enquiries.js +68 -0
  28. package/dist/api/resources/enquiries.js.map +1 -0
  29. package/dist/api/resources/forms.d.ts +21 -1
  30. package/dist/api/resources/forms.d.ts.map +1 -1
  31. package/dist/api/resources/forms.js +44 -17
  32. package/dist/api/resources/forms.js.map +1 -1
  33. package/dist/api/resources/handbookPages.d.ts +1 -1
  34. package/dist/api/resources/handbookPages.js +1 -1
  35. package/dist/api/resources/importJobs.d.ts +5 -0
  36. package/dist/api/resources/importJobs.d.ts.map +1 -1
  37. package/dist/api/resources/importJobs.js +3 -0
  38. package/dist/api/resources/importJobs.js.map +1 -1
  39. package/dist/api/resources/listMembers.d.ts +27 -0
  40. package/dist/api/resources/listMembers.d.ts.map +1 -0
  41. package/dist/api/resources/listMembers.js +108 -0
  42. package/dist/api/resources/listMembers.js.map +1 -0
  43. package/dist/api/resources/lists.d.ts +14 -0
  44. package/dist/api/resources/lists.d.ts.map +1 -0
  45. package/dist/api/resources/lists.js +34 -0
  46. package/dist/api/resources/lists.js.map +1 -0
  47. package/dist/api/resources/opportunities.d.ts +2 -0
  48. package/dist/api/resources/opportunities.d.ts.map +1 -1
  49. package/dist/api/resources/opportunities.js +1 -0
  50. package/dist/api/resources/opportunities.js.map +1 -1
  51. package/dist/api/resources/people.d.ts.map +1 -1
  52. package/dist/api/resources/people.js +4 -1
  53. package/dist/api/resources/people.js.map +1 -1
  54. package/dist/api/resources/roles.d.ts +2 -0
  55. package/dist/api/resources/roles.d.ts.map +1 -1
  56. package/dist/api/resources/roles.js +1 -1
  57. package/dist/api/resources/roles.js.map +1 -1
  58. package/dist/api/resources/sampleData.d.ts +17 -0
  59. package/dist/api/resources/sampleData.d.ts.map +1 -0
  60. package/dist/api/resources/sampleData.js +31 -0
  61. package/dist/api/resources/sampleData.js.map +1 -0
  62. package/dist/api/resources/webhooks.d.ts.map +1 -1
  63. package/dist/api/resources/webhooks.js +7 -22
  64. package/dist/api/resources/webhooks.js.map +1 -1
  65. package/dist/app/KelpieApp.d.ts.map +1 -1
  66. package/dist/app/KelpieApp.js +9 -1
  67. package/dist/app/KelpieApp.js.map +1 -1
  68. package/dist/components/ActivitiesPanel.d.ts.map +1 -1
  69. package/dist/components/ActivitiesPanel.js +3 -1
  70. package/dist/components/ActivitiesPanel.js.map +1 -1
  71. package/dist/components/ColumnPicker.d.ts +24 -0
  72. package/dist/components/ColumnPicker.d.ts.map +1 -0
  73. package/dist/components/ColumnPicker.js +56 -0
  74. package/dist/components/ColumnPicker.js.map +1 -0
  75. package/dist/components/CustomFieldsPanel.d.ts +23 -0
  76. package/dist/components/CustomFieldsPanel.d.ts.map +1 -0
  77. package/dist/components/CustomFieldsPanel.js +116 -0
  78. package/dist/components/CustomFieldsPanel.js.map +1 -0
  79. package/dist/components/DataTable.d.ts +16 -1
  80. package/dist/components/DataTable.d.ts.map +1 -1
  81. package/dist/components/DataTable.js +51 -10
  82. package/dist/components/DataTable.js.map +1 -1
  83. package/dist/components/DecisionsPanel.d.ts.map +1 -1
  84. package/dist/components/DecisionsPanel.js +4 -3
  85. package/dist/components/DecisionsPanel.js.map +1 -1
  86. package/dist/components/EntitySearch.d.ts.map +1 -1
  87. package/dist/components/EntitySearch.js +11 -5
  88. package/dist/components/EntitySearch.js.map +1 -1
  89. package/dist/components/FormsPanel.d.ts +18 -0
  90. package/dist/components/FormsPanel.d.ts.map +1 -0
  91. package/dist/components/FormsPanel.js +37 -0
  92. package/dist/components/FormsPanel.js.map +1 -0
  93. package/dist/components/ListsPanel.d.ts +14 -0
  94. package/dist/components/ListsPanel.d.ts.map +1 -0
  95. package/dist/components/ListsPanel.js +56 -0
  96. package/dist/components/ListsPanel.js.map +1 -0
  97. package/dist/components/NotesPanel.d.ts.map +1 -1
  98. package/dist/components/NotesPanel.js +6 -5
  99. package/dist/components/NotesPanel.js.map +1 -1
  100. package/dist/components/Paginator.d.ts +26 -0
  101. package/dist/components/Paginator.d.ts.map +1 -0
  102. package/dist/components/Paginator.js +16 -0
  103. package/dist/components/Paginator.js.map +1 -0
  104. package/dist/components/PlanAttention.d.ts +3 -2
  105. package/dist/components/PlanAttention.d.ts.map +1 -1
  106. package/dist/components/PlanAttention.js +7 -3
  107. package/dist/components/PlanAttention.js.map +1 -1
  108. package/dist/components/PlanPanel.d.ts.map +1 -1
  109. package/dist/components/PlanPanel.js +2 -1
  110. package/dist/components/PlanPanel.js.map +1 -1
  111. package/dist/components/Shell.d.ts.map +1 -1
  112. package/dist/components/Shell.js +4 -0
  113. package/dist/components/Shell.js.map +1 -1
  114. package/dist/components/useHasCustomFields.d.ts +13 -0
  115. package/dist/components/useHasCustomFields.d.ts.map +1 -0
  116. package/dist/components/useHasCustomFields.js +16 -0
  117. package/dist/components/useHasCustomFields.js.map +1 -0
  118. package/dist/index.d.ts +8 -2
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +4 -1
  121. package/dist/index.js.map +1 -1
  122. package/dist/lib/listView.d.ts +15 -0
  123. package/dist/lib/listView.d.ts.map +1 -0
  124. package/dist/lib/listView.js +66 -0
  125. package/dist/lib/listView.js.map +1 -0
  126. package/dist/lib/sort.d.ts +43 -0
  127. package/dist/lib/sort.d.ts.map +1 -0
  128. package/dist/lib/sort.js +102 -0
  129. package/dist/lib/sort.js.map +1 -0
  130. package/dist/pages/CompaniesPage.d.ts.map +1 -1
  131. package/dist/pages/CompaniesPage.js +88 -3
  132. package/dist/pages/CompaniesPage.js.map +1 -1
  133. package/dist/pages/CompanyDetail.d.ts.map +1 -1
  134. package/dist/pages/CompanyDetail.js +40 -15
  135. package/dist/pages/CompanyDetail.js.map +1 -1
  136. package/dist/pages/DealDetail.d.ts.map +1 -1
  137. package/dist/pages/DealDetail.js +20 -1
  138. package/dist/pages/DealDetail.js.map +1 -1
  139. package/dist/pages/DealsPage.d.ts.map +1 -1
  140. package/dist/pages/DealsPage.js +134 -14
  141. package/dist/pages/DealsPage.js.map +1 -1
  142. package/dist/pages/DecisionsPage.d.ts.map +1 -1
  143. package/dist/pages/DecisionsPage.js +52 -8
  144. package/dist/pages/DecisionsPage.js.map +1 -1
  145. package/dist/pages/EnquiriesPage.d.ts +2 -0
  146. package/dist/pages/EnquiriesPage.d.ts.map +1 -0
  147. package/dist/pages/EnquiriesPage.js +251 -0
  148. package/dist/pages/EnquiriesPage.js.map +1 -0
  149. package/dist/pages/EnquiryDetail.d.ts +2 -0
  150. package/dist/pages/EnquiryDetail.d.ts.map +1 -0
  151. package/dist/pages/EnquiryDetail.js +207 -0
  152. package/dist/pages/EnquiryDetail.js.map +1 -0
  153. package/dist/pages/EnquiryStageSettingsPage.d.ts +3 -0
  154. package/dist/pages/EnquiryStageSettingsPage.d.ts.map +1 -0
  155. package/dist/pages/EnquiryStageSettingsPage.js +34 -0
  156. package/dist/pages/EnquiryStageSettingsPage.js.map +1 -0
  157. package/dist/pages/FormDetail.d.ts.map +1 -1
  158. package/dist/pages/FormDetail.js +19 -1
  159. package/dist/pages/FormDetail.js.map +1 -1
  160. package/dist/pages/FormsPage.d.ts.map +1 -1
  161. package/dist/pages/FormsPage.js +53 -2
  162. package/dist/pages/FormsPage.js.map +1 -1
  163. package/dist/pages/FundraisingPage.d.ts.map +1 -1
  164. package/dist/pages/FundraisingPage.js +116 -14
  165. package/dist/pages/FundraisingPage.js.map +1 -1
  166. package/dist/pages/HiringPage.d.ts.map +1 -1
  167. package/dist/pages/HiringPage.js +27 -4
  168. package/dist/pages/HiringPage.js.map +1 -1
  169. package/dist/pages/ListDetail.d.ts +11 -0
  170. package/dist/pages/ListDetail.d.ts.map +1 -0
  171. package/dist/pages/ListDetail.js +214 -0
  172. package/dist/pages/ListDetail.js.map +1 -0
  173. package/dist/pages/ListsPage.d.ts +9 -0
  174. package/dist/pages/ListsPage.d.ts.map +1 -0
  175. package/dist/pages/ListsPage.js +125 -0
  176. package/dist/pages/ListsPage.js.map +1 -0
  177. package/dist/pages/OpportunitiesPage.d.ts.map +1 -1
  178. package/dist/pages/OpportunitiesPage.js +97 -14
  179. package/dist/pages/OpportunitiesPage.js.map +1 -1
  180. package/dist/pages/OpportunityDetail.d.ts.map +1 -1
  181. package/dist/pages/OpportunityDetail.js +64 -3
  182. package/dist/pages/OpportunityDetail.js.map +1 -1
  183. package/dist/pages/PartnershipDetail.d.ts.map +1 -1
  184. package/dist/pages/PartnershipDetail.js +19 -1
  185. package/dist/pages/PartnershipDetail.js.map +1 -1
  186. package/dist/pages/PartnershipsPage.d.ts.map +1 -1
  187. package/dist/pages/PartnershipsPage.js +111 -14
  188. package/dist/pages/PartnershipsPage.js.map +1 -1
  189. package/dist/pages/PeoplePage.d.ts.map +1 -1
  190. package/dist/pages/PeoplePage.js +119 -3
  191. package/dist/pages/PeoplePage.js.map +1 -1
  192. package/dist/pages/PersonDetail.d.ts.map +1 -1
  193. package/dist/pages/PersonDetail.js +67 -5
  194. package/dist/pages/PersonDetail.js.map +1 -1
  195. package/dist/pages/PlanningPage.d.ts.map +1 -1
  196. package/dist/pages/PlanningPage.js +88 -17
  197. package/dist/pages/PlanningPage.js.map +1 -1
  198. package/dist/pages/RaiseDetail.d.ts.map +1 -1
  199. package/dist/pages/RaiseDetail.js +13 -1
  200. package/dist/pages/RaiseDetail.js.map +1 -1
  201. package/dist/pages/RoleDetail.d.ts.map +1 -1
  202. package/dist/pages/RoleDetail.js +5 -4
  203. package/dist/pages/RoleDetail.js.map +1 -1
  204. package/dist/pages/SearchPage.d.ts.map +1 -1
  205. package/dist/pages/SearchPage.js +1 -0
  206. package/dist/pages/SearchPage.js.map +1 -1
  207. package/dist/pages/admin/DataPage.d.ts.map +1 -1
  208. package/dist/pages/admin/DataPage.js +126 -13
  209. package/dist/pages/admin/DataPage.js.map +1 -1
  210. package/dist/pages/admin/FieldsPage.d.ts +2 -0
  211. package/dist/pages/admin/FieldsPage.d.ts.map +1 -0
  212. package/dist/pages/admin/FieldsPage.js +164 -0
  213. package/dist/pages/admin/FieldsPage.js.map +1 -0
  214. package/dist/pages/admin/PrivacyPage.d.ts +9 -0
  215. package/dist/pages/admin/PrivacyPage.d.ts.map +1 -0
  216. package/dist/pages/admin/PrivacyPage.js +170 -0
  217. package/dist/pages/admin/PrivacyPage.js.map +1 -0
  218. package/dist/pages/admin/WebhookDeliveries.d.ts.map +1 -1
  219. package/dist/pages/admin/WebhookDeliveries.js +3 -2
  220. package/dist/pages/admin/WebhookDeliveries.js.map +1 -1
  221. package/dist/pages/admin/WebhooksPage.d.ts.map +1 -1
  222. package/dist/pages/admin/WebhooksPage.js +3 -2
  223. package/dist/pages/admin/WebhooksPage.js.map +1 -1
  224. package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
  225. package/dist/pages/admin/WorkspaceSettingsPage.js +45 -2
  226. package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
  227. package/dist/pages/dashboard/attention.d.ts.map +1 -1
  228. package/dist/pages/dashboard/attention.js +3 -0
  229. package/dist/pages/dashboard/attention.js.map +1 -1
  230. package/dist/pages/forms/AddFieldMenu.d.ts +17 -0
  231. package/dist/pages/forms/AddFieldMenu.d.ts.map +1 -0
  232. package/dist/pages/forms/AddFieldMenu.js +43 -0
  233. package/dist/pages/forms/AddFieldMenu.js.map +1 -0
  234. package/dist/pages/forms/EmbedPanel.d.ts +4 -0
  235. package/dist/pages/forms/EmbedPanel.d.ts.map +1 -1
  236. package/dist/pages/forms/EmbedPanel.js +62 -3
  237. package/dist/pages/forms/EmbedPanel.js.map +1 -1
  238. package/dist/pages/forms/FieldsEditor.d.ts.map +1 -1
  239. package/dist/pages/forms/FieldsEditor.js +179 -36
  240. package/dist/pages/forms/FieldsEditor.js.map +1 -1
  241. package/dist/pages/forms/FormActions.d.ts +6 -0
  242. package/dist/pages/forms/FormActions.d.ts.map +1 -0
  243. package/dist/pages/forms/FormActions.js +149 -0
  244. package/dist/pages/forms/FormActions.js.map +1 -0
  245. package/dist/pages/forms/FormSettings.d.ts.map +1 -1
  246. package/dist/pages/forms/FormSettings.js +15 -19
  247. package/dist/pages/forms/FormSettings.js.map +1 -1
  248. package/dist/pages/forms/SubmissionDetail.d.ts +2 -0
  249. package/dist/pages/forms/SubmissionDetail.d.ts.map +1 -0
  250. package/dist/pages/forms/SubmissionDetail.js +76 -0
  251. package/dist/pages/forms/SubmissionDetail.js.map +1 -0
  252. package/dist/pages/forms/SubmissionsTable.d.ts.map +1 -1
  253. package/dist/pages/forms/SubmissionsTable.js +57 -8
  254. package/dist/pages/forms/SubmissionsTable.js.map +1 -1
  255. package/dist/pages/forms/fieldList.d.ts +11 -5
  256. package/dist/pages/forms/fieldList.d.ts.map +1 -1
  257. package/dist/pages/forms/fieldList.js +66 -6
  258. package/dist/pages/forms/fieldList.js.map +1 -1
  259. package/dist/pages/forms/template.d.ts +17 -0
  260. package/dist/pages/forms/template.d.ts.map +1 -1
  261. package/dist/pages/forms/template.js +93 -0
  262. package/dist/pages/forms/template.js.map +1 -1
  263. package/dist/pages/handbook/HandbookPage.js +1 -1
  264. package/dist/pages/hiringDirectory.js +2 -2
  265. package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
  266. package/dist/pages/onboarding/WorkspaceStepPage.js +15 -2
  267. package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
  268. package/dist/pages/positionDirectory.js +2 -2
  269. package/package.json +2 -2
  270. package/src/api/resource.ts +155 -32
  271. package/src/api/resources/companies.ts +7 -1
  272. package/src/api/resources/consentPurposes.ts +83 -0
  273. package/src/api/resources/customFields.ts +88 -0
  274. package/src/api/resources/decisions.ts +3 -0
  275. package/src/api/resources/emailDomainLinker.ts +43 -0
  276. package/src/api/resources/enquiries.ts +118 -0
  277. package/src/api/resources/forms.ts +59 -19
  278. package/src/api/resources/handbookPages.ts +1 -1
  279. package/src/api/resources/importJobs.ts +9 -0
  280. package/src/api/resources/listMembers.ts +173 -0
  281. package/src/api/resources/lists.ts +61 -0
  282. package/src/api/resources/opportunities.ts +3 -0
  283. package/src/api/resources/people.ts +4 -1
  284. package/src/api/resources/roles.ts +3 -1
  285. package/src/api/resources/sampleData.ts +57 -0
  286. package/src/api/resources/webhooks.ts +8 -24
  287. package/src/app/KelpieApp.tsx +16 -0
  288. package/src/components/ActivitiesPanel.tsx +3 -10
  289. package/src/components/ColumnPicker.tsx +124 -0
  290. package/src/components/CustomFieldsPanel.tsx +330 -0
  291. package/src/components/DataTable.tsx +89 -10
  292. package/src/components/DecisionsPanel.tsx +4 -12
  293. package/src/components/EntitySearch.tsx +12 -5
  294. package/src/components/FormsPanel.tsx +86 -0
  295. package/src/components/ListsPanel.tsx +176 -0
  296. package/src/components/NotesPanel.tsx +6 -14
  297. package/src/components/Paginator.tsx +78 -0
  298. package/src/components/PlanAttention.tsx +20 -3
  299. package/src/components/PlanPanel.tsx +2 -10
  300. package/src/components/Shell.tsx +4 -0
  301. package/src/components/useHasCustomFields.ts +18 -0
  302. package/src/index.ts +10 -1
  303. package/src/lib/listView.ts +149 -0
  304. package/src/lib/sort.ts +143 -0
  305. package/src/pages/CompaniesPage.tsx +125 -11
  306. package/src/pages/CompanyDetail.tsx +98 -63
  307. package/src/pages/DealDetail.tsx +34 -0
  308. package/src/pages/DealsPage.tsx +178 -24
  309. package/src/pages/DecisionsPage.tsx +75 -18
  310. package/src/pages/EnquiriesPage.tsx +434 -0
  311. package/src/pages/EnquiryDetail.tsx +554 -0
  312. package/src/pages/EnquiryStageSettingsPage.tsx +63 -0
  313. package/src/pages/FormDetail.tsx +23 -1
  314. package/src/pages/FormsPage.tsx +81 -17
  315. package/src/pages/FundraisingPage.tsx +152 -24
  316. package/src/pages/HiringPage.tsx +39 -11
  317. package/src/pages/ListDetail.tsx +470 -0
  318. package/src/pages/ListsPage.tsx +281 -0
  319. package/src/pages/OpportunitiesPage.tsx +130 -24
  320. package/src/pages/OpportunityDetail.tsx +168 -2
  321. package/src/pages/PartnershipDetail.tsx +35 -0
  322. package/src/pages/PartnershipsPage.tsx +149 -24
  323. package/src/pages/PeoplePage.tsx +157 -11
  324. package/src/pages/PersonDetail.tsx +157 -6
  325. package/src/pages/PlanningPage.tsx +259 -29
  326. package/src/pages/RaiseDetail.tsx +24 -0
  327. package/src/pages/RoleDetail.tsx +21 -26
  328. package/src/pages/SearchPage.tsx +1 -0
  329. package/src/pages/admin/DataPage.tsx +324 -13
  330. package/src/pages/admin/FieldsPage.tsx +455 -0
  331. package/src/pages/admin/PrivacyPage.tsx +461 -0
  332. package/src/pages/admin/WebhookDeliveries.tsx +10 -15
  333. package/src/pages/admin/WebhooksPage.tsx +14 -19
  334. package/src/pages/admin/WorkspaceSettingsPage.tsx +134 -2
  335. package/src/pages/dashboard/attention.ts +3 -0
  336. package/src/pages/forms/AddFieldMenu.tsx +121 -0
  337. package/src/pages/forms/EmbedPanel.tsx +163 -2
  338. package/src/pages/forms/FieldsEditor.tsx +539 -165
  339. package/src/pages/forms/FormActions.tsx +552 -0
  340. package/src/pages/forms/FormSettings.tsx +29 -70
  341. package/src/pages/forms/SubmissionDetail.tsx +249 -0
  342. package/src/pages/forms/SubmissionsTable.tsx +112 -26
  343. package/src/pages/forms/fieldList.ts +68 -5
  344. package/src/pages/forms/template.ts +103 -0
  345. package/src/pages/handbook/HandbookPage.tsx +1 -1
  346. package/src/pages/hiringDirectory.ts +2 -2
  347. package/src/pages/onboarding/WorkspaceStepPage.tsx +31 -3
  348. package/src/pages/positionDirectory.ts +2 -2
  349. package/src/styles.css +3 -3
@@ -1,5 +1,4 @@
1
1
  import type { Deal, PipelineStage } from '@kelpie/schemas'
2
- import { useState } from 'react'
3
2
  import { Link, useNavigate } from 'react-router'
4
3
 
5
4
  import { useCompanies } from '../api/resources/companies.ts'
@@ -7,17 +6,22 @@ import { useDeals, useCreateDeal, useUpdateDeal } from '../api/resources/deals.t
7
6
  import { useMembers } from '../api/resources/members.ts'
8
7
  import { usePipelineStages } from '../api/resources/pipelineStages.ts'
9
8
  import { MAX_PAGE_SIZE, usePlanItems } from '../api/resources/planItems.ts'
9
+ import { useTimezone } from '../api/resources/account.ts'
10
10
  import { Chip } from '../components/Chip.tsx'
11
11
  import type { ChipTone } from '../components/Chip.tsx'
12
+ import { ColumnPicker } from '../components/ColumnPicker.tsx'
12
13
  import { DataTable } from '../components/DataTable.tsx'
13
14
  import type { Column, DataTableGroup } from '../components/DataTable.tsx'
14
15
  import { KanbanBoard } from '../components/KanbanBoard.tsx'
15
16
  import { PageHeader } from '../components/PageHeader.tsx'
17
+ import { Paginator } from '../components/Paginator.tsx'
16
18
  import { ErrorPanel, LoadingPanel } from '../components/QueryState.tsx'
17
19
  import { SegmentedControl } from '../components/SegmentedControl.tsx'
18
- import { formatDay } from '../lib/dates.ts'
20
+ import { formatDate, formatDay } from '../lib/dates.ts'
21
+ import { useListView } from '../lib/listView.ts'
19
22
  import { formatMoney } from '../lib/money.ts'
20
23
  import { DUE_BUCKETS, byDateThenTitle, dueBucketFor, nextOpenByTarget } from '../lib/plan.ts'
24
+ import { serverSortOnly } from '../lib/sort.ts'
21
25
 
22
26
  /**
23
27
  * The Deals pipeline: a board and a list over the same records, grouped by stage
@@ -31,6 +35,17 @@ type BoardView = 'list' | 'columns'
31
35
  type PipelineScope = 'open' | 'all'
32
36
  type ListGrouping = 'stage' | 'due'
33
37
 
38
+ const SCOPES: readonly PipelineScope[] = ['open', 'all']
39
+ const GROUPINGS: readonly ListGrouping[] = ['stage', 'due']
40
+
41
+ function readScope(stored: string | undefined): PipelineScope {
42
+ return SCOPES.includes(stored as PipelineScope) ? (stored as PipelineScope) : 'open'
43
+ }
44
+
45
+ function readGrouping(stored: string | undefined): ListGrouping {
46
+ return GROUPINGS.includes(stored as ListGrouping) ? (stored as ListGrouping) : 'stage'
47
+ }
48
+
34
49
  /** Tones keyed by the seeded slugs. A renamed stage keeps its slug and its tone. */
35
50
  const STAGE_TONES: Readonly<Record<string, ChipTone>> = {
36
51
  won: 'success',
@@ -43,19 +58,54 @@ function stageTone(stage: PipelineStage): ChipTone {
43
58
  return STAGE_TONES[stage.slug] ?? 'neutral'
44
59
  }
45
60
 
61
+ const DEFAULT_VISIBLE_KEYS: readonly string[] = [
62
+ 'name',
63
+ 'company',
64
+ 'value',
65
+ 'nextPlan',
66
+ 'owner',
67
+ 'close',
68
+ ]
69
+
70
+ const SERVER_SORT_KEYS: readonly string[] = ['name', 'created_at', 'updated_at']
71
+
46
72
  export function DealsPage(): React.JSX.Element {
47
73
  const navigate = useNavigate()
48
- const [view, setView] = useState<BoardView>('list')
49
- const [scope, setScope] = useState<PipelineScope>('open')
50
- const [grouping, setGrouping] = useState<ListGrouping>('stage')
51
- const [sort, setSort] = useState<string | undefined>(undefined)
52
74
 
53
75
  const stages = usePipelineStages('deal')
54
- const deals = useDeals({ sort })
55
76
  const companies = useCompanies({ limit: 200 })
77
+ // The list-view controller holds every persisted knob, so it is declared
78
+ // before the deals query — the sort it stores decides which page comes
79
+ // back. Board vs list defaults to the board here; the other three read
80
+ // their own default from the readers above.
81
+ const supportedKeys: readonly string[] = [
82
+ 'name',
83
+ 'company',
84
+ 'stage',
85
+ 'value',
86
+ 'currency',
87
+ 'nextPlan',
88
+ 'owner',
89
+ 'close',
90
+ 'competitors',
91
+ 'tags',
92
+ 'summary',
93
+ 'risks',
94
+ 'whyWin',
95
+ 'externalId',
96
+ 'createdAt',
97
+ 'updatedAt',
98
+ ]
99
+ const listView = useListView('deals', supportedKeys, DEFAULT_VISIBLE_KEYS)
100
+ const view: BoardView = listView.mode ?? 'columns'
101
+ const scope = readScope(listView.scope)
102
+ const grouping = readGrouping(listView.grouping)
103
+ const sort = listView.sort
104
+ const deals = useDeals({ sort: serverSortOnly(sort, SERVER_SORT_KEYS) })
56
105
  const members = useMembers()
57
106
  const createDeal = useCreateDeal()
58
107
  const updateDeal = useUpdateDeal()
108
+ const timezone = useTimezone()
59
109
 
60
110
  const allStages = [...stages.records].sort((a, b) => a.sortOrder - b.sortOrder)
61
111
  const visibleStages = scope === 'open' ? allStages.filter((stage) => stage.open) : allStages
@@ -71,7 +121,7 @@ export function DealsPage(): React.JSX.Element {
71
121
  { enabled: askedDealIds.length > 0 },
72
122
  )
73
123
  const nextPlanByDeal = nextOpenByTarget(planItems.records)
74
- const plansTruncated = visibleDeals.length > MAX_PAGE_SIZE || planItems.hasMore
124
+ const plansTruncated = visibleDeals.length > MAX_PAGE_SIZE || planItems.hasNext
75
125
 
76
126
  const companyNameById = new Map(companies.records.map((company) => [company.id, company.name]))
77
127
 
@@ -87,6 +137,8 @@ export function DealsPage(): React.JSX.Element {
87
137
  await navigate(`/deals/${deal.id}`)
88
138
  }
89
139
 
140
+ const stageLabelById = new Map(allStages.map((stage) => [stage.id, stage.label]))
141
+
90
142
  const columns: readonly Column<Deal>[] = [
91
143
  {
92
144
  key: 'name',
@@ -97,21 +149,36 @@ export function DealsPage(): React.JSX.Element {
97
149
  {
98
150
  key: 'company',
99
151
  header: 'Company',
152
+ getSortValue: (deal) => companyNameById.get(deal.companyId) ?? null,
100
153
  render: (deal) => companyNameById.get(deal.companyId) ?? '—',
101
154
  },
155
+ {
156
+ key: 'stage',
157
+ header: 'Stage',
158
+ getSortValue: (deal) => stageLabelById.get(deal.stageId) ?? null,
159
+ render: (deal) => stageLabelById.get(deal.stageId) ?? '—',
160
+ },
102
161
  {
103
162
  key: 'value',
104
163
  header: 'Value',
105
164
  className: 'w-28',
165
+ getSortValue: (deal) => deal.valueCents,
106
166
  render: (deal) => (
107
167
  <span className="font-mono text-[12px]">
108
168
  {deal.valueCents === null ? '—' : formatMoney(deal.valueCents, deal.currency)}
109
169
  </span>
110
170
  ),
111
171
  },
172
+ {
173
+ key: 'currency',
174
+ header: 'Currency',
175
+ getSortValue: (deal) => deal.currency,
176
+ render: (deal) => deal.currency ?? '—',
177
+ },
112
178
  {
113
179
  key: 'nextPlan',
114
180
  header: 'Next plan',
181
+ getSortValue: (deal) => nextPlanByDeal.get(deal.id)?.date ?? null,
115
182
  render: (deal) => {
116
183
  const next = nextPlanByDeal.get(deal.id)
117
184
 
@@ -132,6 +199,8 @@ export function DealsPage(): React.JSX.Element {
132
199
  {
133
200
  key: 'owner',
134
201
  header: 'Owner',
202
+ getSortValue: (deal) =>
203
+ deal.ownerId === null ? null : (members.nameById.get(deal.ownerId) ?? 'Unknown'),
135
204
  render: (deal) =>
136
205
  deal.ownerId === null ? '—' : (members.nameById.get(deal.ownerId) ?? 'Unknown'),
137
206
  },
@@ -139,14 +208,91 @@ export function DealsPage(): React.JSX.Element {
139
208
  key: 'close',
140
209
  header: 'Close',
141
210
  className: 'w-28',
211
+ getSortValue: (deal) => deal.expectedClose,
142
212
  render: (deal) => (
143
213
  <span className="font-mono text-[12px] text-ink-muted">
144
214
  {deal.expectedClose === null ? '—' : formatDay(deal.expectedClose)}
145
215
  </span>
146
216
  ),
147
217
  },
218
+ {
219
+ key: 'competitors',
220
+ header: 'Competitors',
221
+ getSortValue: (deal) => deal.competitors.join(', ') || null,
222
+ render: (deal) =>
223
+ deal.competitors.length === 0 ? '—' : (
224
+ <span className="flex flex-wrap gap-1">
225
+ {deal.competitors.map((entry) => (
226
+ <Chip key={entry}>
227
+ <span className="text-[10px]">{entry}</span>
228
+ </Chip>
229
+ ))}
230
+ </span>
231
+ ),
232
+ },
233
+ {
234
+ key: 'tags',
235
+ header: 'Tags',
236
+ getSortValue: (deal) => deal.tags.join(', ') || null,
237
+ render: (deal) =>
238
+ deal.tags.length === 0 ? '—' : (
239
+ <span className="flex flex-wrap gap-1">
240
+ {deal.tags.map((tag) => (
241
+ <Chip key={tag}>
242
+ <span className="text-[10px]">{tag}</span>
243
+ </Chip>
244
+ ))}
245
+ </span>
246
+ ),
247
+ },
248
+ {
249
+ key: 'summary',
250
+ header: 'Summary',
251
+ getSortValue: (deal) => deal.summary || null,
252
+ render: (deal) =>
253
+ deal.summary.length === 0 ? '—' : <span className="text-ink-muted">{deal.summary}</span>,
254
+ },
255
+ {
256
+ key: 'risks',
257
+ header: 'Risks',
258
+ getSortValue: (deal) => deal.risks || null,
259
+ render: (deal) =>
260
+ deal.risks.length === 0 ? '—' : <span className="text-ink-muted">{deal.risks}</span>,
261
+ },
262
+ {
263
+ key: 'whyWin',
264
+ header: 'Why win',
265
+ getSortValue: (deal) => deal.whyWin || null,
266
+ render: (deal) =>
267
+ deal.whyWin.length === 0 ? '—' : <span className="text-ink-muted">{deal.whyWin}</span>,
268
+ },
269
+ {
270
+ key: 'externalId',
271
+ header: 'External ID',
272
+ getSortValue: (deal) => deal.externalId,
273
+ render: (deal) =>
274
+ deal.externalId === null ? (
275
+ '—'
276
+ ) : (
277
+ <span className="font-mono text-[12px] text-ink-muted">{deal.externalId}</span>
278
+ ),
279
+ },
280
+ {
281
+ key: 'createdAt',
282
+ header: 'Created',
283
+ sortKey: 'created_at',
284
+ render: (deal) => formatDate(deal.createdAt, timezone),
285
+ },
286
+ {
287
+ key: 'updatedAt',
288
+ header: 'Updated',
289
+ sortKey: 'updated_at',
290
+ render: (deal) => formatDate(deal.updatedAt, timezone),
291
+ },
148
292
  ]
149
293
 
294
+ const pickerOptions = columns.map((column) => ({ key: column.key, label: column.header }))
295
+
150
296
  const stageGroups: readonly DataTableGroup<Deal>[] = visibleStages.map((stage) => ({
151
297
  id: stage.id,
152
298
  label: <Chip tone={stageTone(stage)}>{stage.label}</Chip>,
@@ -208,7 +354,9 @@ export function DealsPage(): React.JSX.Element {
208
354
  <SegmentedControl
209
355
  ariaLabel="Stage scope"
210
356
  value={scope}
211
- onChange={setScope}
357
+ onChange={(next) => {
358
+ listView.setScope(next)
359
+ }}
212
360
  options={[
213
361
  { id: 'open', label: 'Open' },
214
362
  { id: 'all', label: 'All' },
@@ -218,7 +366,9 @@ export function DealsPage(): React.JSX.Element {
218
366
  <SegmentedControl
219
367
  ariaLabel="Group the list"
220
368
  value={grouping}
221
- onChange={setGrouping}
369
+ onChange={(next) => {
370
+ listView.setGrouping(next)
371
+ }}
222
372
  options={[
223
373
  { id: 'stage', label: 'By stage' },
224
374
  { id: 'due', label: 'By due' },
@@ -228,12 +378,21 @@ export function DealsPage(): React.JSX.Element {
228
378
  <SegmentedControl
229
379
  ariaLabel="Board or list"
230
380
  value={view}
231
- onChange={setView}
381
+ onChange={(next) => {
382
+ listView.setMode(next)
383
+ }}
232
384
  options={[
233
385
  { id: 'list', label: 'List' },
234
386
  { id: 'columns', label: 'Board' },
235
387
  ]}
236
388
  />
389
+ {view === 'list' && (
390
+ <ColumnPicker
391
+ options={pickerOptions}
392
+ visibleKeys={listView.visibleKeys}
393
+ onChange={listView.setVisibleKeys}
394
+ />
395
+ )}
237
396
  </>
238
397
  }
239
398
  />
@@ -255,6 +414,7 @@ export function DealsPage(): React.JSX.Element {
255
414
  <LoadingPanel label="Loading deals…" />
256
415
  ) : (
257
416
  <>
417
+ {view === 'list' && <Paginator list={deals} placement="top" />}
258
418
  {view === 'columns' ? (
259
419
  <KanbanBoard
260
420
  stages={visibleStages.map((stage) => ({ id: stage.id, label: stage.label }))}
@@ -301,13 +461,16 @@ export function DealsPage(): React.JSX.Element {
301
461
  ? {
302
462
  label: 'Show all deals',
303
463
  onClick: () => {
304
- setScope('all')
464
+ listView.setScope('all')
305
465
  },
306
466
  }
307
467
  : undefined
308
468
  }
309
469
  sort={sort}
310
- onSortChange={setSort}
470
+ onSortChange={(next) => {
471
+ listView.setSort(next)
472
+ }}
473
+ visibleColumnKeys={listView.visibleKeys}
311
474
  />
312
475
  )}
313
476
  {updateDeal.error !== null && (
@@ -315,17 +478,8 @@ export function DealsPage(): React.JSX.Element {
315
478
  <ErrorPanel error={updateDeal.error} />
316
479
  </div>
317
480
  )}
318
- {deals.hasMore && (
319
- <button
320
- type="button"
321
- onClick={deals.loadMore}
322
- disabled={deals.isLoadingMore}
323
- 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"
324
- >
325
- {deals.isLoadingMore ? 'Loading…' : 'Load more'}
326
- </button>
327
- )}
328
- {companies.hasMore && (
481
+ {view === 'list' && <Paginator list={deals} />}
482
+ {companies.hasNext && (
329
483
  <p className="mt-2 text-[11px] text-ink-faint">
330
484
  More companies exist than one page returns, so some company names may show as “—”.
331
485
  </p>
@@ -11,11 +11,15 @@ import { useOpportunities } from '../api/resources/opportunities.ts'
11
11
  import { usePartnerships } from '../api/resources/partnerships.ts'
12
12
  import { usePeople } from '../api/resources/people.ts'
13
13
  import { useRaises } from '../api/resources/raises.ts'
14
+ import { ColumnPicker } from '../components/ColumnPicker.tsx'
14
15
  import { DataTable } from '../components/DataTable.tsx'
15
16
  import type { Column } from '../components/DataTable.tsx'
16
17
  import { FilterBar, PageHeader } from '../components/PageHeader.tsx'
18
+ import { Paginator } from '../components/Paginator.tsx'
17
19
  import { ErrorPanel, LoadingPanel } from '../components/QueryState.tsx'
18
20
  import { formatDate } from '../lib/dates.ts'
21
+ import { serverSortOnly } from '../lib/sort.ts'
22
+ import { useListView } from '../lib/listView.ts'
19
23
 
20
24
  /**
21
25
  * Every decision in the workspace.
@@ -40,6 +44,7 @@ const TARGET_LABELS: Readonly<Record<RecordTargetType, string>> = {
40
44
  opportunity: 'Opportunity',
41
45
  partnership: 'Partnership',
42
46
  raise: 'Raise',
47
+ enquiry: 'Enquiry',
43
48
  candidate: 'Candidate',
44
49
  }
45
50
 
@@ -50,6 +55,7 @@ const TARGET_ROUTES: Readonly<Partial<Record<RecordTargetType, string>>> = {
50
55
  opportunity: '/opportunities',
51
56
  raise: '/fundraising',
52
57
  partnership: '/partnerships',
58
+ enquiry: '/enquiries',
53
59
  }
54
60
 
55
61
  interface TargetDirectory {
@@ -90,12 +96,12 @@ function useTargetDirectory(): TargetDirectory {
90
96
  return {
91
97
  nameFor: (decision) => nameById.get(decision.targetId),
92
98
  isComplete:
93
- !people.hasMore &&
94
- !companies.hasMore &&
95
- !deals.hasMore &&
96
- !opportunities.hasMore &&
97
- !raises.hasMore &&
98
- !partnerships.hasMore,
99
+ !people.hasNext &&
100
+ !companies.hasNext &&
101
+ !deals.hasNext &&
102
+ !opportunities.hasNext &&
103
+ !raises.hasNext &&
104
+ !partnerships.hasNext,
99
105
  }
100
106
  }
101
107
 
@@ -123,9 +129,17 @@ function LinkedTo({
123
129
  )
124
130
  }
125
131
 
132
+ const DEFAULT_VISIBLE_KEYS: readonly string[] = ['decision', 'target', 'decided', 'due', 'owner']
133
+
134
+ const SERVER_SORT_KEYS: readonly string[] = ['decided_at', 'created_at', 'updated_at']
135
+
126
136
  export function DecisionsPage(): React.JSX.Element {
127
137
  const [term, setTerm] = useState('')
128
- const decisions = useDecisions({ term: term.trim().length > 0 ? term.trim() : undefined })
138
+ const [sort, setSort] = useState<string | undefined>(undefined)
139
+ const decisions = useDecisions({
140
+ term: term.trim().length > 0 ? term.trim() : undefined,
141
+ sort: serverSortOnly(sort, SERVER_SORT_KEYS),
142
+ })
129
143
  const directory = useTargetDirectory()
130
144
  const members = useMembers()
131
145
  const timezone = useTimezone()
@@ -134,6 +148,7 @@ export function DecisionsPage(): React.JSX.Element {
134
148
  {
135
149
  key: 'decision',
136
150
  header: 'Decision',
151
+ getSortValue: (decision) => decision.body,
137
152
  render: (decision) => (
138
153
  <>
139
154
  <div className="font-medium text-ink">{decision.body}</div>
@@ -143,14 +158,33 @@ export function DecisionsPage(): React.JSX.Element {
143
158
  </>
144
159
  ),
145
160
  },
161
+ {
162
+ key: 'rationale',
163
+ header: 'Rationale',
164
+ getSortValue: (decision) => decision.rationale,
165
+ render: (decision) =>
166
+ decision.rationale === null ? (
167
+ '—'
168
+ ) : (
169
+ <span className="text-ink-muted">{decision.rationale}</span>
170
+ ),
171
+ },
146
172
  {
147
173
  key: 'target',
148
174
  header: 'Linked to',
175
+ getSortValue: (decision) => directory.nameFor(decision) ?? TARGET_LABELS[decision.targetType],
149
176
  render: (decision) => <LinkedTo decision={decision} name={directory.nameFor(decision)} />,
150
177
  },
178
+ {
179
+ key: 'targetType',
180
+ header: 'Type',
181
+ getSortValue: (decision) => TARGET_LABELS[decision.targetType],
182
+ render: (decision) => TARGET_LABELS[decision.targetType],
183
+ },
151
184
  {
152
185
  key: 'decided',
153
186
  header: 'Decided',
187
+ sortKey: 'decided_at',
154
188
  render: (decision) => (
155
189
  <span className="text-ink-muted">{formatDate(decision.decidedAt, timezone)}</span>
156
190
  ),
@@ -158,6 +192,7 @@ export function DecisionsPage(): React.JSX.Element {
158
192
  {
159
193
  key: 'due',
160
194
  header: 'By',
195
+ getSortValue: (decision) => decision.dueAt,
161
196
  render: (decision) => (
162
197
  <span className="text-ink-muted">
163
198
  {decision.dueAt === null ? '—' : formatDate(decision.dueAt, timezone)}
@@ -167,17 +202,44 @@ export function DecisionsPage(): React.JSX.Element {
167
202
  {
168
203
  key: 'owner',
169
204
  header: 'Owner',
205
+ getSortValue: (decision) =>
206
+ decision.ownerId === null ? null : (members.nameById.get(decision.ownerId) ?? null),
170
207
  render: (decision) => (
171
208
  <span className="text-ink-muted">
172
209
  {decision.ownerId === null ? '—' : (members.nameById.get(decision.ownerId) ?? '—')}
173
210
  </span>
174
211
  ),
175
212
  },
213
+ {
214
+ key: 'createdAt',
215
+ header: 'Created',
216
+ sortKey: 'created_at',
217
+ render: (decision) => formatDate(decision.createdAt, timezone),
218
+ },
219
+ {
220
+ key: 'updatedAt',
221
+ header: 'Updated',
222
+ sortKey: 'updated_at',
223
+ render: (decision) => formatDate(decision.updatedAt, timezone),
224
+ },
176
225
  ]
177
226
 
227
+ const supportedKeys = columns.map((column) => column.key)
228
+ const listView = useListView('decisions', supportedKeys, DEFAULT_VISIBLE_KEYS)
229
+ const pickerOptions = columns.map((column) => ({ key: column.key, label: column.header }))
230
+
178
231
  return (
179
232
  <div className="animate-fade-in">
180
- <PageHeader title="Decisions" />
233
+ <PageHeader
234
+ title="Decisions"
235
+ actions={
236
+ <ColumnPicker
237
+ options={pickerOptions}
238
+ visibleKeys={listView.visibleKeys}
239
+ onChange={listView.setVisibleKeys}
240
+ />
241
+ }
242
+ />
181
243
  <FilterBar value={term} onChange={setTerm} placeholder="Filter decisions…" />
182
244
 
183
245
  {decisions.error !== null ? (
@@ -186,11 +248,15 @@ export function DecisionsPage(): React.JSX.Element {
186
248
  <LoadingPanel label="Loading decisions…" />
187
249
  ) : (
188
250
  <>
251
+ <Paginator list={decisions} placement="top" />
189
252
  <DataTable
190
253
  columns={columns}
191
254
  rows={decisions.records}
192
255
  getRowId={(decision) => decision.id}
193
256
  emptyMessage="No decisions match"
257
+ sort={sort}
258
+ onSortChange={setSort}
259
+ visibleColumnKeys={listView.visibleKeys}
194
260
  />
195
261
  {!directory.isComplete && (
196
262
  <p className="mt-2 text-[11px] text-ink-faint">
@@ -198,16 +264,7 @@ export function DecisionsPage(): React.JSX.Element {
198
264
  record type.
199
265
  </p>
200
266
  )}
201
- {decisions.hasMore && (
202
- <button
203
- type="button"
204
- onClick={decisions.loadMore}
205
- disabled={decisions.isLoadingMore}
206
- 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"
207
- >
208
- {decisions.isLoadingMore ? 'Loading…' : 'Load more'}
209
- </button>
210
- )}
267
+ <Paginator list={decisions} />
211
268
  </>
212
269
  )}
213
270
  </div>