@graph8/sdk 0.370.0 → 0.374.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 (646) hide show
  1. package/README.md +22 -3
  2. package/blocks/_foundation/README.md +61 -0
  3. package/blocks/_foundation/block.json +57 -0
  4. package/blocks/_foundation/components/auth-gate.tsx +363 -0
  5. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  6. package/blocks/_foundation/components/empty-state.tsx +5 -2
  7. package/blocks/_foundation/components/list-select.tsx +112 -0
  8. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  9. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  10. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  11. package/blocks/_foundation/components/screen/index.ts +1 -0
  12. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  13. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  14. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  15. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  16. package/blocks/_foundation/lib/paging.ts +57 -0
  17. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  18. package/blocks/_foundation/server/auth.ts +815 -0
  19. package/blocks/agents/README.md +81 -4
  20. package/blocks/agents/block.json +311 -2
  21. package/blocks/agents/components/agent-activity.tsx +112 -0
  22. package/blocks/agents/components/agent-approvals.tsx +135 -0
  23. package/blocks/agents/components/agent-builder.tsx +17 -3
  24. package/blocks/agents/components/agent-conversations.tsx +143 -0
  25. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
  26. package/blocks/agents/components/agent-operators.tsx +164 -0
  27. package/blocks/agents/components/agent-run-detail.tsx +20 -3
  28. package/blocks/agents/components/agent-runs.tsx +3 -3
  29. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  30. package/blocks/agents/components/agent-test-history.tsx +121 -0
  31. package/blocks/agents/components/agents-screen.tsx +53 -11
  32. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  33. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  34. package/blocks/agents/components/from-template-button.tsx +65 -0
  35. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  36. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  37. package/blocks/agents/components/knowledge-form.tsx +69 -0
  38. package/blocks/agents/components/name-dialog.tsx +32 -0
  39. package/blocks/agents/components/receptionist-button.tsx +46 -0
  40. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  41. package/blocks/agents/components/skill-editor.tsx +147 -0
  42. package/blocks/agents/components/skill-library.tsx +111 -0
  43. package/blocks/agents/lib/agent-types.ts +7 -1
  44. package/blocks/agents/lib/facts.tsx +13 -0
  45. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  46. package/blocks/agents/lib/load-more.tsx +141 -0
  47. package/blocks/agents/lib/skill-utils.ts +91 -0
  48. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  49. package/blocks/analytics/components/analytics-screen.tsx +16 -3
  50. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  51. package/blocks/analytics/components/report-builder.tsx +15 -2
  52. package/blocks/appointments/README.md +6 -0
  53. package/blocks/appointments/block.json +27 -0
  54. package/blocks/appointments/components/booking-page.tsx +2 -2
  55. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  56. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  57. package/blocks/appointments/components/meeting-list.tsx +29 -13
  58. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  59. package/blocks/campaigns/README.md +1 -1
  60. package/blocks/campaigns/block.json +34 -1
  61. package/blocks/campaigns/components/campaign-assets.tsx +1 -1
  62. package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
  63. package/blocks/campaigns/components/campaign-links.tsx +180 -0
  64. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  65. package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
  66. package/blocks/campaigns/components/icp-detail.tsx +11 -2
  67. package/blocks/campaigns/components/icp-editor.tsx +8 -3
  68. package/blocks/campaigns/components/persona-detail.tsx +11 -2
  69. package/blocks/campaigns/components/persona-editor.tsx +6 -1
  70. package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
  71. package/blocks/coach/components/call-scorecard.tsx +7 -7
  72. package/blocks/company-intelligence/README.md +132 -0
  73. package/blocks/company-intelligence/block.json +318 -0
  74. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  75. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  76. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  77. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  78. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  79. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  80. package/blocks/company-intelligence/components/research-run.tsx +165 -0
  81. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  82. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  83. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  84. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  85. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  86. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  87. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  88. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  89. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  90. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  91. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  92. package/blocks/copilot/components/copilot-panel.tsx +1 -1
  93. package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
  94. package/blocks/crm/README.md +25 -0
  95. package/blocks/crm/block.json +257 -9
  96. package/blocks/crm/components/bulk-update.tsx +119 -0
  97. package/blocks/crm/components/companies-screen.tsx +23 -9
  98. package/blocks/crm/components/company-detail.tsx +2 -2
  99. package/blocks/crm/components/contact-detail.tsx +4 -4
  100. package/blocks/crm/components/contact-documents-tab.tsx +144 -0
  101. package/blocks/crm/components/contact-insights-tab.tsx +118 -0
  102. package/blocks/crm/components/contact-lookup-link.tsx +85 -0
  103. package/blocks/crm/components/contact-row-actions.tsx +43 -0
  104. package/blocks/crm/components/contact-tag-chips.tsx +113 -0
  105. package/blocks/crm/components/contacts-screen.tsx +77 -18
  106. package/blocks/crm/components/dedupe-review.tsx +6 -1
  107. package/blocks/crm/components/import-wizard.tsx +3 -7
  108. package/blocks/crm/components/list-select.tsx +20 -0
  109. package/blocks/crm/components/owner-assign.tsx +132 -0
  110. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  111. package/blocks/crm/components/record-form.tsx +1 -1
  112. package/blocks/crm/components/record-open-links.tsx +79 -0
  113. package/blocks/crm/components/tag-assign.tsx +112 -0
  114. package/blocks/crm/components/tag-manager.tsx +178 -0
  115. package/blocks/crm/components/tasks-screen.tsx +35 -15
  116. package/blocks/crm/components/unlock-info-button.tsx +82 -0
  117. package/blocks/crm/components/verify-email-button.tsx +93 -0
  118. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  119. package/blocks/crm/hooks/use-contact-tags.ts +29 -0
  120. package/blocks/crm/hooks/use-page-query.ts +3 -1
  121. package/blocks/crm/lib/crm-types.ts +14 -0
  122. package/blocks/crm/lib/read-lists.ts +16 -0
  123. package/blocks/crm/lib/record-actions.ts +102 -0
  124. package/blocks/deals/README.md +25 -6
  125. package/blocks/deals/block.json +168 -2
  126. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  127. package/blocks/deals/components/create-deal-button.tsx +109 -0
  128. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  129. package/blocks/deals/components/deal-detail.tsx +7 -5
  130. package/blocks/deals/components/deal-filters.tsx +65 -0
  131. package/blocks/deals/components/deal-line-items.tsx +120 -0
  132. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  133. package/blocks/deals/components/deal-related.tsx +30 -9
  134. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  135. package/blocks/deals/components/deals-screen.tsx +57 -22
  136. package/blocks/deals/components/forecast-view.tsx +4 -4
  137. package/blocks/deals/components/pipeline-board.tsx +8 -6
  138. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  139. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  140. package/blocks/deals/components/stage-editor.tsx +1 -1
  141. package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
  142. package/blocks/deals/lib/deal-extras.ts +156 -0
  143. package/blocks/dialer/README.md +14 -5
  144. package/blocks/dialer/block.json +17 -2
  145. package/blocks/dialer/components/call-contact-button.tsx +26 -9
  146. package/blocks/dialer/components/call-log-screen.tsx +10 -2
  147. package/blocks/dialer/components/call-log.tsx +16 -4
  148. package/blocks/dialer/components/call-queue.tsx +3 -3
  149. package/blocks/dialer/components/dialer-screen.tsx +69 -27
  150. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  151. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  152. package/blocks/dialer/components/session-start-form.tsx +58 -11
  153. package/blocks/dialer/components/softphone.tsx +1 -1
  154. package/blocks/documents/README.md +86 -0
  155. package/blocks/documents/block.json +378 -0
  156. package/blocks/documents/components/account-bar.tsx +48 -0
  157. package/blocks/documents/components/correspondence-form.tsx +95 -0
  158. package/blocks/documents/components/correspondence-import.tsx +106 -0
  159. package/blocks/documents/components/correspondence-panel.tsx +191 -0
  160. package/blocks/documents/components/documents-screen.tsx +76 -0
  161. package/blocks/documents/components/library-document-detail.tsx +175 -0
  162. package/blocks/documents/components/library-document-form.tsx +105 -0
  163. package/blocks/documents/components/library-folder-form.tsx +74 -0
  164. package/blocks/documents/components/library-panel.tsx +235 -0
  165. package/blocks/documents/components/library-upload.tsx +160 -0
  166. package/blocks/documents/components/question-row.tsx +130 -0
  167. package/blocks/documents/components/questionnaire-detail.tsx +197 -0
  168. package/blocks/documents/components/questionnaires-panel.tsx +173 -0
  169. package/blocks/documents/components/record-documents-tab.tsx +109 -0
  170. package/blocks/documents/lib/documents-types.ts +252 -0
  171. package/blocks/documents/lib/download.ts +64 -0
  172. package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
  173. package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
  174. package/blocks/documents/recipes/organize-the-library.ts +25 -0
  175. package/blocks/email-signatures/README.md +123 -0
  176. package/blocks/email-signatures/block.json +341 -0
  177. package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
  178. package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
  179. package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
  180. package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
  181. package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
  182. package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
  183. package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
  184. package/blocks/email-signatures/components/signature-policy.tsx +130 -0
  185. package/blocks/email-signatures/components/signature-preview.tsx +119 -0
  186. package/blocks/email-signatures/components/signature-report.tsx +77 -0
  187. package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
  188. package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
  189. package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
  190. package/blocks/email-signatures/lib/signatures-types.ts +392 -0
  191. package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
  192. package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
  193. package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
  194. package/blocks/enrichment/README.md +38 -10
  195. package/blocks/enrichment/block.json +164 -12
  196. package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
  197. package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
  198. package/blocks/enrichment/components/bulk-enrich-panel.tsx +79 -30
  199. package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
  200. package/blocks/enrichment/components/enrich-button.tsx +1 -1
  201. package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
  202. package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
  203. package/blocks/enrichment/components/enrichment-screen.tsx +103 -19
  204. package/blocks/enrichment/components/list-pipelines.tsx +97 -0
  205. package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
  206. package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
  207. package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
  208. package/blocks/enrichment/components/waterfall-config.tsx +27 -7
  209. package/blocks/enrichment/lib/enrichment-types.ts +51 -0
  210. package/blocks/enrichment/lib/pipeline-types.ts +234 -0
  211. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  212. package/blocks/forms/README.md +10 -0
  213. package/blocks/forms/block.json +107 -0
  214. package/blocks/forms/components/form-activity.tsx +71 -0
  215. package/blocks/forms/components/form-backfill.tsx +108 -0
  216. package/blocks/forms/components/form-builder.tsx +1 -1
  217. package/blocks/forms/components/form-embed.tsx +1 -1
  218. package/blocks/forms/components/form-groups.tsx +151 -0
  219. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  220. package/blocks/forms/components/form-route-history.tsx +72 -0
  221. package/blocks/forms/components/form-saved-views.tsx +81 -0
  222. package/blocks/forms/components/forms-screen.tsx +57 -20
  223. package/blocks/forms/components/more-rows.tsx +30 -0
  224. package/blocks/forms/components/submissions-export.tsx +67 -0
  225. package/blocks/forms/components/submissions-table.tsx +15 -6
  226. package/blocks/forms/lib/forms-extra.ts +67 -0
  227. package/blocks/inbox/README.md +45 -4
  228. package/blocks/inbox/block.json +145 -8
  229. package/blocks/inbox/components/blocked-senders.tsx +103 -0
  230. package/blocks/inbox/components/compose-message.tsx +193 -0
  231. package/blocks/inbox/components/inbox-screen.tsx +54 -6
  232. package/blocks/inbox/components/inbox-search.tsx +106 -0
  233. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  234. package/blocks/inbox/components/tag-manager.tsx +136 -0
  235. package/blocks/inbox/components/thread-actions.tsx +133 -0
  236. package/blocks/inbox/components/thread-badge.tsx +2 -2
  237. package/blocks/inbox/components/thread-view.tsx +3 -1
  238. package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
  239. package/blocks/inbox/lib/inbox-manage.ts +227 -0
  240. package/blocks/integrations/README.md +54 -1
  241. package/blocks/integrations/block.json +189 -15
  242. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  243. package/blocks/integrations/components/connection-health.tsx +59 -0
  244. package/blocks/integrations/components/crm-detail.tsx +20 -1
  245. package/blocks/integrations/components/crm-records.tsx +103 -0
  246. package/blocks/integrations/components/field-mappings.tsx +61 -0
  247. package/blocks/integrations/components/integration-list.tsx +4 -4
  248. package/blocks/integrations/components/integrations-screen.tsx +20 -3
  249. package/blocks/integrations/components/migration-panel.tsx +133 -0
  250. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  251. package/blocks/integrations/components/sync-status.tsx +6 -6
  252. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  253. package/blocks/integrations/components/webhook-detail.tsx +26 -7
  254. package/blocks/integrations/components/webhook-manager.tsx +30 -17
  255. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  256. package/blocks/integrations/lib/crm-utils.ts +39 -0
  257. package/blocks/integrations/lib/facts.tsx +13 -0
  258. package/blocks/integrations/lib/integrations-utils.ts +5 -0
  259. package/blocks/integrations/lib/load-more.tsx +141 -0
  260. package/blocks/intent/components/account-intent-score.tsx +5 -5
  261. package/blocks/intent/components/intent-feed.tsx +8 -8
  262. package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
  263. package/blocks/intent/lib/intent-utils.ts +4 -4
  264. package/blocks/lifecycle/components/account-related.tsx +8 -1
  265. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  266. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  267. package/blocks/lists/README.md +7 -1
  268. package/blocks/lists/block.json +31 -3
  269. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  270. package/blocks/lists/components/add-to-list-button.tsx +17 -7
  271. package/blocks/lists/components/list-picker.tsx +13 -3
  272. package/blocks/lists/components/list-table.tsx +1 -1
  273. package/blocks/lists/components/lists-screen.tsx +4 -2
  274. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  275. package/blocks/lists/components/suppression-manager.tsx +27 -6
  276. package/blocks/lists/components/suppressions-screen.tsx +21 -12
  277. package/blocks/mailboxes/block.json +4 -0
  278. package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
  279. package/blocks/mailboxes/components/domain-detail.tsx +10 -4
  280. package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
  281. package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
  282. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  283. package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
  284. package/blocks/mailboxes/components/warmup-health.tsx +20 -20
  285. package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
  286. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  287. package/blocks/newsletter/README.md +12 -0
  288. package/blocks/newsletter/block.json +117 -0
  289. package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
  290. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  291. package/blocks/newsletter/components/more-rows.tsx +30 -0
  292. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  293. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  294. package/blocks/newsletter/components/newsletter-screen.tsx +38 -13
  295. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  296. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  297. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  298. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  299. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  300. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  301. package/blocks/nurture/components/nurture-builder.tsx +10 -2
  302. package/blocks/nurture/components/nurture-enroll.tsx +6 -7
  303. package/blocks/nurture/components/nurture-form.tsx +5 -8
  304. package/blocks/nurture/components/nurture-screen.tsx +16 -5
  305. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  306. package/blocks/objects/README.md +1 -1
  307. package/blocks/objects/block.json +13 -5
  308. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  309. package/blocks/objects/components/object-record-detail.tsx +8 -1
  310. package/blocks/objects/components/object-schema-editor.tsx +1 -1
  311. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  312. package/blocks/pages/README.md +14 -0
  313. package/blocks/pages/block.json +123 -2
  314. package/blocks/pages/components/more-rows.tsx +30 -0
  315. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  316. package/blocks/pages/components/page-analytics.tsx +176 -0
  317. package/blocks/pages/components/page-clone.tsx +99 -0
  318. package/blocks/pages/components/page-domains.tsx +167 -0
  319. package/blocks/pages/components/page-editor.tsx +1 -1
  320. package/blocks/pages/components/page-generation-status.tsx +48 -0
  321. package/blocks/pages/components/page-tracking.tsx +137 -0
  322. package/blocks/pages/components/pages-screen.tsx +57 -16
  323. package/blocks/pages/components/seo-report.tsx +6 -1
  324. package/blocks/pages/hooks/use-landing-page-paths.ts +60 -0
  325. package/blocks/pages/lib/studio.ts +7 -0
  326. package/blocks/phone-numbers/README.md +74 -0
  327. package/blocks/phone-numbers/block.json +262 -0
  328. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  329. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  330. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  331. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  332. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  333. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  334. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  335. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  336. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  337. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  338. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  339. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  340. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  341. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  342. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  343. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  344. package/blocks/pickers/README.md +23 -0
  345. package/blocks/pickers/block.json +103 -0
  346. package/blocks/pickers/components/company-picker.tsx +90 -0
  347. package/blocks/pickers/components/contact-picker.tsx +131 -0
  348. package/blocks/pickers/components/member-picker.tsx +99 -0
  349. package/blocks/pickers/components/search-picker.tsx +98 -0
  350. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  351. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  352. package/blocks/pickers/lib/use-debounced.ts +13 -0
  353. package/blocks/programmatic-pages/README.md +82 -0
  354. package/blocks/programmatic-pages/block.json +292 -0
  355. package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
  356. package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
  357. package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
  358. package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
  359. package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
  360. package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
  361. package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
  362. package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
  363. package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
  364. package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
  365. package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
  366. package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
  367. package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
  368. package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
  369. package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
  370. package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
  371. package/blocks/quotes/README.md +38 -1
  372. package/blocks/quotes/block.json +170 -6
  373. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  374. package/blocks/quotes/components/manual-products.tsx +144 -0
  375. package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
  376. package/blocks/quotes/components/quote-builder.tsx +1 -1
  377. package/blocks/quotes/components/quote-documents.tsx +94 -0
  378. package/blocks/quotes/components/quote-evidence.tsx +95 -0
  379. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  380. package/blocks/quotes/components/quote-list.tsx +28 -8
  381. package/blocks/quotes/components/quote-related.tsx +3 -2
  382. package/blocks/quotes/components/quote-settings.tsx +184 -0
  383. package/blocks/quotes/components/quote-templates.tsx +189 -0
  384. package/blocks/quotes/components/quote-viewer.tsx +23 -5
  385. package/blocks/quotes/components/quotes-screen.tsx +15 -5
  386. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  387. package/blocks/quotes/lib/quote-admin.ts +264 -0
  388. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  389. package/blocks/radar/components/radar-overview.tsx +4 -4
  390. package/blocks/radar/components/radar-screen.tsx +2 -2
  391. package/blocks/sequencer/README.md +61 -0
  392. package/blocks/sequencer/block.json +200 -6
  393. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  394. package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
  395. package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
  396. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  397. package/blocks/sequencer/components/sequence-builder.tsx +32 -23
  398. package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
  399. package/blocks/sequencer/components/sequence-detail.tsx +10 -2
  400. package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
  401. package/blocks/sequencer/components/sequence-enrollments.tsx +5 -2
  402. package/blocks/sequencer/components/sequence-list.tsx +1 -1
  403. package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
  404. package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
  405. package/blocks/sequencer/components/sequence-stats.tsx +8 -8
  406. package/blocks/sequencer/components/sequence-templates.tsx +160 -0
  407. package/blocks/sequencer/components/sequences-screen.tsx +39 -6
  408. package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
  409. package/blocks/sequencer/components/step-editor.tsx +21 -12
  410. package/blocks/sequencer/components/step-templates.tsx +224 -0
  411. package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
  412. package/blocks/sequencer/lib/sequencer-utils.ts +9 -5
  413. package/blocks/signals/README.md +39 -0
  414. package/blocks/signals/block.json +287 -0
  415. package/blocks/signals/components/auto-seed-panel.tsx +111 -0
  416. package/blocks/signals/components/listener-form.tsx +184 -0
  417. package/blocks/signals/components/matched-posts.tsx +195 -0
  418. package/blocks/signals/components/post-engagements.tsx +114 -0
  419. package/blocks/signals/components/record-social-tab.tsx +49 -0
  420. package/blocks/signals/components/social-listeners-screen.tsx +250 -0
  421. package/blocks/signals/components/social-settings.tsx +128 -0
  422. package/blocks/signals/components/source-lists.tsx +83 -0
  423. package/blocks/signals/components/top-engagers.tsx +68 -0
  424. package/blocks/signals/components/x-integration.tsx +129 -0
  425. package/blocks/signals/lib/signals-types.ts +228 -0
  426. package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
  427. package/blocks/signals/recipes/spend-against-caps.ts +14 -0
  428. package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
  429. package/blocks/social-ads/README.md +8 -0
  430. package/blocks/social-ads/block.json +137 -4
  431. package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
  432. package/blocks/social-ads/components/audience-sync.tsx +6 -2
  433. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  434. package/blocks/social-ads/components/campaign-detail.tsx +6 -1
  435. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  436. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  437. package/blocks/social-ads/components/creative-detail.tsx +6 -1
  438. package/blocks/social-ads/components/creative-gallery.tsx +20 -2
  439. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  440. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  441. package/blocks/social-ads/components/post-composer.tsx +4 -2
  442. package/blocks/social-ads/components/post-detail.tsx +3 -0
  443. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  444. package/blocks/social-ads/components/social-performance.tsx +80 -0
  445. package/blocks/social-ads/components/social-screen.tsx +33 -6
  446. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  447. package/blocks/surveys/README.md +91 -0
  448. package/blocks/surveys/block.json +299 -0
  449. package/blocks/surveys/components/account-nps.tsx +69 -0
  450. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  451. package/blocks/surveys/components/org-summary.tsx +82 -0
  452. package/blocks/surveys/components/sending-domains.tsx +113 -0
  453. package/blocks/surveys/components/survey-form.tsx +126 -0
  454. package/blocks/surveys/components/survey-responses.tsx +145 -0
  455. package/blocks/surveys/components/survey-results.tsx +72 -0
  456. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  457. package/blocks/surveys/components/survey-send.tsx +162 -0
  458. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  459. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  460. package/blocks/surveys/lib/surveys-types.ts +228 -0
  461. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  462. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  463. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  464. package/blocks/visitors/components/snippet-install.tsx +18 -9
  465. package/blocks/visitors/components/traffic-chart.tsx +7 -6
  466. package/blocks/visitors/components/visitor-company-card.tsx +24 -14
  467. package/blocks/visitors/components/visitor-feed.tsx +5 -5
  468. package/blocks/web-analytics/README.md +102 -0
  469. package/blocks/web-analytics/block.json +292 -0
  470. package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
  471. package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
  472. package/blocks/web-analytics/components/audience-tab.tsx +135 -0
  473. package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
  474. package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
  475. package/blocks/web-analytics/components/overview-tab.tsx +102 -0
  476. package/blocks/web-analytics/components/pages-tab.tsx +148 -0
  477. package/blocks/web-analytics/components/ranked-table.tsx +103 -0
  478. package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
  479. package/blocks/web-analytics/components/report-card.tsx +71 -0
  480. package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
  481. package/blocks/web-analytics/hooks/use-report.ts +48 -0
  482. package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
  483. package/blocks/web-analytics/lib/page-path.ts +14 -0
  484. package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
  485. package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
  486. package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
  487. package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
  488. package/blocks/webchat/components/chat-widget-config.tsx +1 -1
  489. package/blocks/webchat/components/conversation-list.tsx +1 -1
  490. package/blocks/workbench/README.md +54 -2
  491. package/blocks/workbench/block.json +234 -5
  492. package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
  493. package/blocks/workbench/components/run-progress.tsx +1 -1
  494. package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
  495. package/blocks/workbench/components/workbench-automation.tsx +36 -0
  496. package/blocks/workbench/components/workbench-connections.tsx +136 -0
  497. package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
  498. package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
  499. package/blocks/workbench/components/workbench-grid.tsx +71 -8
  500. package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
  501. package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
  502. package/blocks/workbench/components/workbench-schedule.tsx +191 -0
  503. package/blocks/workbench/components/workbench-screen.tsx +65 -22
  504. package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
  505. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  506. package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
  507. package/blocks/workbench/components/workbench-triggers.tsx +178 -0
  508. package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
  509. package/blocks/workbench/lib/api-status.ts +8 -0
  510. package/blocks/workbench/lib/automation-types.ts +257 -0
  511. package/blocks/workflows/components/workflow-editor.tsx +1 -1
  512. package/dist/cli.js +744 -394
  513. package/dist/index.browser.js +1 -1
  514. package/dist/index.browser.js.map +1 -1
  515. package/dist/index.browser.mjs +1 -1
  516. package/dist/index.browser.mjs.map +1 -1
  517. package/dist/index.d.mts +2 -2
  518. package/dist/index.d.ts +2 -2
  519. package/dist/index.js +1 -1
  520. package/dist/index.js.map +1 -1
  521. package/dist/index.mjs +1 -1
  522. package/dist/index.mjs.map +1 -1
  523. package/dist/react.browser.js +1 -1
  524. package/dist/react.browser.js.map +1 -1
  525. package/dist/react.browser.mjs +1 -1
  526. package/dist/react.browser.mjs.map +1 -1
  527. package/dist/react.d.mts +2 -2
  528. package/dist/react.d.ts +2 -2
  529. package/dist/react.js +1 -1
  530. package/dist/react.js.map +1 -1
  531. package/dist/react.mjs +1 -1
  532. package/dist/react.mjs.map +1 -1
  533. package/dist/types/blocks/auth.d.ts +45 -0
  534. package/dist/types/blocks/brand.d.ts +11 -2
  535. package/dist/types/blocks/create.d.ts +11 -4
  536. package/dist/types/blocks/doctor.d.ts +1 -0
  537. package/dist/types/blocks/flows.d.ts +7 -1
  538. package/dist/types/blocks/orgBrand.d.ts +34 -0
  539. package/dist/types/cli.d.ts +3 -0
  540. package/dist/types/version.d.ts +2 -2
  541. package/docs/api/desk.d.ts +2 -2
  542. package/docs/api/desk.md +3 -3
  543. package/docs/api/forms.md +1 -1
  544. package/package.json +1 -1
  545. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  546. package/stacks/6sense/pages/contacts.tsx.tmpl +21 -0
  547. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  548. package/stacks/6sense.json +37 -4
  549. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  550. package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
  551. package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
  552. package/stacks/apollo/pages/contacts.tsx.tmpl +35 -95
  553. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  554. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  555. package/stacks/apollo/pages/lists.tsx.tmpl +24 -0
  556. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  557. package/stacks/apollo/pages/sequences.tsx.tmpl +18 -0
  558. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  559. package/stacks/apollo.json +68 -9
  560. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  561. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  562. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  563. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  564. package/stacks/calendly.json +8 -4
  565. package/stacks/clay/pages/companies.tsx.tmpl +17 -1
  566. package/stacks/clay/pages/contacts.tsx.tmpl +14 -0
  567. package/stacks/clay/pages/lists.tsx.tmpl +24 -0
  568. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  569. package/stacks/clay.json +16 -3
  570. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  571. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  572. package/stacks/gong/pages/contacts.tsx.tmpl +34 -0
  573. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  574. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  575. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  576. package/stacks/gong.json +23 -6
  577. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  578. package/stacks/hootsuite/pages/contacts.tsx.tmpl +30 -0
  579. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  580. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  581. package/stacks/hootsuite.json +19 -4
  582. package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
  583. package/stacks/hubspot/pages/companies.tsx.tmpl +16 -35
  584. package/stacks/hubspot/pages/contacts.tsx.tmpl +35 -95
  585. package/stacks/hubspot/pages/deals.tsx.tmpl +14 -1
  586. package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
  587. package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
  588. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  589. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  590. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  591. package/stacks/hubspot/pages/sequences.tsx.tmpl +18 -0
  592. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  593. package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
  594. package/stacks/hubspot.json +66 -7
  595. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  596. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  597. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  598. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  599. package/stacks/intercom.json +18 -4
  600. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  601. package/stacks/mailchimp/pages/contacts.tsx.tmpl +30 -0
  602. package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
  603. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  604. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  605. package/stacks/mailchimp.json +22 -6
  606. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  607. package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
  608. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  609. package/stacks/outreach/pages/contacts.tsx.tmpl +29 -66
  610. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  611. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  612. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  613. package/stacks/outreach/pages/sequences.tsx.tmpl +18 -0
  614. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  615. package/stacks/outreach.json +38 -9
  616. package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
  617. package/stacks/pipedrive/pages/contacts.tsx.tmpl +17 -35
  618. package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
  619. package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
  620. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  621. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  622. package/stacks/pipedrive.json +14 -2
  623. package/stacks/salesforce/pages/companies.tsx.tmpl +16 -35
  624. package/stacks/salesforce/pages/contacts.tsx.tmpl +26 -35
  625. package/stacks/salesforce/pages/deals.tsx.tmpl +14 -1
  626. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  627. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  628. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  629. package/stacks/salesforce.json +17 -3
  630. package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
  631. package/stacks/zoominfo/pages/contacts.tsx.tmpl +16 -0
  632. package/stacks/zoominfo/pages/lists.tsx.tmpl +24 -0
  633. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  634. package/stacks/zoominfo.json +36 -3
  635. package/styles/styles.css +1 -1
  636. package/templates/auth/auth-route.ts.tmpl +16 -0
  637. package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
  638. package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
  639. package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
  640. package/templates/auth/g8.auth.password.ts.tmpl +18 -0
  641. package/templates/auth/g8.auth.session.ts.tmpl +23 -0
  642. package/templates/auth/middleware.ts.tmpl +14 -0
  643. package/templates/next-app/README.md.tmpl +5 -0
  644. package/templates/next-app/app/globals.css.tmpl +349 -64
  645. package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
  646. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
@@ -0,0 +1,103 @@
1
+ {
2
+ "$schema": "../block.schema.json",
3
+ "id": "pickers",
4
+ "title": "Pickers",
5
+ "summary": "No raw ids: signed-in member hook plus searchable team-member, contact and company pickers that report the id operations take.",
6
+ "features": [
7
+ "useCurrentMember: the signed-in teammate (user id, email, name) from GET /team-members/me",
8
+ "MemberPicker: searchable team-member select, paged",
9
+ "ContactPicker: searchable contact select (single or multiple), by name or work email, paged",
10
+ "CompanyPicker: searchable company select by name or domain, paged",
11
+ "SearchPicker + usePickerPages: the shell and paging engine to build another picker"
12
+ ],
13
+ "dependsOn": [
14
+ "foundation"
15
+ ],
16
+ "operations": [
17
+ "get_my_team_member_team_members_me_get",
18
+ "list_companies_companies_get",
19
+ "list_contacts_contacts_get",
20
+ "list_team_members_team_members_get"
21
+ ],
22
+ "components": [
23
+ {
24
+ "name": "SearchPicker",
25
+ "file": "components/search-picker.tsx",
26
+ "purpose": "The dropdown shell behind the pickers: trigger, search box, listbox, Show more, loading/empty/error states. Calls no API.",
27
+ "operations": [],
28
+ "tiers": []
29
+ },
30
+ {
31
+ "name": "MemberPicker",
32
+ "file": "components/member-picker.tsx",
33
+ "purpose": "Searchable team-member select (paged, fetched on open). Reports the user id operations take, so no screen asks a person to type one.",
34
+ "operations": [
35
+ "list_team_members_team_members_get"
36
+ ],
37
+ "tiers": [
38
+ "read"
39
+ ]
40
+ },
41
+ {
42
+ "name": "ContactPicker",
43
+ "file": "components/contact-picker.tsx",
44
+ "purpose": "Searchable contact select by name or work email (paged), single or multiple with chips. Reports the contact id, so no screen asks a person to type one.",
45
+ "operations": [
46
+ "list_contacts_contacts_get"
47
+ ],
48
+ "tiers": [
49
+ "read"
50
+ ]
51
+ },
52
+ {
53
+ "name": "CompanyPicker",
54
+ "file": "components/company-picker.tsx",
55
+ "purpose": "Searchable company select by name or domain (paged). Reports the company id, so no screen asks a person to type one.",
56
+ "operations": [
57
+ "list_companies_companies_get"
58
+ ],
59
+ "tiers": [
60
+ "read"
61
+ ]
62
+ }
63
+ ],
64
+ "hooks": [
65
+ {
66
+ "name": "useCurrentMember",
67
+ "file": "hooks/use-current-member.ts",
68
+ "purpose": "The signed-in team member (user id, email, name) from GET /team-members/me, shared by the dialer, CallContactButton and role/owner defaults. An organisation key has no person: `forbidden` is true and callers offer a MemberPicker.",
69
+ "operations": [
70
+ "get_my_team_member_team_members_me_get"
71
+ ]
72
+ },
73
+ {
74
+ "name": "usePickerPages",
75
+ "file": "hooks/use-picker-pages.ts",
76
+ "purpose": "Paging + debounced search engine behind ContactPicker and CompanyPicker: load more appends, a new search starts over, a stale response is dropped."
77
+ }
78
+ ],
79
+ "lib": [
80
+ {
81
+ "file": "lib/use-debounced.ts",
82
+ "purpose": "useDebounced: a value after it stops changing; the search box of every picker reads through it."
83
+ }
84
+ ],
85
+ "recipes": [],
86
+ "prerequisites": [],
87
+ "scopes": [
88
+ "companies:read",
89
+ "contacts:read",
90
+ "team:read"
91
+ ],
92
+ "defaults": [
93
+ {
94
+ "decision": "Which person a screen acts as when nobody passed a user id",
95
+ "default": "The signed-in team member (a personal API key). An organisation key has no person: ask with MemberPicker",
96
+ "askHuman": false
97
+ }
98
+ ],
99
+ "peerDependencies": {
100
+ "react": ">=18",
101
+ "lucide-react": ">=0.400.0"
102
+ }
103
+ }
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ /**
4
+ * CompanyPicker: choose a company by name instead of typing a company id. A searchable select over the organisation's
5
+ * companies; it reports the company (id, name, hint) so a screen can show who was chosen and send the id.
6
+ *
7
+ * SDK operations, in order:
8
+ * 1. GET /companies?name|domain&page&limit (companies.listCompanies) read - fetched only once the picker is opened
9
+ *
10
+ * Search: a company name (partial match); text that looks like a domain ("acme.com") is looked up as the domain.
11
+ * Paged with usePickerPages: 25 per page, "Show more" keeps the earlier pages, a new search starts over.
12
+ */
13
+ import { useState } from "react";
14
+ import { g8Get } from "@/g8/lib/g8-client";
15
+ import { usePickerPages } from "../hooks/use-picker-pages";
16
+ import { SearchPicker } from "./search-picker";
17
+
18
+ export interface CompanyOption {
19
+ id: number;
20
+ name: string;
21
+ /** Domain and industry: what tells two companies with a similar name apart. */
22
+ hint?: string;
23
+ }
24
+
25
+ export interface CompanyPickerProps {
26
+ value?: CompanyOption | null;
27
+ onChange: (company: CompanyOption) => void;
28
+ /** Accessible name and trigger prefix. Default "Company". */
29
+ label?: string;
30
+ placeholder?: string;
31
+ disabled?: boolean;
32
+ /** Id of the trigger button, so a <label htmlFor> can name it. */
33
+ id?: string;
34
+ className?: string;
35
+ }
36
+
37
+ interface Row { id?: number | null; name?: string | null; domain?: string | null; industry?: string | null }
38
+
39
+ const PAGE = 25;
40
+ const DOMAIN = /^[^\s@/]+\.[a-z]{2,}$/i;
41
+
42
+ /** A companies-list row as a selectable option; null without an id. */
43
+ export function toCompanyOption(r: Row): CompanyOption | null {
44
+ if (r.id == null) return null;
45
+ const hint = [r.domain, r.industry].filter(Boolean).join(" · ");
46
+ return { id: r.id, name: r.name || r.domain || `Company ${r.id}`, hint: hint || undefined };
47
+ }
48
+
49
+ export function CompanyPicker({ value, onChange, label = "Company", placeholder, disabled, id, className }: CompanyPickerProps) {
50
+ const [open, setOpen] = useState(false);
51
+ const pages = usePickerPages<CompanyOption>(open, async (q, page, signal) => {
52
+ const filter = DOMAIN.test(q) ? { domain: q.toLowerCase() } : { name: q || undefined };
53
+ const env = await g8Get("/companies", { ...filter, page, limit: PAGE }, { signal });
54
+ const rows = ((env.data ?? []) as Row[]).map(toCompanyOption).filter((o): o is CompanyOption => o !== null);
55
+ const has = (env.pagination as { has_next?: boolean } | undefined)?.has_next;
56
+ return { rows, hasNext: has ?? rows.length >= PAGE };
57
+ });
58
+ const pick = (key: string) => {
59
+ const c = pages.rows.find((r) => String(r.id) === key);
60
+ if (!c) return;
61
+ setOpen(false);
62
+ pages.setSearch("");
63
+ onChange(c);
64
+ };
65
+ return (
66
+ <SearchPicker
67
+ label={label}
68
+ id={id}
69
+ className={className}
70
+ disabled={disabled}
71
+ placeholder={placeholder}
72
+ searchPlaceholder="Search by name or domain"
73
+ triggerContent={value ? <span>{value.name}</span> : undefined}
74
+ options={pages.rows.map((r) => ({ key: String(r.id), label: r.name, hint: r.hint }))}
75
+ selectedKeys={new Set(value ? [String(value.id)] : [])}
76
+ onSelect={pick}
77
+ search={pages.search}
78
+ onSearchChange={pages.setSearch}
79
+ open={open}
80
+ onOpenChange={setOpen}
81
+ loading={pages.loading}
82
+ error={pages.error}
83
+ onRetry={pages.refetch}
84
+ hasMore={pages.hasMore}
85
+ loadingMore={pages.loadingMore}
86
+ onLoadMore={pages.loadMore}
87
+ emptyText={pages.q ? "No company matches that search." : "No companies yet."}
88
+ />
89
+ );
90
+ }
@@ -0,0 +1,131 @@
1
+ "use client";
2
+
3
+ /**
4
+ * ContactPicker: choose one or several contacts by name instead of typing contact ids. A searchable select over the
5
+ * organisation's contacts; it reports the contact (id, name, hint), so a screen can show who was chosen and send the id.
6
+ *
7
+ * SDK operations, in order:
8
+ * 1. GET /contacts?name|email&page&limit (contacts.listContacts) read - fetched only once the picker is opened
9
+ *
10
+ * Search: a first or last name (partial match); text containing "@" is looked up as a work email (exact match).
11
+ * Paged (usePickerPages): 25 contacts per page, "Show more" loads the next page and keeps the earlier ones; a new search
12
+ * starts over and an out-of-date response never lands. Locked emails and phones come back as "***" and are never shown.
13
+ * `multiple` keeps the panel open and shows the chosen contacts as removable chips.
14
+ */
15
+ import { useState } from "react";
16
+ import { X } from "lucide-react";
17
+ import { g8Get } from "@/g8/lib/g8-client";
18
+ import { usePickerPages } from "../hooks/use-picker-pages";
19
+ import { SearchPicker } from "./search-picker";
20
+
21
+ /** One selectable contact. */
22
+ export interface ContactOption {
23
+ id: number;
24
+ name: string;
25
+ /** Job title and company, or the work email: what tells two people with the same name apart. */
26
+ hint?: string;
27
+ }
28
+
29
+ export interface ContactPickerProps {
30
+ /** Single mode: the chosen contact (controlled). */
31
+ value?: ContactOption | null;
32
+ /** Single mode: called with the contact the person chose. */
33
+ onChange?: (contact: ContactOption) => void;
34
+ /** Several contacts: tick as many as needed. Use `selected` and `onSelectedChange`. */
35
+ multiple?: boolean;
36
+ selected?: ContactOption[];
37
+ onSelectedChange?: (contacts: ContactOption[]) => void;
38
+ /** Accessible name and trigger prefix. Default "Contact". */
39
+ label?: string;
40
+ placeholder?: string;
41
+ disabled?: boolean;
42
+ /** Id of the trigger button, so a <label htmlFor> can name it. */
43
+ id?: string;
44
+ className?: string;
45
+ }
46
+
47
+ interface Row {
48
+ id?: number | null;
49
+ first_name?: string | null;
50
+ last_name?: string | null;
51
+ job_title?: string | null;
52
+ company_name?: string | null;
53
+ work_email?: string | null;
54
+ }
55
+
56
+ const PAGE = 25;
57
+ const real = (v?: string | null): string => (v && !v.includes("*") ? v.trim() : "");
58
+
59
+ /** A contacts-list row as a selectable option; null without an id. */
60
+ export function toContactOption(r: Row): ContactOption | null {
61
+ if (r.id == null) return null;
62
+ const email = real(r.work_email);
63
+ const name = [real(r.first_name), real(r.last_name)].filter(Boolean).join(" ") || email || `Contact ${r.id}`;
64
+ const where = [real(r.job_title), real(r.company_name)].filter(Boolean).join(" at ");
65
+ return { id: r.id, name, hint: where || (email && email !== name ? email : undefined) };
66
+ }
67
+
68
+ export function ContactPicker(props: ContactPickerProps) {
69
+ const { value, onChange, multiple, selected = [], onSelectedChange, label = "Contact", placeholder, disabled, id, className } = props;
70
+ const [open, setOpen] = useState(false);
71
+ const pages = usePickerPages<ContactOption>(open, async (q, page, signal) => {
72
+ const filter = q.includes("@") ? { email: q } : { name: q || undefined };
73
+ const env = await g8Get("/contacts", { ...filter, page, limit: PAGE }, { signal });
74
+ const rows = ((env.data ?? []) as Row[]).map(toContactOption).filter((o): o is ContactOption => o !== null);
75
+ const has = (env.pagination as { has_next?: boolean } | undefined)?.has_next;
76
+ return { rows, hasNext: has ?? rows.length >= PAGE };
77
+ });
78
+ const { rows, q } = pages;
79
+ const byId = new Map(rows.map((r) => [String(r.id), r]));
80
+ const chosenKeys = new Set((multiple ? selected : value ? [value] : []).map((c) => String(c.id)));
81
+
82
+ const toggle = (key: string) => {
83
+ const c = byId.get(key) ?? [...selected, ...(value ? [value] : [])].find((x) => String(x.id) === key);
84
+ if (!c) return;
85
+ if (multiple) {
86
+ onSelectedChange?.(chosenKeys.has(key) ? selected.filter((x) => String(x.id) !== key) : [...selected, c]);
87
+ return;
88
+ }
89
+ setOpen(false);
90
+ pages.setSearch("");
91
+ onChange?.(c);
92
+ };
93
+ return (
94
+ <div className={`flex flex-col gap-2 ${className ?? ""}`}>
95
+ <SearchPicker
96
+ label={label}
97
+ id={id}
98
+ disabled={disabled}
99
+ placeholder={placeholder ?? (multiple ? "Add contacts" : undefined)}
100
+ searchPlaceholder="Search by name or work email"
101
+ triggerContent={!multiple && value ? <span>{value.name}</span> : undefined}
102
+ options={rows.map((r) => ({ key: String(r.id), label: r.name, hint: r.hint }))}
103
+ selectedKeys={chosenKeys}
104
+ onSelect={toggle}
105
+ search={pages.search}
106
+ onSearchChange={pages.setSearch}
107
+ open={open}
108
+ onOpenChange={setOpen}
109
+ loading={pages.loading}
110
+ error={pages.error}
111
+ onRetry={pages.refetch}
112
+ hasMore={pages.hasMore}
113
+ loadingMore={pages.loadingMore}
114
+ onLoadMore={pages.loadMore}
115
+ emptyText={q ? "No contact matches that search." : "No contacts yet."}
116
+ />
117
+ {multiple && selected.length > 0 ? (
118
+ <ul aria-label={`Chosen ${label.toLowerCase()}s`} className="flex flex-wrap gap-1">
119
+ {selected.map((c) => (
120
+ <li key={c.id} className="flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs">
121
+ <span>{c.name}</span>
122
+ <button type="button" aria-label={`Remove ${c.name}`} className="text-muted-foreground hover:text-foreground" disabled={disabled} onClick={() => onSelectedChange?.(selected.filter((x) => x.id !== c.id))}>
123
+ <X className="size-3" aria-hidden />
124
+ </button>
125
+ </li>
126
+ ))}
127
+ </ul>
128
+ ) : null}
129
+ </div>
130
+ );
131
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ /**
4
+ * MemberPicker: choose a teammate by name instead of typing a user id. A searchable select over the organisation's
5
+ * team; the value it reports is the user id the API operations take (a dialer rep, a record owner, a role holder).
6
+ *
7
+ * SDK operations, in order:
8
+ * 1. GET /team-members?search&status&limit (teamMembers.listTeamMembers) read - fetched only once the picker is opened
9
+ *
10
+ * Paged: it loads 25 people and "Show more" asks for the next 25 (the search box narrows by name or email). People
11
+ * without a user id (a roster row that never signed in) are left out, because no operation could take them.
12
+ * A 403 (a key that may not read the team) shows a plain note instead of an error panel; the caller keeps its own
13
+ * fallback, usually the signed-in member from `useCurrentMember`.
14
+ */
15
+ import { useState } from "react";
16
+ import { g8Get } from "@/g8/lib/g8-client";
17
+ import { useDebounced } from "../lib/use-debounced";
18
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
19
+ import { memberRecordsOf, toMemberOption } from "../hooks/use-current-member";
20
+ import type { MemberOption } from "../hooks/use-current-member";
21
+ import { SearchPicker } from "./search-picker";
22
+
23
+ export type { MemberOption } from "../hooks/use-current-member";
24
+
25
+ export interface MemberPickerProps {
26
+ /** The selected user id (controlled). */
27
+ value?: string | null;
28
+ onChange: (member: MemberOption) => void;
29
+ /** What to show for `value` when that person is not on the loaded page (for example the current holder of a role). */
30
+ selectedName?: string | null;
31
+ /** Accessible name and trigger prefix. Default "Team member". */
32
+ label?: string;
33
+ placeholder?: string;
34
+ disabled?: boolean;
35
+ /** Id of the trigger button, so a <label htmlFor> can name it. */
36
+ id?: string;
37
+ className?: string;
38
+ }
39
+
40
+ const PAGE = 25;
41
+
42
+ export function MemberPicker({ value, onChange, selectedName, label = "Team member", placeholder, disabled, id, className }: MemberPickerProps) {
43
+ const [open, setOpen] = useState(false);
44
+ const [search, setSearch] = useState("");
45
+ const [limit, setLimit] = useState(PAGE);
46
+ const [chosen, setChosen] = useState<MemberOption | null>(null);
47
+ const q = useDebounced(search.trim());
48
+ const query = useG8Query(
49
+ async (signal) => {
50
+ const env = await g8Get("/team-members", { search: q || undefined, status: "active", limit, offset: 0 }, { signal });
51
+ const records = memberRecordsOf(env);
52
+ // `raw` is what the API returned, before any filtering here: "Show more" asks for more when the page was full.
53
+ const active = records.filter((m) => !m.status || m.status === "active");
54
+ return { rows: active.map(toMemberOption).filter((o): o is MemberOption => o !== null), raw: records.length };
55
+ },
56
+ [q, limit],
57
+ { enabled: open },
58
+ );
59
+ const rows = query.data?.rows ?? [];
60
+ const status = (query.error as { status?: number } | undefined)?.status;
61
+ const forbidden = status === 403;
62
+ const shown = rows.find((m) => m.id === value) ?? (chosen?.id === value ? chosen : null);
63
+ const name = shown?.name ?? selectedName ?? value ?? null;
64
+
65
+ const pick = (id: string) => {
66
+ const member = rows.find((m) => m.id === id);
67
+ if (!member) return;
68
+ setChosen(member);
69
+ setOpen(false);
70
+ setSearch("");
71
+ onChange(member);
72
+ };
73
+ return (
74
+ <SearchPicker
75
+ label={label}
76
+ id={id}
77
+ className={className}
78
+ disabled={disabled}
79
+ placeholder={placeholder}
80
+ searchPlaceholder="Search by name or email"
81
+ triggerContent={name ? <span>{name}</span> : undefined}
82
+ options={rows.map((m) => ({ key: m.id, label: m.name, hint: m.email }))}
83
+ selectedKeys={new Set(value ? [value] : [])}
84
+ onSelect={pick}
85
+ search={search}
86
+ onSearchChange={(v) => { setSearch(v); setLimit(PAGE); }}
87
+ open={open}
88
+ onOpenChange={setOpen}
89
+ loading={query.loading}
90
+ error={forbidden ? undefined : query.error}
91
+ onRetry={() => void query.refetch()}
92
+ errorOverride={forbidden ? <p role="status" className="p-2 text-sm text-muted-foreground">This API key cannot read the team list.</p> : undefined}
93
+ hasMore={(query.data?.raw ?? 0) >= limit}
94
+ loadingMore={query.loading && rows.length > 0}
95
+ onLoadMore={() => setLimit((l) => l + PAGE)}
96
+ emptyText={q ? "Nobody matches that search." : "No team members yet."}
97
+ />
98
+ );
99
+ }
@@ -0,0 +1,98 @@
1
+ "use client";
2
+
3
+ /**
4
+ * SearchPicker: the dropdown shell behind MemberPicker and ContactPicker. A trigger button opens a panel with a search
5
+ * box, a listbox of options, a "Show more" pager and the loading / empty / error states. It owns no data: the caller
6
+ * passes the options and the search text, so any lookup (team members, contacts, ...) reuses one accessible widget.
7
+ *
8
+ * It calls no API itself. Closing: Escape, a click outside, or (single mode) choosing an option. In `multiple` mode the
9
+ * panel stays open so several options can be ticked.
10
+ */
11
+ import { useEffect, useRef } from "react";
12
+ import type { ReactNode } from "react";
13
+ import { Check, ChevronDown } from "lucide-react";
14
+ import { Button, Input } from "@graph8/sdk/ui";
15
+ import { ApiError } from "@/g8/components/api-error";
16
+
17
+ export interface SearchPickerOption {
18
+ key: string;
19
+ label: string;
20
+ /** A second muted line: a job title, an email. */
21
+ hint?: string | null;
22
+ }
23
+
24
+ export interface SearchPickerProps {
25
+ /** Accessible name of the whole control ("Team member", "Contact"). */
26
+ label: string;
27
+ /** What the closed trigger shows; defaults to the placeholder. */
28
+ triggerContent?: ReactNode;
29
+ placeholder?: string;
30
+ searchPlaceholder?: string;
31
+ options: SearchPickerOption[];
32
+ selectedKeys: ReadonlySet<string>;
33
+ onSelect: (key: string) => void;
34
+ search: string;
35
+ onSearchChange: (value: string) => void;
36
+ open: boolean;
37
+ onOpenChange: (open: boolean) => void;
38
+ loading?: boolean;
39
+ error?: unknown;
40
+ onRetry?: () => void;
41
+ hasMore?: boolean;
42
+ loadingMore?: boolean;
43
+ onLoadMore?: () => void;
44
+ /** Shown instead of the list when there is nothing to pick. */
45
+ emptyText?: string;
46
+ /** Replaces the error panel for an expected refusal (for example a 403 on the team list). */
47
+ errorOverride?: ReactNode;
48
+ disabled?: boolean;
49
+ /** Id of the trigger button, so a <label htmlFor> can name it. */
50
+ id?: string;
51
+ className?: string;
52
+ }
53
+
54
+ export function SearchPicker(props: SearchPickerProps) {
55
+ const { label, open, onOpenChange, options, selectedKeys, loading, error, disabled, className } = props;
56
+ const root = useRef<HTMLDivElement>(null);
57
+ useEffect(() => {
58
+ if (!open) return;
59
+ const away = (e: MouseEvent) => { if (root.current && !root.current.contains(e.target as Node)) onOpenChange(false); };
60
+ document.addEventListener("mousedown", away);
61
+ return () => document.removeEventListener("mousedown", away);
62
+ }, [open, onOpenChange]);
63
+
64
+ const empty = !loading && !error && options.length === 0;
65
+ return (
66
+ <div ref={root} className={`relative inline-block min-w-48 ${className ?? ""}`} onKeyDown={(e) => { if (e.key === "Escape" && open) { e.stopPropagation(); onOpenChange(false); } }}>
67
+ <Button id={props.id} type="button" variant="outline" size="sm" className="w-full justify-between" disabled={disabled} aria-label={label} aria-haspopup="listbox" aria-expanded={open} onClick={() => onOpenChange(!open)}>
68
+ <span className="truncate">{props.triggerContent ?? <span className="text-muted-foreground">{props.placeholder ?? `Choose ${label.toLowerCase()}`}</span>}</span>
69
+ <ChevronDown className="ml-2 size-4 shrink-0 text-muted-foreground" aria-hidden />
70
+ </Button>
71
+ {open ? (
72
+ <div className="absolute left-0 z-50 mt-1 flex w-72 max-w-[90vw] flex-col gap-2 rounded-md border border-border bg-background p-2 shadow-md">
73
+ <Input autoFocus aria-label={`Search ${label.toLowerCase()}`} placeholder={props.searchPlaceholder ?? "Search"} value={props.search} onChange={(e) => props.onSearchChange(e.target.value)} />
74
+ {props.errorOverride ?? (error ? <ApiError error={error} title={`Could not load ${label.toLowerCase()} options`} onRetry={props.onRetry} /> : null)}
75
+ <ul role="listbox" aria-label={label} className="max-h-56 overflow-y-auto">
76
+ {loading && options.length === 0 ? <li className="p-2 text-sm text-muted-foreground">Loading...</li> : null}
77
+ {empty ? <li className="p-2 text-sm text-muted-foreground">{props.emptyText ?? "Nothing matches."}</li> : null}
78
+ {options.map((o) => {
79
+ const on = selectedKeys.has(o.key);
80
+ return (
81
+ <li key={o.key} role="none">
82
+ <button type="button" role="option" aria-selected={on} className={`flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent ${on ? "bg-accent font-medium" : ""}`} onClick={() => props.onSelect(o.key)}>
83
+ <span className="flex min-w-0 flex-1 flex-col">
84
+ <span className="truncate">{o.label}</span>
85
+ {o.hint ? <span className="truncate text-xs text-muted-foreground">{o.hint}</span> : null}
86
+ </span>
87
+ {on ? <Check className="size-4 shrink-0" aria-hidden /> : null}
88
+ </button>
89
+ </li>
90
+ );
91
+ })}
92
+ </ul>
93
+ {props.hasMore ? <Button type="button" variant="ghost" size="sm" loading={props.loadingMore} onClick={props.onLoadMore}>Show more</Button> : null}
94
+ </div>
95
+ ) : null}
96
+ </div>
97
+ );
98
+ }
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ /**
4
+ * useCurrentMember: the signed-in team member, so no screen has to ask a person for their own user id.
5
+ *
6
+ * It is the ONE place that resolves "who am I" for a PERSONAL API key: the dialer's rep, a record owner default and
7
+ * CallContactButton all read it, so they cannot drift apart. An organisation key has no person: the API answers 403
8
+ * and `member` stays null (`forbidden: true`); callers then offer a MemberPicker instead of a text box.
9
+ *
10
+ * SDK operations, in order:
11
+ * 1. GET /team-members/me (teamMembers.getMyTeamMember) read - the roster record of the person who minted the key
12
+ *
13
+ * `userId` is the identity-provider user id (`propelauth_user_id`): the id the dialer's softphone credential, session
14
+ * drafts and record roles take. `email` is the matching rep email; both describe the same person.
15
+ */
16
+ import { g8Get } from "@/g8/lib/g8-client";
17
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
18
+
19
+ /** A roster record as the team-members endpoints answer it (the API may return more). */
20
+ export interface TeamMemberRecord {
21
+ id?: string | null;
22
+ name?: string | null;
23
+ first_name?: string | null;
24
+ last_name?: string | null;
25
+ email?: string | null;
26
+ propelauth_user_id?: string | null;
27
+ status?: string | null;
28
+ role?: string | null;
29
+ }
30
+
31
+ /** One selectable person. `id` is the identity-provider user id, the value every id-taking operation wants. */
32
+ export interface MemberOption {
33
+ id: string;
34
+ name: string;
35
+ email?: string | null;
36
+ }
37
+
38
+ /** The API envelope's `data`, or the value itself when it was already unwrapped. */
39
+ export function unwrapData<T>(env: unknown): T {
40
+ const o = env as { data?: unknown } | null | undefined;
41
+ return (o && typeof o === "object" && "data" in o ? o.data : env) as T;
42
+ }
43
+
44
+ /** A roster record as a selectable person; null when it has no user id (it cannot be a rep or an owner). */
45
+ export function toMemberOption(m: TeamMemberRecord | null | undefined): MemberOption | null {
46
+ const id = m?.propelauth_user_id;
47
+ if (!m || !id) return null;
48
+ const full = [m.first_name, m.last_name].filter(Boolean).join(" ");
49
+ return { id, name: m.name || full || m.email || id, email: m.email };
50
+ }
51
+
52
+ /** The list endpoints answer a bare array, or an object with `items` / `team_members`. */
53
+ export function memberRecordsOf(env: unknown): TeamMemberRecord[] {
54
+ const inner = unwrapData<unknown>(env);
55
+ if (Array.isArray(inner)) return inner as TeamMemberRecord[];
56
+ const o = inner as { items?: unknown; team_members?: unknown } | null;
57
+ const list = o?.items ?? o?.team_members;
58
+ return Array.isArray(list) ? (list as TeamMemberRecord[]) : [];
59
+ }
60
+
61
+ /** The signed-in member's record, or null. A 403 (organisation key) and a 404 both read as "no person". */
62
+ export async function fetchCurrentMember(signal?: AbortSignal): Promise<TeamMemberRecord | null> {
63
+ return unwrapData<TeamMemberRecord | null>(await g8Get("/team-members/me", undefined, { signal })) ?? null;
64
+ }
65
+
66
+ export interface UseCurrentMemberOptions {
67
+ /** Set false to hold the request (for example when the caller already passed a `userId`). Default true. */
68
+ enabled?: boolean;
69
+ }
70
+
71
+ export interface CurrentMember {
72
+ /** The signed-in person as a selectable option; null for an organisation key, while loading, or on failure. */
73
+ member: MemberOption | null;
74
+ /** `propelauth_user_id`: what the dialer, softphone and role assignments take. */
75
+ userId?: string;
76
+ email?: string;
77
+ name?: string;
78
+ /** The raw roster record (role, status ...), for callers that need more than the id. */
79
+ record: TeamMemberRecord | null;
80
+ loading: boolean;
81
+ /** The key is an organisation key: it has no person, so ask with a MemberPicker. */
82
+ forbidden: boolean;
83
+ /** Any failure other than the expected 403/404 of an organisation key. */
84
+ error: unknown;
85
+ }
86
+
87
+ export function useCurrentMember(options: UseCurrentMemberOptions = {}): CurrentMember {
88
+ const enabled = options.enabled ?? true;
89
+ const q = useG8Query((signal) => fetchCurrentMember(signal), [], { enabled });
90
+ const status = (q.error as { status?: number } | undefined)?.status;
91
+ const refusal = status === 403 || status === 404;
92
+ const record = enabled ? (q.data ?? null) : null;
93
+ const member = toMemberOption(record);
94
+ return {
95
+ member,
96
+ userId: member?.id,
97
+ email: record?.email ?? undefined,
98
+ name: member?.name,
99
+ record,
100
+ loading: enabled && q.loading && !q.data,
101
+ forbidden: enabled && status === 403,
102
+ error: enabled && q.error && !refusal ? q.error : undefined,
103
+ };
104
+ }