@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
@@ -0,0 +1,434 @@
1
+ import type { Enquiry, PipelineStage } from '@kelpie/schemas'
2
+ import { Link, useNavigate } from 'react-router'
3
+
4
+ import { useCompanies } from '../api/resources/companies.ts'
5
+ import {
6
+ useCreateEnquiry,
7
+ useEnquiries,
8
+ useUpdateEnquiry,
9
+ } from '../api/resources/enquiries.ts'
10
+ import { useMembers } from '../api/resources/members.ts'
11
+ import { usePipelineStages } from '../api/resources/pipelineStages.ts'
12
+ import { MAX_PAGE_SIZE, usePlanItems } from '../api/resources/planItems.ts'
13
+ import { useTimezone } from '../api/resources/account.ts'
14
+ import { Chip } from '../components/Chip.tsx'
15
+ import type { ChipTone } from '../components/Chip.tsx'
16
+ import { ColumnPicker } from '../components/ColumnPicker.tsx'
17
+ import { DataTable } from '../components/DataTable.tsx'
18
+ import type { Column, DataTableGroup } from '../components/DataTable.tsx'
19
+ import { KanbanBoard } from '../components/KanbanBoard.tsx'
20
+ import { PageHeader } from '../components/PageHeader.tsx'
21
+ import { Paginator } from '../components/Paginator.tsx'
22
+ import { ErrorPanel, LoadingPanel } from '../components/QueryState.tsx'
23
+ import { SegmentedControl } from '../components/SegmentedControl.tsx'
24
+ import { formatDate, formatDay } from '../lib/dates.ts'
25
+ import { useListView } from '../lib/listView.ts'
26
+ import { DUE_BUCKETS, byDateThenTitle, dueBucketFor, nextOpenByTarget } from '../lib/plan.ts'
27
+ import { serverSortOnly } from '../lib/sort.ts'
28
+
29
+ /**
30
+ * The Enquiries pipeline: inbound requests that may become a Deal. The same
31
+ * board-and-list pair as Opportunities over the shared kanban, with `source`
32
+ * in place of `kind` and a company that may be absent.
33
+ */
34
+
35
+ type BoardView = 'list' | 'columns'
36
+ type PipelineScope = 'open' | 'all'
37
+ type ListGrouping = 'stage' | 'due'
38
+
39
+ const SCOPES: readonly PipelineScope[] = ['open', 'all']
40
+ const GROUPINGS: readonly ListGrouping[] = ['stage', 'due']
41
+
42
+ function readScope(stored: string | undefined): PipelineScope {
43
+ return SCOPES.includes(stored as PipelineScope) ? (stored as PipelineScope) : 'open'
44
+ }
45
+
46
+ function readGrouping(stored: string | undefined): ListGrouping {
47
+ return GROUPINGS.includes(stored as ListGrouping) ? (stored as ListGrouping) : 'stage'
48
+ }
49
+
50
+ const COLUMN_KEYS: readonly string[] = [
51
+ 'name',
52
+ 'source',
53
+ 'company',
54
+ 'stage',
55
+ 'nextPlan',
56
+ 'owner',
57
+ 'tags',
58
+ 'summary',
59
+ 'createdAt',
60
+ 'updatedAt',
61
+ ]
62
+
63
+ /** Tones keyed by the seeded slugs. A renamed stage keeps its slug and its tone. */
64
+ const STAGE_TONES: Readonly<Record<string, ChipTone>> = {
65
+ new: 'accent',
66
+ in_progress: 'warning',
67
+ closed: 'neutral',
68
+ }
69
+
70
+ function stageTone(stage: PipelineStage): ChipTone {
71
+ return STAGE_TONES[stage.slug] ?? 'neutral'
72
+ }
73
+
74
+ const DEFAULT_VISIBLE_KEYS: readonly string[] = [
75
+ 'name',
76
+ 'source',
77
+ 'company',
78
+ 'nextPlan',
79
+ 'owner',
80
+ ]
81
+
82
+ const SERVER_SORT_KEYS: readonly string[] = ['name', 'created_at', 'updated_at']
83
+
84
+ export function EnquiriesPage(): React.JSX.Element {
85
+ const navigate = useNavigate()
86
+
87
+ const listView = useListView('enquiries', COLUMN_KEYS, DEFAULT_VISIBLE_KEYS)
88
+ const view: BoardView = listView.mode ?? 'columns'
89
+ const scope = readScope(listView.scope)
90
+ const grouping = readGrouping(listView.grouping)
91
+ const sort = listView.sort
92
+
93
+ const stages = usePipelineStages('enquiry')
94
+ const enquiries = useEnquiries({ sort: serverSortOnly(sort, SERVER_SORT_KEYS) })
95
+ const companies = useCompanies({ limit: 200 })
96
+ const members = useMembers()
97
+ const createEnquiry = useCreateEnquiry()
98
+ const updateEnquiry = useUpdateEnquiry()
99
+ const timezone = useTimezone()
100
+
101
+ const allStages = [...stages.records].sort((a, b) => a.sortOrder - b.sortOrder)
102
+ const visibleStages = scope === 'open' ? allStages.filter((stage) => stage.open) : allStages
103
+ const visibleStageIds = new Set(visibleStages.map((stage) => stage.id))
104
+ const visibleEnquiries = enquiries.records.filter((enquiry) =>
105
+ visibleStageIds.has(enquiry.stageId),
106
+ )
107
+
108
+ const askedIds = visibleEnquiries.slice(0, MAX_PAGE_SIZE).map((enquiry) => enquiry.id)
109
+ const planItems = usePlanItems(
110
+ {
111
+ targetType: 'enquiry',
112
+ targetIds: askedIds,
113
+ statuses: ['todo', 'in_progress'],
114
+ limit: MAX_PAGE_SIZE,
115
+ },
116
+ { enabled: askedIds.length > 0 },
117
+ )
118
+ const nextPlanByEnquiry = nextOpenByTarget(planItems.records)
119
+ const plansTruncated = visibleEnquiries.length > MAX_PAGE_SIZE || planItems.hasNext
120
+
121
+ const companyNameById = new Map(companies.records.map((company) => [company.id, company.name]))
122
+
123
+ async function addEnquiry(): Promise<void> {
124
+ const enquiry = await createEnquiry.runAsync({ name: 'New enquiry' })
125
+
126
+ await navigate(`/enquiries/${enquiry.id}`)
127
+ }
128
+
129
+ const stageLabelById = new Map(allStages.map((stage) => [stage.id, stage.label]))
130
+
131
+ const columns: readonly Column<Enquiry>[] = [
132
+ {
133
+ key: 'name',
134
+ header: 'Enquiry',
135
+ sortKey: 'name',
136
+ render: (enquiry) => <span className="font-medium text-ink">{enquiry.name}</span>,
137
+ },
138
+ {
139
+ key: 'source',
140
+ header: 'Source',
141
+ getSortValue: (enquiry) => enquiry.source || null,
142
+ render: (enquiry) => (enquiry.source.length > 0 ? enquiry.source : '—'),
143
+ },
144
+ {
145
+ key: 'company',
146
+ header: 'Company',
147
+ getSortValue: (enquiry) =>
148
+ enquiry.companyId === null ? null : (companyNameById.get(enquiry.companyId) ?? null),
149
+ render: (enquiry) =>
150
+ enquiry.companyId === null
151
+ ? '—'
152
+ : (companyNameById.get(enquiry.companyId) ?? '—'),
153
+ },
154
+ {
155
+ key: 'stage',
156
+ header: 'Stage',
157
+ getSortValue: (enquiry) => stageLabelById.get(enquiry.stageId) ?? null,
158
+ render: (enquiry) => stageLabelById.get(enquiry.stageId) ?? '—',
159
+ },
160
+ {
161
+ key: 'nextPlan',
162
+ header: 'Next plan',
163
+ getSortValue: (enquiry) => nextPlanByEnquiry.get(enquiry.id)?.date ?? null,
164
+ render: (enquiry) => {
165
+ const next = nextPlanByEnquiry.get(enquiry.id)
166
+
167
+ if (next === undefined) {
168
+ return '—'
169
+ }
170
+
171
+ return (
172
+ <span className="text-ink-muted">
173
+ {next.title}
174
+ <span className="ml-1.5 font-mono text-[11px] text-ink-faint">
175
+ {formatDay(next.date)}
176
+ </span>
177
+ </span>
178
+ )
179
+ },
180
+ },
181
+ {
182
+ key: 'owner',
183
+ header: 'Owner',
184
+ getSortValue: (enquiry) =>
185
+ enquiry.ownerId === null ? null : (members.nameById.get(enquiry.ownerId) ?? 'Unknown'),
186
+ render: (enquiry) =>
187
+ enquiry.ownerId === null
188
+ ? '—'
189
+ : (members.nameById.get(enquiry.ownerId) ?? 'Unknown'),
190
+ },
191
+ {
192
+ key: 'tags',
193
+ header: 'Tags',
194
+ getSortValue: (enquiry) => enquiry.tags.join(', ') || null,
195
+ render: (enquiry) =>
196
+ enquiry.tags.length === 0 ? '—' : (
197
+ <span className="flex flex-wrap gap-1">
198
+ {enquiry.tags.map((tag) => (
199
+ <Chip key={tag}>
200
+ <span className="text-[10px]">{tag}</span>
201
+ </Chip>
202
+ ))}
203
+ </span>
204
+ ),
205
+ },
206
+ {
207
+ key: 'summary',
208
+ header: 'Summary',
209
+ getSortValue: (enquiry) => enquiry.summary || null,
210
+ render: (enquiry) =>
211
+ enquiry.summary.length === 0 ? '—' : (
212
+ <span className="text-ink-muted">{enquiry.summary}</span>
213
+ ),
214
+ },
215
+ {
216
+ key: 'createdAt',
217
+ header: 'Created',
218
+ sortKey: 'created_at',
219
+ render: (enquiry) => formatDate(enquiry.createdAt, timezone),
220
+ },
221
+ {
222
+ key: 'updatedAt',
223
+ header: 'Updated',
224
+ sortKey: 'updated_at',
225
+ render: (enquiry) => formatDate(enquiry.updatedAt, timezone),
226
+ },
227
+ ]
228
+
229
+ const pickerOptions = columns.map((column) => ({ key: column.key, label: column.header }))
230
+
231
+ const stageGroups: readonly DataTableGroup<Enquiry>[] = visibleStages.map((stage) => ({
232
+ id: stage.id,
233
+ label: <Chip tone={stageTone(stage)}>{stage.label}</Chip>,
234
+ rows: visibleEnquiries.filter((enquiry) => enquiry.stageId === stage.id),
235
+ }))
236
+
237
+ const dueGroups: readonly DataTableGroup<Enquiry>[] = DUE_BUCKETS.map((bucket) => ({
238
+ id: bucket.id,
239
+ label: bucket.label,
240
+ rows: visibleEnquiries
241
+ .filter(
242
+ (enquiry) => dueBucketFor(nextPlanByEnquiry.get(enquiry.id)?.date) === bucket.id,
243
+ )
244
+ .sort((left, right) => {
245
+ const leftNext = nextPlanByEnquiry.get(left.id)
246
+ const rightNext = nextPlanByEnquiry.get(right.id)
247
+
248
+ if (leftNext === undefined || rightNext === undefined) {
249
+ return left.name.localeCompare(right.name)
250
+ }
251
+
252
+ return byDateThenTitle(leftNext, rightNext) || left.name.localeCompare(right.name)
253
+ }),
254
+ }))
255
+
256
+ const cards = visibleEnquiries.map((enquiry) => {
257
+ const next = nextPlanByEnquiry.get(enquiry.id)
258
+ const companyName =
259
+ enquiry.companyId === null ? undefined : companyNameById.get(enquiry.companyId)
260
+ const meta = [
261
+ enquiry.source.length > 0 ? enquiry.source : undefined,
262
+ companyName,
263
+ next?.title,
264
+ ]
265
+ .filter((part) => part !== undefined)
266
+ .join(' · ')
267
+
268
+ return {
269
+ id: enquiry.id,
270
+ stage: enquiry.stageId,
271
+ title: enquiry.name,
272
+ meta,
273
+ href: `/enquiries/${enquiry.id}`,
274
+ }
275
+ })
276
+
277
+ const isLoading = enquiries.isLoading || stages.isLoading
278
+ const loadError = enquiries.error ?? stages.error
279
+
280
+ return (
281
+ <div className="animate-fade-in">
282
+ <PageHeader
283
+ title="Enquiries"
284
+ onAdd={() => {
285
+ void addEnquiry()
286
+ }}
287
+ addLabel="Add enquiry"
288
+ actions={
289
+ <>
290
+ <Link
291
+ to="/enquiries/settings"
292
+ className="rounded-md border border-border bg-surface-raised px-2.5 py-1 text-[12px] font-medium text-ink-muted hover:text-ink"
293
+ >
294
+ Settings
295
+ </Link>
296
+ <SegmentedControl
297
+ ariaLabel="Stage scope"
298
+ value={scope}
299
+ onChange={(next) => {
300
+ listView.setScope(next)
301
+ }}
302
+ options={[
303
+ { id: 'open', label: 'Open' },
304
+ { id: 'all', label: 'All' },
305
+ ]}
306
+ />
307
+ {view === 'list' && (
308
+ <SegmentedControl
309
+ ariaLabel="Group the list"
310
+ value={grouping}
311
+ onChange={(next) => {
312
+ listView.setGrouping(next)
313
+ }}
314
+ options={[
315
+ { id: 'stage', label: 'By stage' },
316
+ { id: 'due', label: 'By due' },
317
+ ]}
318
+ />
319
+ )}
320
+ <SegmentedControl
321
+ ariaLabel="Board or list"
322
+ value={view}
323
+ onChange={(next) => {
324
+ listView.setMode(next)
325
+ }}
326
+ options={[
327
+ { id: 'list', label: 'List' },
328
+ { id: 'columns', label: 'Board' },
329
+ ]}
330
+ />
331
+ {view === 'list' && (
332
+ <ColumnPicker
333
+ options={pickerOptions}
334
+ visibleKeys={listView.visibleKeys}
335
+ onChange={listView.setVisibleKeys}
336
+ />
337
+ )}
338
+ </>
339
+ }
340
+ />
341
+
342
+ {createEnquiry.error !== null && (
343
+ <div className="mb-3">
344
+ <ErrorPanel error={createEnquiry.error} />
345
+ </div>
346
+ )}
347
+
348
+ {loadError !== null ? (
349
+ <ErrorPanel error={loadError} />
350
+ ) : isLoading ? (
351
+ <LoadingPanel label="Loading enquiries…" />
352
+ ) : (
353
+ <>
354
+ {view === 'list' && <Paginator list={enquiries} placement="top" />}
355
+ {view === 'columns' ? (
356
+ <KanbanBoard
357
+ stages={visibleStages.map((stage) => ({ id: stage.id, label: stage.label }))}
358
+ cards={cards}
359
+ onMove={(enquiryId, stageId) => {
360
+ const enquiry = enquiries.records.find(
361
+ (candidate) => candidate.id === enquiryId,
362
+ )
363
+
364
+ if (enquiry !== undefined && enquiry.stageId !== stageId) {
365
+ updateEnquiry.run({ id: enquiryId, changes: { stageId } })
366
+ }
367
+ }}
368
+ />
369
+ ) : (
370
+ <DataTable
371
+ columns={columns}
372
+ groups={grouping === 'stage' ? stageGroups : dueGroups}
373
+ getRowId={(enquiry) => enquiry.id}
374
+ onRowClick={(enquiry) => {
375
+ void navigate(`/enquiries/${enquiry.id}`)
376
+ }}
377
+ emptyMessage={
378
+ enquiries.records.length === 0
379
+ ? 'No enquiries yet'
380
+ : scope === 'open'
381
+ ? 'No open enquiries'
382
+ : 'No enquiries in this view'
383
+ }
384
+ emptyDescription={
385
+ enquiries.records.length > 0 && scope === 'open'
386
+ ? 'You have enquiries, but none are open right now.'
387
+ : undefined
388
+ }
389
+ emptyAction={
390
+ enquiries.records.length === 0
391
+ ? {
392
+ label: 'Add enquiry',
393
+ onClick: () => {
394
+ void addEnquiry()
395
+ },
396
+ }
397
+ : scope === 'open'
398
+ ? {
399
+ label: 'Show all enquiries',
400
+ onClick: () => {
401
+ listView.setScope('all')
402
+ },
403
+ }
404
+ : undefined
405
+ }
406
+ sort={sort}
407
+ onSortChange={(next) => {
408
+ listView.setSort(next)
409
+ }}
410
+ visibleColumnKeys={listView.visibleKeys}
411
+ />
412
+ )}
413
+ {updateEnquiry.error !== null && (
414
+ <div className="mt-3">
415
+ <ErrorPanel error={updateEnquiry.error} />
416
+ </div>
417
+ )}
418
+ {view === 'list' && <Paginator list={enquiries} />}
419
+ {companies.hasNext && (
420
+ <p className="mt-2 text-[11px] text-ink-faint">
421
+ More companies exist than one page returns, so some company names may show as “—”.
422
+ </p>
423
+ )}
424
+ {plansTruncated && (
425
+ <p className="mt-2 text-[11px] text-ink-faint">
426
+ More plan items exist than one page returns, so some enquiries may show no next
427
+ plan.
428
+ </p>
429
+ )}
430
+ </>
431
+ )}
432
+ </div>
433
+ )
434
+ }