@graph8/sdk 0.362.0 → 0.373.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 (615) hide show
  1. package/README.md +33 -10
  2. package/blocks/_foundation/README.md +78 -1
  3. package/blocks/_foundation/block.json +123 -6
  4. package/blocks/_foundation/components/api-error.tsx +9 -7
  5. package/blocks/_foundation/components/brand-provider.tsx +23 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  7. package/blocks/_foundation/components/confirmed-action.tsx +90 -0
  8. package/blocks/_foundation/components/connect-graph8.tsx +280 -0
  9. package/blocks/_foundation/components/empty-state.tsx +5 -2
  10. package/blocks/_foundation/components/list-select.tsx +112 -0
  11. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  12. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  13. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  14. package/blocks/_foundation/components/screen/index.ts +2 -0
  15. package/blocks/_foundation/components/screen/record-link.tsx +76 -0
  16. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  17. package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
  18. package/blocks/_foundation/components/screen/time-series-chart.tsx +15 -97
  19. package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
  20. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  21. package/blocks/_foundation/hooks/use-brand.ts +52 -0
  22. package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
  23. package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
  24. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  25. package/blocks/_foundation/hooks/use-paginated.ts +120 -0
  26. package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
  27. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  28. package/blocks/_foundation/lib/g8-cache.ts +454 -0
  29. package/blocks/_foundation/lib/g8-client.ts +1 -1
  30. package/blocks/_foundation/lib/paging.ts +57 -0
  31. package/blocks/_foundation/lib/plural.ts +23 -0
  32. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  33. package/blocks/_foundation/server/connect-route.ts +15 -0
  34. package/blocks/_foundation/server/connect.ts +127 -0
  35. package/blocks/_foundation/server/local-key.ts +70 -0
  36. package/blocks/_foundation/server/proxy.ts +103 -3
  37. package/blocks/_foundation/server/route.ts +16 -4
  38. package/blocks/agents/README.md +93 -2
  39. package/blocks/agents/block.json +343 -2
  40. package/blocks/agents/components/agent-activity.tsx +112 -0
  41. package/blocks/agents/components/agent-approvals.tsx +135 -0
  42. package/blocks/agents/components/agent-builder.tsx +29 -10
  43. package/blocks/agents/components/agent-conversations.tsx +143 -0
  44. package/blocks/agents/components/agent-knowledge-panel.tsx +4 -3
  45. package/blocks/agents/components/agent-operators.tsx +164 -0
  46. package/blocks/agents/components/agent-run-detail.tsx +119 -0
  47. package/blocks/agents/components/agent-runs.tsx +19 -10
  48. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  49. package/blocks/agents/components/agent-test-history.tsx +121 -0
  50. package/blocks/agents/components/agents-screen.tsx +226 -0
  51. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  52. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  53. package/blocks/agents/components/from-template-button.tsx +65 -0
  54. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  55. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  56. package/blocks/agents/components/knowledge-form.tsx +69 -0
  57. package/blocks/agents/components/name-dialog.tsx +32 -0
  58. package/blocks/agents/components/receptionist-button.tsx +46 -0
  59. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  60. package/blocks/agents/components/skill-editor.tsx +147 -0
  61. package/blocks/agents/components/skill-library.tsx +111 -0
  62. package/blocks/agents/lib/agent-types.ts +7 -1
  63. package/blocks/agents/lib/facts.tsx +13 -0
  64. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  65. package/blocks/agents/lib/load-more.tsx +141 -0
  66. package/blocks/agents/lib/skill-utils.ts +91 -0
  67. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  68. package/blocks/analytics/README.md +13 -0
  69. package/blocks/analytics/block.json +45 -1
  70. package/blocks/analytics/components/analytics-screen.tsx +222 -0
  71. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  72. package/blocks/analytics/components/leaderboard.tsx +14 -7
  73. package/blocks/analytics/components/report-builder.tsx +38 -11
  74. package/blocks/analytics/components/report-detail.tsx +84 -0
  75. package/blocks/analytics/components/trend-chart.tsx +62 -0
  76. package/blocks/appointments/README.md +6 -0
  77. package/blocks/appointments/block.json +33 -2
  78. package/blocks/appointments/components/availability-editor.tsx +17 -5
  79. package/blocks/appointments/components/booking-page.tsx +2 -2
  80. package/blocks/appointments/components/event-type-editor.tsx +2 -1
  81. package/blocks/appointments/components/event-type-related.tsx +4 -2
  82. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  83. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  84. package/blocks/appointments/components/meeting-list.tsx +38 -18
  85. package/blocks/appointments/components/meetings-screen.tsx +7 -4
  86. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  87. package/blocks/appointments/components/scheduling-screen.tsx +2 -1
  88. package/blocks/block.schema.json +18 -0
  89. package/blocks/block.types.ts +17 -0
  90. package/blocks/campaigns/README.md +41 -11
  91. package/blocks/campaigns/block.json +105 -42
  92. package/blocks/campaigns/components/campaign-assets.tsx +17 -6
  93. package/blocks/campaigns/components/campaign-launch-step.tsx +114 -26
  94. package/blocks/campaigns/components/campaign-list.tsx +7 -3
  95. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  96. package/blocks/campaigns/components/campaign-wizard.tsx +52 -14
  97. package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
  98. package/blocks/campaigns/components/icp-detail.tsx +163 -0
  99. package/blocks/campaigns/components/icp-editor.tsx +22 -17
  100. package/blocks/campaigns/components/persona-detail.tsx +181 -0
  101. package/blocks/campaigns/components/persona-editor.tsx +19 -11
  102. package/blocks/campaigns/lib/campaigns-utils.ts +138 -5
  103. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
  104. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
  105. package/blocks/coach/README.md +10 -0
  106. package/blocks/coach/block.json +16 -1
  107. package/blocks/coach/components/call-scorecard.tsx +7 -7
  108. package/blocks/coach/components/coach-screen.tsx +222 -0
  109. package/blocks/company-intelligence/README.md +132 -0
  110. package/blocks/company-intelligence/block.json +318 -0
  111. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  112. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  113. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  114. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  115. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  116. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  117. package/blocks/company-intelligence/components/research-run.tsx +161 -0
  118. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  119. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  120. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  121. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  122. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  123. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  124. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  125. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  126. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  127. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  128. package/blocks/copilot/README.md +13 -0
  129. package/blocks/copilot/block.json +14 -1
  130. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  131. package/blocks/copilot/components/copilot-panel.tsx +14 -5
  132. package/blocks/copilot/components/copilot-screen.tsx +148 -0
  133. package/blocks/copilot/components/copilot-team-chat.tsx +13 -6
  134. package/blocks/crm/README.md +34 -0
  135. package/blocks/crm/block.json +107 -2
  136. package/blocks/crm/components/companies-screen.tsx +359 -0
  137. package/blocks/crm/components/company-detail.tsx +10 -8
  138. package/blocks/crm/components/company-table.tsx +11 -4
  139. package/blocks/crm/components/contact-detail.tsx +10 -7
  140. package/blocks/crm/components/contact-lookup-link.tsx +44 -0
  141. package/blocks/crm/components/contact-table.tsx +10 -3
  142. package/blocks/crm/components/contacts-screen.tsx +377 -0
  143. package/blocks/crm/components/dedupe-review.tsx +13 -6
  144. package/blocks/crm/components/import-wizard.tsx +7 -9
  145. package/blocks/crm/components/list-select.tsx +20 -0
  146. package/blocks/crm/components/notes-panel.tsx +4 -4
  147. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  148. package/blocks/crm/components/record-form.tsx +6 -4
  149. package/blocks/crm/components/task-list.tsx +4 -4
  150. package/blocks/crm/components/tasks-screen.tsx +399 -0
  151. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  152. package/blocks/crm/hooks/use-custom-fields.ts +6 -1
  153. package/blocks/crm/hooks/use-page-query.ts +9 -1
  154. package/blocks/crm/lib/crm-types.ts +26 -2
  155. package/blocks/crm/lib/read-lists.ts +16 -0
  156. package/blocks/crm/lib/screen-parts.tsx +105 -0
  157. package/blocks/deals/README.md +24 -6
  158. package/blocks/deals/block.json +160 -5
  159. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  160. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  161. package/blocks/deals/components/deal-detail.tsx +25 -13
  162. package/blocks/deals/components/deal-filters.tsx +65 -0
  163. package/blocks/deals/components/deal-line-items.tsx +120 -0
  164. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  165. package/blocks/deals/components/deal-related.tsx +31 -10
  166. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  167. package/blocks/deals/components/deals-screen.tsx +66 -26
  168. package/blocks/deals/components/forecast-view.tsx +4 -4
  169. package/blocks/deals/components/pipeline-board.tsx +8 -6
  170. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  171. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  172. package/blocks/deals/components/stage-editor.tsx +5 -5
  173. package/blocks/deals/hooks/use-pipeline-deals.ts +21 -4
  174. package/blocks/deals/hooks/use-pipelines.ts +1 -1
  175. package/blocks/deals/lib/deal-extras.ts +156 -0
  176. package/blocks/dialer/README.md +45 -4
  177. package/blocks/dialer/block.json +84 -3
  178. package/blocks/dialer/components/call-contact-button.tsx +269 -0
  179. package/blocks/dialer/components/call-log-screen.tsx +208 -0
  180. package/blocks/dialer/components/call-log.tsx +23 -7
  181. package/blocks/dialer/components/call-queue.tsx +4 -4
  182. package/blocks/dialer/components/dialer-screen.tsx +361 -0
  183. package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
  184. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  185. package/blocks/dialer/components/live-calls-table.tsx +7 -3
  186. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  187. package/blocks/dialer/components/session-start-form.tsx +61 -12
  188. package/blocks/dialer/components/softphone.tsx +4 -2
  189. package/blocks/enrichment/README.md +29 -7
  190. package/blocks/enrichment/block.json +49 -9
  191. package/blocks/enrichment/components/bulk-enrich-panel.tsx +83 -29
  192. package/blocks/enrichment/components/email-verify-badge.tsx +31 -11
  193. package/blocks/enrichment/components/enrich-button.tsx +32 -9
  194. package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
  195. package/blocks/enrichment/components/enrichment-screen.tsx +341 -0
  196. package/blocks/enrichment/components/waterfall-config.tsx +66 -17
  197. package/blocks/enrichment/lib/enrichment-types.ts +39 -0
  198. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  199. package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
  200. package/blocks/forms/README.md +8 -0
  201. package/blocks/forms/block.json +139 -18
  202. package/blocks/forms/components/form-activity.tsx +71 -0
  203. package/blocks/forms/components/form-backfill.tsx +108 -0
  204. package/blocks/forms/components/form-builder.tsx +4 -2
  205. package/blocks/forms/components/form-embed.tsx +4 -2
  206. package/blocks/forms/components/form-groups.tsx +151 -0
  207. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  208. package/blocks/forms/components/form-route-history.tsx +72 -0
  209. package/blocks/forms/components/form-saved-views.tsx +81 -0
  210. package/blocks/forms/components/forms-screen.tsx +56 -19
  211. package/blocks/forms/components/more-rows.tsx +30 -0
  212. package/blocks/forms/components/submissions-export.tsx +67 -0
  213. package/blocks/forms/components/submissions-table.tsx +17 -6
  214. package/blocks/forms/lib/forms-extra.ts +67 -0
  215. package/blocks/inbox/README.md +32 -3
  216. package/blocks/inbox/block.json +29 -2
  217. package/blocks/inbox/components/inbox-list.tsx +7 -3
  218. package/blocks/inbox/components/inbox-screen.tsx +294 -27
  219. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  220. package/blocks/inbox/components/reply-composer.tsx +65 -9
  221. package/blocks/inbox/components/tag-and-assign.tsx +4 -2
  222. package/blocks/inbox/components/thread-badge.tsx +2 -2
  223. package/blocks/inbox/components/thread-view.tsx +1 -1
  224. package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
  225. package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
  226. package/blocks/inbox/lib/inbox-types.ts +47 -1
  227. package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
  228. package/blocks/integrations/README.md +63 -1
  229. package/blocks/integrations/block.json +234 -15
  230. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  231. package/blocks/integrations/components/connection-health.tsx +59 -0
  232. package/blocks/integrations/components/crm-detail.tsx +119 -0
  233. package/blocks/integrations/components/crm-records.tsx +103 -0
  234. package/blocks/integrations/components/field-mappings.tsx +61 -0
  235. package/blocks/integrations/components/integration-list.tsx +13 -8
  236. package/blocks/integrations/components/integrations-screen.tsx +152 -0
  237. package/blocks/integrations/components/migration-panel.tsx +133 -0
  238. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  239. package/blocks/integrations/components/sync-status.tsx +11 -9
  240. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  241. package/blocks/integrations/components/webhook-detail.tsx +179 -0
  242. package/blocks/integrations/components/webhook-manager.tsx +43 -25
  243. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  244. package/blocks/integrations/lib/crm-utils.ts +39 -0
  245. package/blocks/integrations/lib/facts.tsx +13 -0
  246. package/blocks/integrations/lib/integrations-utils.ts +11 -1
  247. package/blocks/integrations/lib/load-more.tsx +141 -0
  248. package/blocks/intent/README.md +14 -0
  249. package/blocks/intent/block.json +27 -1
  250. package/blocks/intent/components/account-intent-score.tsx +5 -5
  251. package/blocks/intent/components/intent-feed.tsx +13 -10
  252. package/blocks/intent/components/intent-keyword-manager.tsx +16 -12
  253. package/blocks/intent/components/intent-screen.tsx +265 -0
  254. package/blocks/intent/components/keyword-detail.tsx +124 -0
  255. package/blocks/intent/lib/intent-utils.ts +4 -4
  256. package/blocks/lifecycle/block.json +13 -8
  257. package/blocks/lifecycle/components/account-related.tsx +8 -1
  258. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  259. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  260. package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
  261. package/blocks/lifecycle/components/lifecycle-screen.tsx +3 -1
  262. package/blocks/lifecycle/lib/lifecycle.ts +1 -1
  263. package/blocks/lists/README.md +20 -1
  264. package/blocks/lists/block.json +69 -3
  265. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  266. package/blocks/lists/components/add-to-list-button.tsx +20 -9
  267. package/blocks/lists/components/list-picker.tsx +17 -3
  268. package/blocks/lists/components/list-table.tsx +12 -6
  269. package/blocks/lists/components/lists-screen.tsx +346 -0
  270. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  271. package/blocks/lists/components/suppression-manager.tsx +33 -8
  272. package/blocks/lists/components/suppressions-screen.tsx +264 -0
  273. package/blocks/mailboxes/README.md +17 -0
  274. package/blocks/mailboxes/block.json +69 -17
  275. package/blocks/mailboxes/components/connect-mailbox.tsx +32 -21
  276. package/blocks/mailboxes/components/domain-detail.tsx +148 -0
  277. package/blocks/mailboxes/components/domain-purchase.tsx +20 -14
  278. package/blocks/mailboxes/components/mailbox-detail.tsx +157 -0
  279. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  280. package/blocks/mailboxes/components/mailboxes-screen.tsx +228 -0
  281. package/blocks/mailboxes/components/warmup-health.tsx +24 -22
  282. package/blocks/mailboxes/lib/mailboxes-utils.ts +26 -5
  283. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  284. package/blocks/newsletter/README.md +12 -0
  285. package/blocks/newsletter/block.json +157 -24
  286. package/blocks/newsletter/components/issue-scheduler.tsx +4 -3
  287. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  288. package/blocks/newsletter/components/more-rows.tsx +30 -0
  289. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  290. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  291. package/blocks/newsletter/components/newsletter-screen.tsx +39 -14
  292. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  293. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  294. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  295. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  296. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  297. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  298. package/blocks/nurture/block.json +28 -14
  299. package/blocks/nurture/components/nurture-builder.tsx +13 -3
  300. package/blocks/nurture/components/nurture-enroll.tsx +12 -10
  301. package/blocks/nurture/components/nurture-form.tsx +27 -10
  302. package/blocks/nurture/components/nurture-screen.tsx +26 -11
  303. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  304. package/blocks/nurture/lib/nurture-types.ts +1 -1
  305. package/blocks/objects/README.md +18 -1
  306. package/blocks/objects/block.json +42 -21
  307. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  308. package/blocks/objects/components/object-not-found.tsx +3 -1
  309. package/blocks/objects/components/object-record-detail.tsx +10 -2
  310. package/blocks/objects/components/object-schema-editor.tsx +4 -2
  311. package/blocks/objects/components/object-table.tsx +13 -3
  312. package/blocks/objects/components/objects-screen.tsx +147 -0
  313. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  314. package/blocks/objects/lib/object-types.ts +1 -1
  315. package/blocks/pages/README.md +14 -0
  316. package/blocks/pages/block.json +140 -23
  317. package/blocks/pages/components/brand-kit-editor.tsx +5 -2
  318. package/blocks/pages/components/content-library.tsx +9 -4
  319. package/blocks/pages/components/more-rows.tsx +30 -0
  320. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  321. package/blocks/pages/components/page-analytics.tsx +176 -0
  322. package/blocks/pages/components/page-clone.tsx +99 -0
  323. package/blocks/pages/components/page-domains.tsx +167 -0
  324. package/blocks/pages/components/page-editor.tsx +1 -1
  325. package/blocks/pages/components/page-generation-status.tsx +48 -0
  326. package/blocks/pages/components/page-list.tsx +2 -1
  327. package/blocks/pages/components/page-tracking.tsx +137 -0
  328. package/blocks/pages/components/pages-screen.tsx +57 -16
  329. package/blocks/pages/components/seo-report.tsx +6 -1
  330. package/blocks/pages/lib/studio.ts +7 -0
  331. package/blocks/phone-numbers/README.md +74 -0
  332. package/blocks/phone-numbers/block.json +262 -0
  333. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  334. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  335. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  336. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  337. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  338. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  339. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  340. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  341. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  342. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  343. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  344. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  345. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  346. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  347. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  348. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  349. package/blocks/pickers/README.md +23 -0
  350. package/blocks/pickers/block.json +103 -0
  351. package/blocks/pickers/components/company-picker.tsx +90 -0
  352. package/blocks/pickers/components/contact-picker.tsx +131 -0
  353. package/blocks/pickers/components/member-picker.tsx +99 -0
  354. package/blocks/pickers/components/search-picker.tsx +98 -0
  355. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  356. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  357. package/blocks/pickers/lib/use-debounced.ts +13 -0
  358. package/blocks/quotes/README.md +13 -0
  359. package/blocks/quotes/block.json +65 -13
  360. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  361. package/blocks/quotes/components/product-picker.tsx +3 -1
  362. package/blocks/quotes/components/quote-builder.tsx +1 -1
  363. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  364. package/blocks/quotes/components/quote-list.tsx +5 -3
  365. package/blocks/quotes/components/quote-related.tsx +8 -4
  366. package/blocks/quotes/components/quote-viewer.tsx +7 -2
  367. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  368. package/blocks/radar/README.md +10 -0
  369. package/blocks/radar/block.json +41 -1
  370. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  371. package/blocks/radar/components/competitor-detail.tsx +117 -0
  372. package/blocks/radar/components/competitor-tracker.tsx +3 -1
  373. package/blocks/radar/components/radar-overview.tsx +4 -4
  374. package/blocks/radar/components/radar-report.tsx +4 -2
  375. package/blocks/radar/components/radar-screen.tsx +214 -0
  376. package/blocks/radar/components/report-detail.tsx +104 -0
  377. package/blocks/search/README.md +10 -0
  378. package/blocks/search/block.json +16 -1
  379. package/blocks/search/components/filter-builder.tsx +3 -3
  380. package/blocks/search/components/prospect-results.tsx +5 -3
  381. package/blocks/search/components/prospect-search-screen.tsx +235 -0
  382. package/blocks/search/components/saved-searches.tsx +4 -1
  383. package/blocks/search/hooks/use-filter-options.ts +20 -6
  384. package/blocks/search/lib/search-types.ts +2 -0
  385. package/blocks/sequencer/README.md +55 -9
  386. package/blocks/sequencer/block.json +126 -19
  387. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  388. package/blocks/sequencer/components/enroll-dialog.tsx +11 -5
  389. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  390. package/blocks/sequencer/components/sequence-builder.tsx +224 -60
  391. package/blocks/sequencer/components/sequence-detail.tsx +261 -0
  392. package/blocks/sequencer/components/sequence-enrollments.tsx +116 -0
  393. package/blocks/sequencer/components/sequence-list.tsx +34 -9
  394. package/blocks/sequencer/components/sequence-stats.tsx +11 -10
  395. package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
  396. package/blocks/sequencer/components/sequences-screen.tsx +162 -0
  397. package/blocks/sequencer/components/step-editor.tsx +14 -12
  398. package/blocks/sequencer/lib/sequencer-utils.ts +133 -10
  399. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
  400. package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
  401. package/blocks/social-ads/README.md +19 -0
  402. package/blocks/social-ads/block.json +233 -1
  403. package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
  404. package/blocks/social-ads/components/ads-screen.tsx +152 -0
  405. package/blocks/social-ads/components/audience-sync-detail.tsx +194 -0
  406. package/blocks/social-ads/components/audience-sync.tsx +17 -9
  407. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  408. package/blocks/social-ads/components/campaign-detail.tsx +138 -0
  409. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  410. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  411. package/blocks/social-ads/components/creative-detail.tsx +155 -0
  412. package/blocks/social-ads/components/creative-gallery.tsx +35 -7
  413. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  414. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  415. package/blocks/social-ads/components/post-composer.tsx +7 -3
  416. package/blocks/social-ads/components/post-detail.tsx +163 -0
  417. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  418. package/blocks/social-ads/components/social-performance.tsx +80 -0
  419. package/blocks/social-ads/components/social-post-list.tsx +11 -3
  420. package/blocks/social-ads/components/social-screen.tsx +187 -0
  421. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  422. package/blocks/surveys/README.md +91 -0
  423. package/blocks/surveys/block.json +299 -0
  424. package/blocks/surveys/components/account-nps.tsx +69 -0
  425. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  426. package/blocks/surveys/components/org-summary.tsx +82 -0
  427. package/blocks/surveys/components/sending-domains.tsx +113 -0
  428. package/blocks/surveys/components/survey-form.tsx +126 -0
  429. package/blocks/surveys/components/survey-responses.tsx +145 -0
  430. package/blocks/surveys/components/survey-results.tsx +72 -0
  431. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  432. package/blocks/surveys/components/survey-send.tsx +162 -0
  433. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  434. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  435. package/blocks/surveys/lib/surveys-types.ts +228 -0
  436. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  437. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  438. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  439. package/blocks/visitors/README.md +10 -0
  440. package/blocks/visitors/block.json +25 -1
  441. package/blocks/visitors/components/snippet-install.tsx +21 -10
  442. package/blocks/visitors/components/traffic-chart.tsx +4 -4
  443. package/blocks/visitors/components/visitor-company-card.tsx +26 -16
  444. package/blocks/visitors/components/visitor-detail.tsx +105 -0
  445. package/blocks/visitors/components/visitor-feed.tsx +16 -7
  446. package/blocks/visitors/components/visitors-screen.tsx +166 -0
  447. package/blocks/webchat/README.md +10 -0
  448. package/blocks/webchat/block.json +21 -7
  449. package/blocks/webchat/components/chat-widget-config.tsx +8 -5
  450. package/blocks/webchat/components/conversation-list.tsx +8 -4
  451. package/blocks/webchat/components/webchat-screen.tsx +245 -0
  452. package/blocks/workbench/README.md +11 -0
  453. package/blocks/workbench/block.json +48 -1
  454. package/blocks/workbench/components/column-recipe-editor.tsx +5 -3
  455. package/blocks/workbench/components/run-progress.tsx +1 -1
  456. package/blocks/workbench/components/workbench-grid.tsx +10 -4
  457. package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
  458. package/blocks/workbench/components/workbench-screen.tsx +202 -0
  459. package/blocks/workbench/components/workbench-sources.tsx +194 -0
  460. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  461. package/blocks/workflows/README.md +13 -0
  462. package/blocks/workflows/block.json +28 -9
  463. package/blocks/workflows/components/run-history.tsx +31 -9
  464. package/blocks/workflows/components/trigger-config.tsx +3 -1
  465. package/blocks/workflows/components/workflow-editor.tsx +4 -2
  466. package/blocks/workflows/components/workflow-list.tsx +13 -6
  467. package/blocks/workflows/components/workflows-screen.tsx +193 -0
  468. package/dist/cli.js +3109 -1977
  469. package/dist/errors.d.mts +34 -0
  470. package/dist/errors.d.ts +34 -0
  471. package/dist/errors.js +46 -0
  472. package/dist/errors.js.map +1 -0
  473. package/dist/errors.mjs +21 -0
  474. package/dist/errors.mjs.map +1 -0
  475. package/dist/index.browser.js +11 -5
  476. package/dist/index.browser.js.map +1 -1
  477. package/dist/index.browser.mjs +11 -5
  478. package/dist/index.browser.mjs.map +1 -1
  479. package/dist/index.d.mts +164 -25
  480. package/dist/index.d.ts +164 -25
  481. package/dist/index.js +11 -5
  482. package/dist/index.js.map +1 -1
  483. package/dist/index.mjs +11 -5
  484. package/dist/index.mjs.map +1 -1
  485. package/dist/react.browser.js +10 -4
  486. package/dist/react.browser.js.map +1 -1
  487. package/dist/react.browser.mjs +10 -4
  488. package/dist/react.browser.mjs.map +1 -1
  489. package/dist/react.d.mts +142 -5
  490. package/dist/react.d.ts +142 -5
  491. package/dist/react.js +10 -4
  492. package/dist/react.js.map +1 -1
  493. package/dist/react.mjs +10 -4
  494. package/dist/react.mjs.map +1 -1
  495. package/dist/types/blocks/add.d.ts +11 -4
  496. package/dist/types/blocks/brand.d.ts +122 -0
  497. package/dist/types/blocks/compose.d.ts +53 -0
  498. package/dist/types/blocks/create.d.ts +27 -20
  499. package/dist/types/blocks/doctor.d.ts +6 -1
  500. package/dist/types/blocks/flows.d.ts +7 -1
  501. package/dist/types/blocks/navfile.d.ts +35 -0
  502. package/dist/types/blocks/orgBrand.d.ts +34 -0
  503. package/dist/types/blocks/paths.d.ts +4 -0
  504. package/dist/types/blocks/prefixes.d.ts +37 -0
  505. package/dist/types/blocks/registry.d.ts +17 -0
  506. package/dist/types/blocks/screens.d.ts +53 -0
  507. package/dist/types/cli.d.ts +5 -0
  508. package/dist/types/errors.d.ts +33 -0
  509. package/dist/types/http.d.ts +2 -18
  510. package/dist/types/version.d.ts +2 -2
  511. package/docs/agents.md +15 -4
  512. package/docs/api/README.md +2 -2
  513. package/docs/api/by-path.md +1 -0
  514. package/docs/api/desk.d.ts +98 -2
  515. package/docs/api/desk.md +123 -1
  516. package/docs/api/inbox.d.ts +10 -2
  517. package/docs/api/inbox.md +10 -2
  518. package/docs/api/seo.d.ts +19 -1
  519. package/docs/api/seo.md +24 -1
  520. package/package.json +16 -2
  521. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  522. package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
  523. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  524. package/stacks/6sense.json +55 -17
  525. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  526. package/stacks/apollo/pages/companies.tsx.tmpl +33 -0
  527. package/stacks/apollo/pages/contacts.tsx.tmpl +51 -0
  528. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  529. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  530. package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
  531. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  532. package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
  533. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  534. package/stacks/apollo.json +147 -37
  535. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  536. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  537. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  538. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  539. package/stacks/calendly.json +64 -0
  540. package/stacks/clay/pages/companies.tsx.tmpl +31 -0
  541. package/stacks/clay/pages/contacts.tsx.tmpl +31 -0
  542. package/stacks/clay/pages/lists.tsx.tmpl +12 -0
  543. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  544. package/stacks/clay.json +73 -27
  545. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  546. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  547. package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
  548. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  549. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  550. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  551. package/stacks/gong.json +98 -35
  552. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  553. package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
  554. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  555. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  556. package/stacks/hootsuite.json +104 -0
  557. package/stacks/hubspot/pages/companies.tsx.tmpl +26 -0
  558. package/stacks/hubspot/pages/contacts.tsx.tmpl +45 -0
  559. package/stacks/hubspot/pages/deals.tsx.tmpl +20 -0
  560. package/stacks/hubspot/pages/inbox.tsx.tmpl +103 -0
  561. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  562. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  563. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  564. package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
  565. package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
  566. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  567. package/stacks/hubspot.json +181 -64
  568. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  569. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  570. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  571. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  572. package/stacks/intercom.json +52 -20
  573. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  574. package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
  575. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  576. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  577. package/stacks/mailchimp.json +68 -34
  578. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  579. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  580. package/stacks/outreach/pages/contacts.tsx.tmpl +46 -0
  581. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  582. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  583. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  584. package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
  585. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  586. package/stacks/outreach.json +106 -25
  587. package/stacks/pipedrive/pages/companies.tsx.tmpl +23 -0
  588. package/stacks/pipedrive/pages/contacts.tsx.tmpl +27 -0
  589. package/stacks/pipedrive/pages/deals.tsx.tmpl +20 -0
  590. package/stacks/pipedrive/pages/inbox.tsx.tmpl +103 -0
  591. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  592. package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
  593. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  594. package/stacks/pipedrive.json +117 -0
  595. package/stacks/salesforce/pages/companies.tsx.tmpl +26 -0
  596. package/stacks/salesforce/pages/contacts.tsx.tmpl +32 -0
  597. package/stacks/salesforce/pages/deals.tsx.tmpl +20 -0
  598. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  599. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  600. package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
  601. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  602. package/stacks/salesforce.json +85 -31
  603. package/stacks/zoominfo/pages/companies.tsx.tmpl +31 -0
  604. package/stacks/zoominfo/pages/contacts.tsx.tmpl +31 -0
  605. package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
  606. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  607. package/stacks/zoominfo.json +115 -0
  608. package/styles/styles.css +1 -1
  609. package/templates/next-app/README.md.tmpl +40 -9
  610. package/templates/next-app/app/globals.css.tmpl +349 -64
  611. package/templates/next-app/app/layout.tsx.tmpl +7 -2
  612. package/templates/next-app/components/app-shell.tsx.tmpl +104 -88
  613. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
  614. package/templates/next-app/dot-gitignore.tmpl +2 -0
  615. package/templates/next-app/package.json.tmpl +1 -1
@@ -45,21 +45,21 @@ export function StageEditor({ pipelineId, onChanged, className }: StageEditorPro
45
45
  const saved = dataOf<Stage>(await g8Put(`${base}/stages/${encodeURIComponent(s.id)}`, { name: s.name, probability: s.probability ?? 0, stage_type: s.stage_type ?? "open", color: s.color ?? undefined }));
46
46
  done(stages.map((x) => (x.id === saved.id ? { ...x, ...saved } : x)));
47
47
  } finally { setSavingId(null); }
48
- });
48
+ }, { invalidates: [["deals", "pipelines"]] });
49
49
  const add = useG8Mutation(async (name: string) => {
50
50
  const created = dataOf<Stage>(await g8Post(`${base}/stages`, { name, probability: 0, stage_type: "open" }));
51
51
  done([...stages, created]);
52
52
  setNewName("");
53
- });
53
+ }, { invalidates: [["deals", "pipelines"]] });
54
54
  const reorder = useG8Mutation(async (next: Stage[]) => {
55
55
  await g8Put(`${base}/stages/reorder`, { stage_ids: next.map((s) => s.id) });
56
56
  done(next);
57
- });
57
+ }, { invalidates: [["deals", "pipelines"]] });
58
58
  const remove = useG8Mutation(async (s: Stage) => {
59
59
  await g8Delete(`${base}/stages/${encodeURIComponent(s.id)}`);
60
60
  done(stages.filter((x) => x.id !== s.id));
61
61
  setRemoving(null);
62
- }, { onError: () => setRemoving(null) });
62
+ }, { invalidates: [["deals", "pipelines"]], onError: () => setRemoving(null) });
63
63
 
64
64
  const patch = (id: string, change: Partial<Stage>) => setStages((all) => all.map((s) => (s.id === id ? { ...s, ...change } : s)));
65
65
  const swap = (i: number, j: number) => { const next = [...stages]; [next[i], next[j]] = [next[j], next[i]]; void reorder.mutate(next); };
@@ -69,7 +69,7 @@ export function StageEditor({ pipelineId, onChanged, className }: StageEditorPro
69
69
  if (pipeline.loading && !pipeline.data) return <Skeleton className="h-48 w-full" aria-label="Loading stages" />;
70
70
  return (
71
71
  <div className={`space-y-3 ${className ?? ""}`}>
72
- <h2 className="text-base font-semibold">{pipeline.data?.name} stages</h2>
72
+ <h2 className="text-base font-medium">{pipeline.data?.name} stages</h2>
73
73
  {failure ? <ApiError error={failure} title="That stage change failed" /> : null}
74
74
  <ol className="space-y-2">
75
75
  {stages.map((s, i) => (
@@ -4,7 +4,7 @@
4
4
  * usePipelineDeals: the deals in one pipeline, all pages up to `maxDeals`, with an optimistic stage move.
5
5
  *
6
6
  * SDK operations:
7
- * GET /deals?pipeline_id=&search=&owner_id=&page=&limit= (deals.listDeals) read
7
+ * GET /deals?pipeline_id=&search=&owner_id=&stage_id=&outcome=&stale_before=&has_activity=&close_after=&close_before=&has_contact=&page=&limit= (deals.listDeals) read
8
8
  * PATCH /deals/{deal_id} (deals.updateDeal) write - the stage move
9
9
  *
10
10
  * `moveDeal` updates the board immediately and, if the API refuses, refetches the list (the server is the truth)
@@ -15,12 +15,26 @@
15
15
  import { useCallback, useEffect, useState } from "react";
16
16
  import { g8Get, g8Patch } from "@/g8/lib/g8-client";
17
17
  import { useG8Query } from "@/g8/hooks/use-g8-query";
18
+ import { invalidate } from "@/g8/lib/g8-cache";
18
19
  import type { Deal } from "../lib/deals-types";
19
20
  import { dataOf } from "../lib/deals-types";
20
21
 
21
22
  export interface PipelineDealFilters {
22
23
  search?: string;
23
24
  ownerId?: string;
25
+ /** Only this stage (GET /deals stage_id). */
26
+ stageId?: string;
27
+ /** Exactly one of open, won or lost. */
28
+ outcome?: string;
29
+ /** Deals last touched before this timestamp OR never touched (GET /deals stale_before). */
30
+ staleBefore?: string;
31
+ hasActivity?: boolean;
32
+ /** Close date on or after this timestamp or YYYY-MM-DD (inclusive). */
33
+ closeAfter?: string;
34
+ /** Close date strictly before this timestamp or YYYY-MM-DD (exclusive). */
35
+ closeBefore?: string;
36
+ /** false returns the deals with no contact. */
37
+ hasContact?: boolean;
24
38
  }
25
39
 
26
40
  export interface MoveExtra {
@@ -35,14 +49,14 @@ export function usePipelineDeals(pipelineId: string | undefined, filters: Pipeli
35
49
  async (signal) => {
36
50
  const all: Deal[] = [];
37
51
  for (let page = 1; all.length < maxDeals; page++) {
38
- const env = await g8Get<Deal[]>("/deals", { pipeline_id: pipelineId, search: filters.search || undefined, owner_id: filters.ownerId || undefined, page, limit: 100 }, { signal });
52
+ const env = await g8Get<Deal[]>("/deals", { pipeline_id: pipelineId, search: filters.search || undefined, owner_id: filters.ownerId || undefined, stage_id: filters.stageId, outcome: filters.outcome, stale_before: filters.staleBefore, has_activity: filters.hasActivity, close_after: filters.closeAfter, close_before: filters.closeBefore, has_contact: filters.hasContact, page, limit: 100 }, { signal });
39
53
  all.push(...(env.data ?? []));
40
54
  if (!env.pagination?.has_next) break;
41
55
  }
42
56
  return all.slice(0, maxDeals);
43
57
  },
44
- [pipelineId, filters.search, filters.ownerId, maxDeals],
45
- { enabled: Boolean(pipelineId) },
58
+ [pipelineId, filters.search, filters.ownerId, filters.stageId, filters.outcome, filters.staleBefore, filters.hasActivity, filters.closeAfter, filters.closeBefore, filters.hasContact, maxDeals],
59
+ { enabled: Boolean(pipelineId), key: ["deals", "list", "board", { pipelineId, search: filters.search || undefined, ownerId: filters.ownerId || undefined, stageId: filters.stageId, outcome: filters.outcome, staleBefore: filters.staleBefore, hasActivity: filters.hasActivity, closeAfter: filters.closeAfter, closeBefore: filters.closeBefore, hasContact: filters.hasContact, maxDeals }] },
46
60
  );
47
61
  useEffect(() => { if (query.data) setDeals(query.data); }, [query.data]);
48
62
 
@@ -53,6 +67,9 @@ export function usePipelineDeals(pipelineId: string | undefined, filters: Pipeli
53
67
  try {
54
68
  const saved = dataOf<Deal>(await g8Patch(`/deals/${encodeURIComponent(deal.id)}`, { stage_id: stageId, expected_revision: deal.revision ?? undefined, ...extra }));
55
69
  setDeals((all) => all.map((d) => (d.id === deal.id ? { ...d, ...saved } : d)));
70
+ // The deal's revision changed: a cached detail or history of it is out of date.
71
+ void invalidate(["deals", "detail"]);
72
+ void invalidate(["deals", "history"]);
56
73
  return saved;
57
74
  } catch (err) {
58
75
  // Roll back by re-reading the server's truth: a snapshot taken before the move could itself be stale.
@@ -13,7 +13,7 @@ export function usePipelines() {
13
13
  const q = useG8Query(async (signal) => {
14
14
  const env = await g8Get<Pipeline[]>("/deals/pipelines", undefined, { signal });
15
15
  return (env.data ?? []).map((p) => ({ ...p, stages: sortedStages(p) }));
16
- }, []);
16
+ }, [], { key: ["deals", "pipelines"] });
17
17
  const pipelines = q.data ?? [];
18
18
  return { ...q, pipelines, defaultPipeline: pipelines.find((p) => p.is_default) ?? pipelines[0] };
19
19
  }
@@ -0,0 +1,156 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Shapes and pure helpers for the deal depth components: notes, tasks, contact roles, line items, close reasons
5
+ * and the list filters. Types only describe the fields the components read; the API may return more.
6
+ *
7
+ * Money: a deal's `amount` is in DOLLARS (major units); a LINE ITEM's `unit_amount` and `line_total` are in CENTS.
8
+ * Dates: date-only values are `YYYY-MM-DD` and are shifted with UTC arithmetic, never through a local Date.
9
+ */
10
+ import { dataOf } from "./deals-types";
11
+
12
+ /** Buying-committee roles the API accepts on a deal contact (any other value is a 422). */
13
+ export const CONTACT_ROLES = ["champion", "decision_maker", "influencer", "blocker", "coach", "end_user"] as const;
14
+
15
+ export function roleLabel(role: string | null | undefined): string {
16
+ if (!role) return "No role";
17
+ const text = role.replace(/_/g, " ");
18
+ return text.charAt(0).toUpperCase() + text.slice(1);
19
+ }
20
+
21
+ export interface DealNote {
22
+ id: string;
23
+ content: string;
24
+ created_by_name?: string | null;
25
+ created_at?: string | null;
26
+ updated_at?: string | null;
27
+ read_only?: boolean;
28
+ }
29
+
30
+ export interface DealTask {
31
+ id: string;
32
+ title: string;
33
+ description?: string | null;
34
+ due_date?: string | null;
35
+ status?: string | null;
36
+ priority?: number | null;
37
+ assignee_name?: string | null;
38
+ }
39
+
40
+ export interface DealLineItem {
41
+ id?: string;
42
+ product_name: string;
43
+ description?: string | null;
44
+ quantity: number;
45
+ /** Cents. */
46
+ unit_amount: number;
47
+ discount_pct: number;
48
+ billing_frequency?: string | null;
49
+ sort_order?: number;
50
+ stripe_product_id?: string | null;
51
+ stripe_price_id?: string | null;
52
+ manual_product_id?: string | null;
53
+ }
54
+
55
+ export interface LineItemsPayload {
56
+ deal_id: string;
57
+ /** Dollars. */
58
+ amount?: number | null;
59
+ currency?: string | null;
60
+ line_items?: DealLineItem[];
61
+ amount_mismatch?: boolean;
62
+ /** Dollars. */
63
+ linked_quote_total?: number | null;
64
+ }
65
+
66
+ export interface CloseReason {
67
+ id: string;
68
+ label: string;
69
+ kind: string;
70
+ /** Whether this person may archive it (created it themselves, or may manage all). */
71
+ canArchive: boolean;
72
+ }
73
+
74
+ /** Rows of a list response that is either a bare array or `{ items | tasks | notes | reasons | contacts: [] }`. */
75
+ export function rowsOf<T>(env: unknown): T[] {
76
+ const inner = dataOf<unknown>(env);
77
+ if (Array.isArray(inner)) return inner as T[];
78
+ const o = inner as Record<string, unknown> | null;
79
+ for (const key of ["items", "tasks", "notes", "reasons", "contacts"]) if (o && Array.isArray(o[key])) return o[key] as T[];
80
+ return [];
81
+ }
82
+
83
+ /** The close reasons of `GET /deals/close-reasons`: `{ can_manage, reasons: [{ id, label, kind, archived? }] }`. */
84
+ export function reasonsOf(env: unknown): CloseReason[] {
85
+ const inner = dataOf<{ can_manage?: boolean | null; reasons?: Array<Record<string, unknown>> } | null>(env);
86
+ const manage = Boolean(inner?.can_manage);
87
+ return (inner?.reasons ?? [])
88
+ .filter((r) => !r.archived && !r.archived_at)
89
+ .map((r) => ({ id: String(r.id ?? ""), label: String(r.label ?? r.name ?? ""), kind: String(r.kind ?? ""), canArchive: manage || r.can_archive === true || r.can_delete === true }))
90
+ .filter((r) => r.id && r.label);
91
+ }
92
+
93
+ /** Whole cents from a dollars text input; null when it is not a non-negative number. */
94
+ export function dollarsToCents(text: string): number | null {
95
+ const n = Number(text.trim());
96
+ return text.trim() !== "" && Number.isFinite(n) && n >= 0 ? Math.round(n * 100) : null;
97
+ }
98
+
99
+ export const centsToDollarsText = (cents: number): string => (cents / 100).toFixed(2);
100
+
101
+ export function lineTotalCents(l: Pick<DealLineItem, "unit_amount" | "quantity" | "discount_pct">): number {
102
+ return Math.round(l.unit_amount * l.quantity * (1 - Math.min(100, Math.max(0, l.discount_pct)) / 100));
103
+ }
104
+
105
+ export function formatCents(cents: number | null | undefined, currency?: string | null): string {
106
+ if (cents == null) return "";
107
+ try {
108
+ return new Intl.NumberFormat(undefined, { style: "currency", currency: currency || "USD" }).format(cents / 100);
109
+ } catch {
110
+ return String(cents / 100);
111
+ }
112
+ }
113
+
114
+ /** `date` (YYYY-MM-DD) shifted by whole days, in UTC so the result is the same in every time zone. */
115
+ export function addDaysUtc(date: string, days: number): string {
116
+ const d = new Date(`${date}T00:00:00Z`);
117
+ d.setUTCDate(d.getUTCDate() + days);
118
+ return d.toISOString().slice(0, 10);
119
+ }
120
+
121
+ /** Midnight UTC `days` before today, as an ISO timestamp. Stable all day, so it can sit in a query key. */
122
+ export function staleCutoff(days: number, now = new Date()): string {
123
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - days)).toISOString();
124
+ }
125
+
126
+ export interface DealFilterState {
127
+ ownerId: string;
128
+ stageId: string;
129
+ outcome: "" | "open" | "won" | "lost";
130
+ /** Only deals untouched for this many days (or never touched). "" = off. */
131
+ staleDays: "" | "7" | "14" | "30" | "60" | "90";
132
+ activity: "" | "yes" | "no";
133
+ /** Inclusive date range of the close date. */
134
+ closeFrom: string;
135
+ closeTo: string;
136
+ /** Only deals with no contact at all. */
137
+ orphaned: boolean;
138
+ }
139
+
140
+ export const NO_FILTERS: DealFilterState = { ownerId: "", stageId: "", outcome: "", staleDays: "", activity: "", closeFrom: "", closeTo: "", orphaned: false };
141
+
142
+ export const filtersActive = (f: DealFilterState): boolean => (Object.keys(NO_FILTERS) as Array<keyof DealFilterState>).some((k) => f[k] !== NO_FILTERS[k]);
143
+
144
+ /** The `GET /deals` query for a filter state. `close_before` is exclusive, so the inclusive "to" date becomes the next day. */
145
+ export function filterQuery(f: DealFilterState, now = new Date()) {
146
+ return {
147
+ ownerId: f.ownerId || undefined,
148
+ stageId: f.stageId || undefined,
149
+ outcome: f.outcome || undefined,
150
+ staleBefore: f.staleDays ? staleCutoff(Number(f.staleDays), now) : undefined,
151
+ hasActivity: f.activity === "" ? undefined : f.activity === "yes",
152
+ closeAfter: f.closeFrom || undefined,
153
+ closeBefore: f.closeTo ? addDaysUtc(f.closeTo, 1) : undefined,
154
+ hasContact: f.orphaned ? false : undefined,
155
+ };
156
+ }
@@ -9,6 +9,28 @@ browser, record outcomes, and review transcripts. Copy `DialerWorkspace` for the
9
9
  > voicemail, or mints a softphone credential without a confirm dialog first, and the block never auto-advances to
10
10
  > the next batch.
11
11
 
12
+ ## Screens
13
+
14
+ - `DialerScreen` (`components/dialer-screen.tsx`): **Session** tab picks a list (ListPicker), creates a session (dials
15
+ nobody), then runs the queue: every batch, voicemail drop, softphone credential and stop is confirmed; finished calls
16
+ open the outcome panel and transcript. **Sessions** tab lists every session; a row opens `DialerSessionDetail` (stats, details, calls, queue; pause, confirmed resume, confirmed stop; `?tab=sessions&session=<id>`). **History** tab lists the rep's calls. Props are optional: `userId` (overrides the rep; without it the
17
+ rep is the signed-in team member from `GET /team-members/me`, so the softphone works out of the box with a personal key,
18
+ and a `MemberPicker` switches rep where the API allows it; a personal key naming another rep gets a 403, which the form
19
+ explains; an organisation key is not a person, so the rep is chosen with the picker), `userEmail`, `listId`, `sessionId`, `transport` (default: the Twilio adapter, loaded
20
+ on demand, needs `@twilio/voice-sdk`). Resume a session with `?session=<id>`. Add `&view=full` to any detail (session, call, and the same in the other screens) to open it as a full page.
21
+ - `CallLogScreen` (`components/call-log-screen.tsx`): history of one rep, contact or session with outcome filter and search;
22
+ open a call for recording, transcript and AI grade (`?id=<room_name>`), and **Call back** the number (`CallContactButton`). Needs a rep email (it asks) or a prop.
23
+ - Slots on both: `toolbarActions`, `renderRowActions`, `renderDetailActions`, `extraDetailTabs`.
24
+ - `CallContactButton` (`components/call-contact-button.tsx`): **call one contact from any screen**. Drop it into a
25
+ contact page's `renderDetailActions` (the apollo and outreach stacks do) or a row slot: `<CallContactButton contact={c} />`.
26
+ Closed, it loads nothing. Open: pick the number (masked `***` numbers are never offered), a PAUSED or COMPLETED
27
+ session to log the call under, and the caller ID; turn on the softphone; confirm (external, 1 recipient). It calls
28
+ `POST /dialer/parallel-calls/manual-dial` and the softphone joins the returned `conference_name`. The rep is
29
+ `userId` if passed, else the personal key's user (`GET /team-members/me`); an organisation key is not a rep, so it
30
+ then asks for `userId`. Why not `next-batch` with `force_next_contact`: that only picks a contact already in the
31
+ session's list and dials a whole batch.
32
+ Scopes: `voice:read`, `voice:write`, `voice:run` (and `team:read` for `CallContactButton` without `userId`). A phone number must exist (buy or verify it in the graph8 app).
33
+
12
34
  ## How it works (read this before building)
13
35
 
14
36
  - graph8 places the calls **from its servers** into a conference. The rep's browser only **joins that
@@ -70,8 +92,10 @@ Softphone), `call_id` = `room_name` (live call -> disposition -> transcript -> r
70
92
 
71
93
  ## Stack nav pages
72
94
 
73
- `CallQueue` and `CallLog` render with no props (a stack uses them as pages): the start form asks for the rep's user id
74
- and the call log for a rep email. Pass `userId` / `userEmail` from your session to skip those fields.
95
+ `CallQueue` and `CallLog` render with no props (a stack uses them as pages): the start form defaults the rep to the
96
+ signed-in team member (a `MemberPicker` switches it; the raw user id is only a fallback under "Use a user id instead"),
97
+ the list is a `ListPicker`, and the call log shows the signed-in rep's calls (a rep email can still be typed). Pass
98
+ `userId` / `userEmail` from your session to override.
75
99
 
76
100
  ## Gotchas
77
101
 
@@ -81,13 +105,22 @@ and the call log for a rep email. Pass `userId` / `userEmail` from your session
81
105
  - Voicemail drop plays the rep's cloned voice to a stranger's phone. The confirm dialog names the contact.
82
106
  - Another tenant's session id is a 404, the same as a missing one.
83
107
  - Manual dial (`POST /dialer/parallel-calls/manual-dial`, external) needs a PAUSED or COMPLETED session and can
84
- create a contact; it is a server recipe here, not a component.
108
+ create a contact (an unknown number is matched to a contact or one is created). `CallContactButton` uses it.
109
+ - **Late join.** The conference name of a batch is only known from the `next-batch` (or `manual-dial`) response; the
110
+ app learns the NEXT batch's name earlier over a WebSocket the API does not have, and the session read does not
111
+ expose it. Voice waits up to 2 s for the rep only when the name was announced, so an SDK softphone joins after the
112
+ dials go out and the first second of an instant answer can be missed (voice's late-join reconciliation still
113
+ connects the audio). Keep the softphone registered BEFORE dialing (`CallContactButton` only enables the call once
114
+ it is ready) so the join is a single `joinConference` the moment the response lands.
115
+ - `next-batch` `user_email`: with a personal key the dialer's rep is the key's own user (naming someone else is a
116
+ 403); with an organisation key the name is passed through but not vouched for, so voice keeps dialing without
117
+ moving the session to that rep.
85
118
 
86
119
  ## Operations
87
120
 
88
121
  | Operation | Method and path | Tier | Used by |
89
122
  |---|---|---|---|
90
- | `voice.listDialerNumbers` | GET /voice/dialer/numbers | read | SessionStartForm |
123
+ | `voice.listDialerNumbers` | GET /voice/dialer/numbers | read | SessionStartForm, CallContactButton |
91
124
  | `dialer.createSessionDraft` | POST /dialer/sessions/draft | write | SessionStartForm |
92
125
  | `dialer.readQueueStats` | GET /dialer/sessions/{id}/queue-stats | read | useDialerSession |
93
126
  | `voice.getDialerSession` | GET /dialer/sessions/{id} | read | useDialerSession |
@@ -106,9 +139,17 @@ and the call log for a rep email. Pass `userId` / `userEmail` from your session
106
139
  | `voice.getCallTranscript` | GET /voice/dialer/calls/{room}/transcript | read | TranscriptViewer |
107
140
  | `voice.getCallGrading` | GET /voice/dialer/calls/{room}/grading | read | TranscriptViewer |
108
141
  | `voice.downloadCallRecording` | GET /voice/calls/{id}/recording | read | TranscriptViewer |
142
+ | `teamMembers.getMyTeamMember` | GET /team-members/me | read | CallContactButton, DialerScreen, SessionStartForm, CallLogScreen (only without `userId` / `userEmail`; via the pickers block `useCurrentMember`) |
143
+ | `voice.listDialerSessions` | GET /voice/dialer/sessions | read | CallContactButton |
144
+ | `dialer.manualDial` | POST /dialer/parallel-calls/manual-dial | external | CallContactButton |
109
145
 
110
146
  ## Recipes (server side)
111
147
 
112
148
  - `01-create-session-and-dial.ts`: draft, one confirmed batch, poll queue-stats.
113
149
  - `02-disposition-calls.ts`: record outcomes from the org's accepted list.
114
150
  - `03-review-a-call.ts`: transcript, AI grade, rep performance (`dialerAnalytics`).
151
+
152
+ ## Cross-block glue
153
+
154
+
155
+ - `RecordCallsTab` (`components/record-calls-tab.tsx`): a contact's call history (`listCallsForContact`, 25 then "Show more" up to the API maximum of 100, then a notice) with a confirmed `CallContactButton`. `extraDetailTabs` entry for ContactsScreen.
@@ -11,7 +11,8 @@
11
11
  "call log, transcript, AI grading and recording per call"
12
12
  ],
13
13
  "dependsOn": [
14
- "lists"
14
+ "lists",
15
+ "pickers"
15
16
  ],
16
17
  "operations": [
17
18
  "create_session_draft_dialer_sessions_draft_post",
@@ -21,15 +22,20 @@
21
22
  "get_call_grading_voice_dialer_calls__room_name__grading_get",
22
23
  "get_call_transcript_voice_dialer_calls__room_name__transcript_get",
23
24
  "get_dialer_session_dialer_sessions__session_id__get",
25
+ "get_my_team_member_team_members_me_get",
24
26
  "hangup_session_call_dialer_sessions_calls_hangup_post",
25
27
  "list_calls_for_contact_voice_dialer_calls_get",
26
28
  "list_dialer_numbers_voice_dialer_numbers_get",
29
+ "list_dialer_sessions_voice_dialer_sessions_get",
30
+ "list_team_members_team_members_get",
27
31
  "list_voice_dispositions_voice_dispositions_get",
32
+ "manual_dial_dialer_parallel_calls_manual_dial_post",
28
33
  "mint_softphone_token_dialer_sessions_token_get",
29
34
  "mute_call_dialer_sessions_calls_mute_post",
30
35
  "pause_dialer_session_dialer_sessions__session_id__pause_post",
31
36
  "read_queue_stats_dialer_sessions__session_id__queue_stats_get",
32
37
  "read_session_grid_dialer_sessions__session_id__grid_post",
38
+ "resume_dialer_session_voice_dialer_sessions__session_id__resume_post",
33
39
  "set_call_disposition_dialer_parallel_calls_calls__livekit_room_name__disposition_patch",
34
40
  "skip_contact_dialer_sessions__session_id__calls_skip_contact_post",
35
41
  "stop_dialer_session_dialer_sessions__session_id__stop_post"
@@ -60,7 +66,9 @@
60
66
  "purpose": "Create a draft session (dials nobody) for a list, caller ID and mode.",
61
67
  "operations": [
62
68
  "create_session_draft_dialer_sessions_draft_post",
63
- "list_dialer_numbers_voice_dialer_numbers_get"
69
+ "get_my_team_member_team_members_me_get",
70
+ "list_dialer_numbers_voice_dialer_numbers_get",
71
+ "list_team_members_team_members_get"
64
72
  ],
65
73
  "tiers": [
66
74
  "read",
@@ -135,6 +143,72 @@
135
143
  "purpose": "The whole rep screen wiring queue, softphone, outcome panel and transcript together.",
136
144
  "operations": [],
137
145
  "tiers": []
146
+ },
147
+ {
148
+ "name": "DialerScreen",
149
+ "file": "components/dialer-screen.tsx",
150
+ "purpose": "Finished calling page: pick a list, run a session, softphone, outcomes, transcript and history.",
151
+ "operations": [
152
+ "get_dialer_session_dialer_sessions__session_id__get",
153
+ "list_dialer_sessions_voice_dialer_sessions_get"
154
+ ],
155
+ "tiers": [
156
+ "read"
157
+ ]
158
+ },
159
+ {
160
+ "name": "CallLogScreen",
161
+ "file": "components/call-log-screen.tsx",
162
+ "purpose": "Finished call history page with outcome filter, recording, transcript and AI grade, and Call back on a call (CallContactButton, confirmed); also exports CallHistory.",
163
+ "operations": [
164
+ "list_calls_for_contact_voice_dialer_calls_get"
165
+ ],
166
+ "tiers": [
167
+ "read"
168
+ ]
169
+ },
170
+ {
171
+ "name": "DialerSessionDetail",
172
+ "file": "components/dialer-session-detail.tsx",
173
+ "purpose": "One dialing session as a full record: stats, details, calls and queue, with pause, confirmed resume and confirmed stop.",
174
+ "operations": [
175
+ "get_dialer_session_dialer_sessions__session_id__get",
176
+ "pause_dialer_session_dialer_sessions__session_id__pause_post",
177
+ "read_queue_stats_dialer_sessions__session_id__queue_stats_get",
178
+ "resume_dialer_session_voice_dialer_sessions__session_id__resume_post",
179
+ "stop_dialer_session_dialer_sessions__session_id__stop_post"
180
+ ],
181
+ "tiers": [
182
+ "read",
183
+ "write",
184
+ "external"
185
+ ]
186
+ },
187
+ {
188
+ "name": "CallContactButton",
189
+ "file": "components/call-contact-button.tsx",
190
+ "purpose": "Call one contact from any screen (contact page, table row): pick the number, a paused or completed session and the caller ID, turn on the softphone, confirm (external), and the softphone joins the call.",
191
+ "operations": [
192
+ "get_my_team_member_team_members_me_get",
193
+ "list_dialer_sessions_voice_dialer_sessions_get",
194
+ "list_dialer_numbers_voice_dialer_numbers_get",
195
+ "manual_dial_dialer_parallel_calls_manual_dial_post"
196
+ ],
197
+ "tiers": [
198
+ "read",
199
+ "external"
200
+ ]
201
+ },
202
+ {
203
+ "name": "RecordCallsTab",
204
+ "file": "components/record-calls-tab.tsx",
205
+ "purpose": "One contact's call history with a confirmed Call button; an extraDetailTabs entry for ContactsScreen.",
206
+ "operations": [
207
+ "list_calls_for_contact_voice_dialer_calls_get"
208
+ ],
209
+ "tiers": [
210
+ "read"
211
+ ]
138
212
  }
139
213
  ],
140
214
  "hooks": [
@@ -222,6 +296,7 @@
222
296
  }
223
297
  ],
224
298
  "scopes": [
299
+ "team:read",
225
300
  "voice:read",
226
301
  "voice:run",
227
302
  "voice:write"
@@ -251,10 +326,16 @@
251
326
  "decision": "Browser audio vendor for the Softphone",
252
327
  "default": "Twilio via lib/twilio-transport.ts; graph8's dialer runs on Twilio conferences. For another vendor write a SoftphoneTransport and delete twilio-transport.ts",
253
328
  "askHuman": true
329
+ },
330
+ {
331
+ "decision": "How a single contact is called outside a dialing session",
332
+ "default": "manual-dial under a PAUSED or COMPLETED session the rep picks (same call log and analytics), only once the softphone is ready; the rep is the personal key's user unless userId is passed",
333
+ "askHuman": false
254
334
  }
255
335
  ],
256
336
  "peerDependencies": {
257
337
  "react": ">=18",
258
- "@twilio/voice-sdk": "^2.18.5"
338
+ "@twilio/voice-sdk": "^2.18.5",
339
+ "lucide-react": ">=0.400.0"
259
340
  }
260
341
  }