@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,16 +1,28 @@
1
- import { PIPELINE_KIND_LABELS, PIPELINE_KINDS, PLAN_ITEM_STATUS_LABELS } from '@kelpie/schemas'
2
- import type { PipelineKind, PlanItem } from '@kelpie/schemas'
1
+ import {
2
+ PIPELINE_KIND_LABELS,
3
+ PIPELINE_KINDS,
4
+ PLAN_ITEM_STATUS_LABELS,
5
+ PLAN_ITEM_STATUSES,
6
+ } from '@kelpie/schemas'
7
+ import type { PipelineKind, PlanItem, PlanItemStatus } from '@kelpie/schemas'
3
8
  import { useState } from 'react'
9
+ import type { FormEvent } from 'react'
4
10
 
5
11
  import { useDeals } from '../api/resources/deals.ts'
12
+ import { useEnquiries } from '../api/resources/enquiries.ts'
6
13
  import { useMembers } from '../api/resources/members.ts'
7
14
  import { useOpportunities } from '../api/resources/opportunities.ts'
8
15
  import { usePartnerships } from '../api/resources/partnerships.ts'
9
- import { MAX_PAGE_SIZE, usePlanItems } from '../api/resources/planItems.ts'
16
+ import {
17
+ MAX_PAGE_SIZE,
18
+ useCreatePlanItem,
19
+ usePlanItems,
20
+ } from '../api/resources/planItems.ts'
10
21
  import { useRaises } from '../api/resources/raises.ts'
11
22
  import { Chip } from '../components/Chip.tsx'
12
23
  import { PageHeader } from '../components/PageHeader.tsx'
13
24
  import { PlanTargetLink } from '../components/PlanAttention.tsx'
25
+ import { Paginator } from '../components/Paginator.tsx'
14
26
  import { ErrorPanel, LoadingPanel } from '../components/QueryState.tsx'
15
27
  import { SegmentedControl } from '../components/SegmentedControl.tsx'
16
28
  import { formatDay } from '../lib/dates.ts'
@@ -23,6 +35,9 @@ import { monthBounds, planStatusTone, todayIso } from '../lib/plan.ts'
23
35
  * upcoming and pages through it; the calendar wants one month, which is what
24
36
  * `?from=` and `?to=` are for, so moving between months is a request rather than
25
37
  * a filter over rows the page happens to hold.
38
+ *
39
+ * Adding a plan item lives here as well as on each record's Plan panel: the page
40
+ * is the workspace-wide view of the same resource, so create belongs here too.
26
41
  */
27
42
 
28
43
  type ViewMode = 'list' | 'calendar'
@@ -35,15 +50,27 @@ const CELL_ITEM_LIMIT = 3
35
50
 
36
51
  const MONTH_AND_YEAR = new Intl.DateTimeFormat(undefined, { month: 'long', year: 'numeric' })
37
52
 
53
+ const inputClass =
54
+ 'h-9 w-full rounded-md border border-border bg-surface-raised px-2.5 text-[13px] outline-none focus:border-accent'
55
+
56
+ interface PipelineTarget {
57
+ readonly id: string
58
+ readonly name: string
59
+ }
60
+
38
61
  export function PlanningPage(): React.JSX.Element {
39
62
  const [view, setView] = useState<ViewMode>('list')
40
63
  const [typeFilter, setTypeFilter] = useState<TypeFilter>('all')
64
+ const [adding, setAdding] = useState(false)
41
65
  const [month, setMonth] = useState(() => {
42
66
  const now = new Date()
43
67
 
44
68
  return { year: now.getFullYear(), month: now.getMonth() }
45
69
  })
46
70
 
71
+ const targets = usePipelineTargets()
72
+ const createItem = useCreatePlanItem()
73
+
47
74
  const typeQuery = typeFilter === 'all' ? {} : { targetType: typeFilter }
48
75
  // A month is a bounded question, so the calendar asks for it at the largest
49
76
  // page size and draws the answer whole. The list pages the ordinary way.
@@ -65,6 +92,10 @@ export function PlanningPage(): React.JSX.Element {
65
92
  <div className="animate-fade-in">
66
93
  <PageHeader
67
94
  title="Planning"
95
+ onAdd={() => {
96
+ setAdding((current) => !current)
97
+ }}
98
+ addLabel="Add plan item"
68
99
  actions={
69
100
  <>
70
101
  <label className="sr-only" htmlFor="plan-type-filter">
@@ -98,6 +129,33 @@ export function PlanningPage(): React.JSX.Element {
98
129
  }
99
130
  />
100
131
 
132
+ {adding && (
133
+ <AddPlanItemForm
134
+ initialTargetType={typeFilter === 'all' ? 'deal' : typeFilter}
135
+ targetsByKind={targets.byKind}
136
+ isPending={createItem.isPending}
137
+ error={createItem.error}
138
+ onCancel={() => {
139
+ setAdding(false)
140
+ }}
141
+ onSubmit={(fields) => {
142
+ createItem
143
+ .runAsync({
144
+ targetType: fields.targetType,
145
+ targetId: fields.targetId,
146
+ date: fields.date,
147
+ title: fields.title,
148
+ ownerId: fields.ownerId.length === 0 ? null : fields.ownerId,
149
+ status: fields.status,
150
+ })
151
+ .then(() => {
152
+ setAdding(false)
153
+ })
154
+ .catch(() => undefined)
155
+ }}
156
+ />
157
+ )}
158
+
101
159
  {view === 'calendar' && (
102
160
  <div className="mb-4 flex items-center gap-2">
103
161
  <button
@@ -131,47 +189,219 @@ export function PlanningPage(): React.JSX.Element {
131
189
  ) : items.isLoading ? (
132
190
  <LoadingPanel label="Loading plan…" />
133
191
  ) : view === 'list' ? (
134
- <PlanList items={items.records} />
192
+ <>
193
+ <Paginator list={items} placement="top" />
194
+ <PlanList items={items.records} targetNames={targets.nameById} />
195
+ </>
135
196
  ) : (
136
197
  <PlanCalendar year={month.year} month={month.month} items={items.records} />
137
198
  )}
138
199
 
139
- {items.hasMore && (
140
- <button
141
- type="button"
142
- onClick={items.loadMore}
143
- disabled={items.isLoadingMore}
144
- 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"
145
- >
146
- {items.isLoadingMore ? 'Loading…' : 'Load more'}
147
- </button>
148
- )}
200
+ {view === 'list' && <Paginator list={items} />}
149
201
  </div>
150
202
  )
151
203
  }
152
204
 
153
205
  /**
154
- * Names for the records the loaded items point at.
155
- *
156
- * The directory is one page per pipeline, matching the Deal contacts precedent:
157
- * past it a row still says what kind of record it belongs to.
206
+ * Pipeline records the add form can attach to, and the name map the list uses
207
+ * for PlanTargetLink. One page per kind, same ceiling as the rest of Planning.
158
208
  */
159
- function useTargetNames(): ReadonlyMap<string, string> {
160
- const deals = useDeals({ limit: 200 })
161
- const opportunities = useOpportunities({ limit: 200 })
162
- const raises = useRaises({ limit: 200 })
163
- const partnerships = usePartnerships({ limit: 200 })
164
-
165
- return new Map(
166
- [...deals.records, ...opportunities.records, ...raises.records, ...partnerships.records].map(
167
- (record) => [record.id, record.name],
209
+ function usePipelineTargets(): {
210
+ readonly byKind: Readonly<Record<PipelineKind, readonly PipelineTarget[]>>
211
+ readonly nameById: ReadonlyMap<string, string>
212
+ } {
213
+ const deals = useDeals({ limit: MAX_PAGE_SIZE })
214
+ const opportunities = useOpportunities({ limit: MAX_PAGE_SIZE })
215
+ const raises = useRaises({ limit: MAX_PAGE_SIZE })
216
+ const partnerships = usePartnerships({ limit: MAX_PAGE_SIZE })
217
+ const enquiries = useEnquiries({ limit: MAX_PAGE_SIZE })
218
+
219
+ const byKind: Readonly<Record<PipelineKind, readonly PipelineTarget[]>> = {
220
+ enquiry: enquiries.records.map((record) => ({ id: record.id, name: record.name })),
221
+ deal: deals.records.map((record) => ({ id: record.id, name: record.name })),
222
+ opportunity: opportunities.records.map((record) => ({ id: record.id, name: record.name })),
223
+ raise: raises.records.map((record) => ({ id: record.id, name: record.name })),
224
+ partnership: partnerships.records.map((record) => ({ id: record.id, name: record.name })),
225
+ }
226
+
227
+ return {
228
+ byKind,
229
+ nameById: new Map(
230
+ Object.values(byKind)
231
+ .flat()
232
+ .map((record) => [record.id, record.name]),
168
233
  ),
234
+ }
235
+ }
236
+
237
+ interface AddPlanItemFields {
238
+ readonly targetType: PipelineKind
239
+ readonly targetId: string
240
+ readonly date: string
241
+ readonly title: string
242
+ readonly ownerId: string
243
+ readonly status: PlanItemStatus
244
+ }
245
+
246
+ function AddPlanItemForm({
247
+ initialTargetType,
248
+ targetsByKind,
249
+ isPending,
250
+ error,
251
+ onSubmit,
252
+ onCancel,
253
+ }: {
254
+ readonly initialTargetType: PipelineKind
255
+ readonly targetsByKind: Readonly<Record<PipelineKind, readonly PipelineTarget[]>>
256
+ readonly isPending: boolean
257
+ readonly error: Error | null
258
+ readonly onSubmit: (fields: AddPlanItemFields) => void
259
+ readonly onCancel: () => void
260
+ }): React.JSX.Element {
261
+ const members = useMembers()
262
+ const [draft, setDraft] = useState<AddPlanItemFields>({
263
+ targetType: initialTargetType,
264
+ targetId: '',
265
+ date: todayIso(),
266
+ title: '',
267
+ ownerId: '',
268
+ status: 'todo',
269
+ })
270
+
271
+ const targets = targetsByKind[draft.targetType]
272
+
273
+ function submit(event: FormEvent): void {
274
+ event.preventDefault()
275
+
276
+ const title = draft.title.trim()
277
+
278
+ if (title.length === 0 || draft.date.length === 0 || draft.targetId.length === 0) {
279
+ return
280
+ }
281
+
282
+ onSubmit({ ...draft, title })
283
+ }
284
+
285
+ return (
286
+ <form onSubmit={submit} className="mb-4 space-y-3 rounded-md border border-border p-3">
287
+ <div className="grid grid-cols-1 gap-2 sm:grid-cols-[11rem_minmax(0,1fr)]">
288
+ <select
289
+ value={draft.targetType}
290
+ onChange={(event) => {
291
+ const targetType = event.target.value as PipelineKind
292
+
293
+ setDraft((current) => ({ ...current, targetType, targetId: '' }))
294
+ }}
295
+ aria-label="Record type"
296
+ className={inputClass}
297
+ >
298
+ {PIPELINE_KINDS.map((kind) => (
299
+ <option key={kind} value={kind}>
300
+ {PIPELINE_KIND_LABELS[kind]}
301
+ </option>
302
+ ))}
303
+ </select>
304
+ <select
305
+ value={draft.targetId}
306
+ onChange={(event) => {
307
+ setDraft((current) => ({ ...current, targetId: event.target.value }))
308
+ }}
309
+ required
310
+ aria-label="Record"
311
+ className={inputClass}
312
+ >
313
+ <option value="">{targets.length === 0 ? 'No records' : 'Select…'}</option>
314
+ {targets.map((target) => (
315
+ <option key={target.id} value={target.id}>
316
+ {target.name}
317
+ </option>
318
+ ))}
319
+ </select>
320
+
321
+ <input
322
+ type="date"
323
+ value={draft.date}
324
+ onChange={(event) => {
325
+ setDraft((current) => ({ ...current, date: event.target.value }))
326
+ }}
327
+ required
328
+ aria-label="Date"
329
+ className={inputClass}
330
+ />
331
+ <input
332
+ value={draft.title}
333
+ onChange={(event) => {
334
+ setDraft((current) => ({ ...current, title: event.target.value }))
335
+ }}
336
+ placeholder="What happens…"
337
+ required
338
+ autoFocus
339
+ aria-label="What happens"
340
+ className={inputClass}
341
+ />
342
+
343
+ <select
344
+ value={draft.ownerId}
345
+ onChange={(event) => {
346
+ setDraft((current) => ({ ...current, ownerId: event.target.value }))
347
+ }}
348
+ aria-label="Owner"
349
+ className={inputClass}
350
+ >
351
+ <option value="">Unassigned</option>
352
+ {members.members.map((member) => (
353
+ <option key={member.id} value={member.id}>
354
+ {member.name}
355
+ </option>
356
+ ))}
357
+ </select>
358
+ <select
359
+ value={draft.status}
360
+ onChange={(event) => {
361
+ setDraft((current) => ({
362
+ ...current,
363
+ status: event.target.value as PlanItemStatus,
364
+ }))
365
+ }}
366
+ aria-label="Status"
367
+ className={inputClass}
368
+ >
369
+ {PLAN_ITEM_STATUSES.map((status) => (
370
+ <option key={status} value={status}>
371
+ {PLAN_ITEM_STATUS_LABELS[status]}
372
+ </option>
373
+ ))}
374
+ </select>
375
+ </div>
376
+ {error !== null && <ErrorPanel error={error} />}
377
+ <div className="flex justify-end gap-2">
378
+ <button
379
+ type="button"
380
+ onClick={onCancel}
381
+ className="rounded-md px-2.5 py-1.5 text-[12px] font-medium text-ink-muted hover:text-ink"
382
+ >
383
+ Cancel
384
+ </button>
385
+ <button
386
+ type="submit"
387
+ disabled={isPending || targets.length === 0}
388
+ className="h-9 rounded-md bg-accent px-3 text-[12px] font-semibold text-accent-fg hover:bg-accent-hover disabled:opacity-50"
389
+ >
390
+ {isPending ? 'Adding…' : 'Add'}
391
+ </button>
392
+ </div>
393
+ </form>
169
394
  )
170
395
  }
171
396
 
172
- function PlanList({ items }: { readonly items: readonly PlanItem[] }): React.JSX.Element {
397
+ function PlanList({
398
+ items,
399
+ targetNames,
400
+ }: {
401
+ readonly items: readonly PlanItem[]
402
+ readonly targetNames: ReadonlyMap<string, string>
403
+ }): React.JSX.Element {
173
404
  const members = useMembers()
174
- const targetNames = useTargetNames()
175
405
 
176
406
  if (items.length === 0) {
177
407
  return <p className="text-[13px] text-ink-faint">No plan items match this filter.</p>
@@ -18,7 +18,10 @@ import type { ChipTone } from '../components/Chip.tsx'
18
18
  import { DecisionsPanel } from '../components/DecisionsPanel.tsx'
19
19
  import { DeleteRecord } from '../components/DeleteRecord.tsx'
20
20
  import { EntitySearch } from '../components/EntitySearch.tsx'
21
+ import { CustomFieldsPanel } from '../components/CustomFieldsPanel.tsx'
22
+ import { useHasCustomFields } from '../components/useHasCustomFields.ts'
21
23
  import { InlineEdit } from '../components/InlineEdit.tsx'
24
+ import { ListsPanel } from '../components/ListsPanel.tsx'
22
25
  import { NotesPanel } from '../components/NotesPanel.tsx'
23
26
  import { PlanAttention } from '../components/PlanAttention.tsx'
24
27
  import { PlanPanel } from '../components/PlanPanel.tsx'
@@ -59,6 +62,7 @@ export function RaiseDetail(): React.JSX.Element {
59
62
  const { record, isLoading, isNotFound, error } = useRaise(id)
60
63
  const deleteRaise = useDeleteRaise()
61
64
  const moduleTabs = inSlotOrder(useRecordTabs('raise'))
65
+ const hasCustomFields = useHasCustomFields('raise')
62
66
  const [activeTab, setActiveTab] = useState('overview')
63
67
 
64
68
  if (isNotFound) {
@@ -75,10 +79,12 @@ export function RaiseDetail(): React.JSX.Element {
75
79
 
76
80
  const tabs: readonly RecordTabDescriptor<string>[] = [
77
81
  { id: 'overview', label: 'Overview' },
82
+ ...(hasCustomFields ? [{ id: 'fields', label: 'Fields' }] : []),
78
83
  { id: 'plan', label: 'Plan' },
79
84
  { id: 'activity', label: 'Activity' },
80
85
  { id: 'notes', label: 'Notes' },
81
86
  { id: 'decisions', label: 'Decisions' },
87
+ { id: 'lists', label: 'Lists' },
82
88
  ...moduleTabs.map((tab) => ({ id: tab.id, label: tab.label })),
83
89
  ]
84
90
  const active = tabs.some((tab) => tab.id === activeTab) ? activeTab : 'overview'
@@ -115,10 +121,12 @@ export function RaiseDetail(): React.JSX.Element {
115
121
 
116
122
  <RecordTabs tabs={tabs} active={active} onChange={setActiveTab} ariaLabel="Raise sections">
117
123
  {active === 'overview' && <RaiseOverview raise={record} />}
124
+ {active === 'fields' && <RaiseFields raise={record} />}
118
125
  {active === 'plan' && <PlanPanel targetType="raise" targetId={record.id} />}
119
126
  {active === 'activity' && <ActivitiesPanel targetType="raise" targetId={record.id} />}
120
127
  {active === 'notes' && <NotesPanel targetType="raise" targetId={record.id} />}
121
128
  {active === 'decisions' && <DecisionsPanel targetType="raise" targetId={record.id} />}
129
+ {active === 'lists' && <ListsPanel targetType="raise" targetId={record.id} />}
122
130
  {moduleTab?.render({ objectType: 'raise', recordId: record.id })}
123
131
  </RecordTabs>
124
132
  </div>
@@ -381,6 +389,22 @@ function RaiseSidebar({ raise }: { readonly raise: Raise }): React.JSX.Element {
381
389
  )
382
390
  }
383
391
 
392
+ function RaiseFields({ raise }: { readonly raise: Raise }): React.JSX.Element {
393
+ const { patch, error } = useRaisePatch(raise)
394
+ return (
395
+ <div className="space-y-4">
396
+ {error !== null && <ErrorPanel error={error} />}
397
+ <CustomFieldsPanel
398
+ objectType="raise"
399
+ values={raise.customFields}
400
+ onPatch={(customFields) => {
401
+ patch({ customFields })
402
+ }}
403
+ />
404
+ </div>
405
+ )
406
+ }
407
+
384
408
  /** The key people on the raise, linked and unlinked by replacing `person_ids`. */
385
409
  function RaiseKeyPeople({ raise }: { readonly raise: Raise }): React.JSX.Element {
386
410
  const { patch, error } = useRaisePatch(raise)
@@ -15,6 +15,7 @@ import { Chip } from '../components/Chip.tsx'
15
15
  import { DeleteRecord } from '../components/DeleteRecord.tsx'
16
16
  import { EntitySearch } from '../components/EntitySearch.tsx'
17
17
  import { InlineEdit } from '../components/InlineEdit.tsx'
18
+ import { Paginator } from '../components/Paginator.tsx'
18
19
  import { ErrorPanel, LoadingPanel, NotFoundPanel } from '../components/QueryState.tsx'
19
20
  import { SectionHeader } from '../components/SectionHeader.tsx'
20
21
  import {
@@ -240,34 +241,28 @@ function RoleCandidates({ role }: { readonly role: Role }): React.JSX.Element {
240
241
  ) : candidates.records.length === 0 && !adding ? (
241
242
  <p className="text-[13px] text-ink-faint">No candidates yet.</p>
242
243
  ) : (
243
- <ul className="overflow-hidden rounded-md border border-border">
244
- {candidates.records.map((candidate) => (
245
- <CandidateRow
246
- key={candidate.id}
247
- candidate={candidate}
248
- personName={names.nameFor(candidate.personId)}
249
- referrerName={
250
- candidate.referrerPersonId === null
251
- ? undefined
252
- : names.nameFor(candidate.referrerPersonId)
253
- }
254
- note={notes.noteFor(candidate.id)}
255
- noteResolved={notes.isComplete || notes.noteFor(candidate.id) !== undefined}
256
- />
257
- ))}
258
- </ul>
244
+ <>
245
+ <Paginator list={candidates} placement="top" />
246
+ <ul className="rounded-md border border-border">
247
+ {candidates.records.map((candidate) => (
248
+ <CandidateRow
249
+ key={candidate.id}
250
+ candidate={candidate}
251
+ personName={names.nameFor(candidate.personId)}
252
+ referrerName={
253
+ candidate.referrerPersonId === null
254
+ ? undefined
255
+ : names.nameFor(candidate.referrerPersonId)
256
+ }
257
+ note={notes.noteFor(candidate.id)}
258
+ noteResolved={notes.isComplete || notes.noteFor(candidate.id) !== undefined}
259
+ />
260
+ ))}
261
+ </ul>
262
+ </>
259
263
  )}
260
264
 
261
- {candidates.hasMore && (
262
- <button
263
- type="button"
264
- onClick={candidates.loadMore}
265
- disabled={candidates.isLoadingMore}
266
- 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"
267
- >
268
- {candidates.isLoadingMore ? 'Loading…' : 'Load more'}
269
- </button>
270
- )}
265
+ <Paginator list={candidates} />
271
266
  </section>
272
267
  )
273
268
  }
@@ -19,6 +19,7 @@ const COLLECTIONS: Readonly<Record<SearchCollection, { label: string; path: (id:
19
19
  person: { label: 'People', path: (id) => `/people/${id}` },
20
20
  role: { label: 'Roles', path: (id) => `/hiring/${id}` },
21
21
  company: { label: 'Companies', path: (id) => `/companies/${id}` },
22
+ enquiry: { label: 'Enquiries', path: (id) => `/enquiries/${id}` },
22
23
  deal: { label: 'Deals', path: (id) => `/deals/${id}` },
23
24
  opportunity: { label: 'Opportunities', path: (id) => `/opportunities/${id}` },
24
25
  raise: { label: 'Fundraising', path: (id) => `/fundraising/${id}` },