@graph8/sdk 0.370.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 (466) hide show
  1. package/README.md +19 -0
  2. package/blocks/_foundation/README.md +14 -0
  3. package/blocks/_foundation/block.json +44 -0
  4. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  5. package/blocks/_foundation/components/empty-state.tsx +5 -2
  6. package/blocks/_foundation/components/list-select.tsx +112 -0
  7. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  8. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  9. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  10. package/blocks/_foundation/components/screen/index.ts +1 -0
  11. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  12. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  13. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  14. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  15. package/blocks/_foundation/lib/paging.ts +57 -0
  16. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  17. package/blocks/agents/README.md +81 -4
  18. package/blocks/agents/block.json +311 -2
  19. package/blocks/agents/components/agent-activity.tsx +112 -0
  20. package/blocks/agents/components/agent-approvals.tsx +135 -0
  21. package/blocks/agents/components/agent-builder.tsx +17 -3
  22. package/blocks/agents/components/agent-conversations.tsx +143 -0
  23. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
  24. package/blocks/agents/components/agent-operators.tsx +164 -0
  25. package/blocks/agents/components/agent-run-detail.tsx +20 -3
  26. package/blocks/agents/components/agent-runs.tsx +3 -3
  27. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  28. package/blocks/agents/components/agent-test-history.tsx +121 -0
  29. package/blocks/agents/components/agents-screen.tsx +53 -11
  30. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  31. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  32. package/blocks/agents/components/from-template-button.tsx +65 -0
  33. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  34. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  35. package/blocks/agents/components/knowledge-form.tsx +69 -0
  36. package/blocks/agents/components/name-dialog.tsx +32 -0
  37. package/blocks/agents/components/receptionist-button.tsx +46 -0
  38. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  39. package/blocks/agents/components/skill-editor.tsx +147 -0
  40. package/blocks/agents/components/skill-library.tsx +111 -0
  41. package/blocks/agents/lib/agent-types.ts +7 -1
  42. package/blocks/agents/lib/facts.tsx +13 -0
  43. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  44. package/blocks/agents/lib/load-more.tsx +141 -0
  45. package/blocks/agents/lib/skill-utils.ts +91 -0
  46. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  47. package/blocks/analytics/components/analytics-screen.tsx +16 -3
  48. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  49. package/blocks/analytics/components/report-builder.tsx +15 -2
  50. package/blocks/appointments/README.md +6 -0
  51. package/blocks/appointments/block.json +27 -0
  52. package/blocks/appointments/components/booking-page.tsx +2 -2
  53. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  54. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  55. package/blocks/appointments/components/meeting-list.tsx +29 -13
  56. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  57. package/blocks/campaigns/components/campaign-assets.tsx +1 -1
  58. package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
  59. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  60. package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
  61. package/blocks/campaigns/components/icp-detail.tsx +11 -2
  62. package/blocks/campaigns/components/icp-editor.tsx +8 -3
  63. package/blocks/campaigns/components/persona-detail.tsx +11 -2
  64. package/blocks/campaigns/components/persona-editor.tsx +6 -1
  65. package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
  66. package/blocks/coach/components/call-scorecard.tsx +7 -7
  67. package/blocks/company-intelligence/README.md +132 -0
  68. package/blocks/company-intelligence/block.json +318 -0
  69. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  70. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  71. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  72. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  73. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  74. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  75. package/blocks/company-intelligence/components/research-run.tsx +161 -0
  76. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  77. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  78. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  79. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  80. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  81. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  82. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  83. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  84. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  85. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  86. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  87. package/blocks/copilot/components/copilot-panel.tsx +1 -1
  88. package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
  89. package/blocks/crm/README.md +6 -0
  90. package/blocks/crm/block.json +53 -4
  91. package/blocks/crm/components/companies-screen.tsx +23 -9
  92. package/blocks/crm/components/company-detail.tsx +2 -2
  93. package/blocks/crm/components/contact-detail.tsx +4 -4
  94. package/blocks/crm/components/contact-lookup-link.tsx +44 -0
  95. package/blocks/crm/components/contacts-screen.tsx +31 -13
  96. package/blocks/crm/components/dedupe-review.tsx +6 -1
  97. package/blocks/crm/components/import-wizard.tsx +3 -7
  98. package/blocks/crm/components/list-select.tsx +20 -0
  99. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  100. package/blocks/crm/components/record-form.tsx +1 -1
  101. package/blocks/crm/components/tasks-screen.tsx +35 -15
  102. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  103. package/blocks/crm/hooks/use-page-query.ts +3 -1
  104. package/blocks/crm/lib/crm-types.ts +14 -0
  105. package/blocks/crm/lib/read-lists.ts +16 -0
  106. package/blocks/deals/README.md +24 -6
  107. package/blocks/deals/block.json +151 -2
  108. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  109. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  110. package/blocks/deals/components/deal-detail.tsx +2 -2
  111. package/blocks/deals/components/deal-filters.tsx +65 -0
  112. package/blocks/deals/components/deal-line-items.tsx +120 -0
  113. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  114. package/blocks/deals/components/deal-related.tsx +30 -9
  115. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  116. package/blocks/deals/components/deals-screen.tsx +57 -22
  117. package/blocks/deals/components/forecast-view.tsx +4 -4
  118. package/blocks/deals/components/pipeline-board.tsx +8 -6
  119. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  120. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  121. package/blocks/deals/components/stage-editor.tsx +1 -1
  122. package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
  123. package/blocks/deals/lib/deal-extras.ts +156 -0
  124. package/blocks/dialer/README.md +14 -5
  125. package/blocks/dialer/block.json +17 -2
  126. package/blocks/dialer/components/call-contact-button.tsx +26 -9
  127. package/blocks/dialer/components/call-log-screen.tsx +4 -1
  128. package/blocks/dialer/components/call-log.tsx +16 -4
  129. package/blocks/dialer/components/call-queue.tsx +3 -3
  130. package/blocks/dialer/components/dialer-screen.tsx +69 -27
  131. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  132. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  133. package/blocks/dialer/components/session-start-form.tsx +58 -11
  134. package/blocks/dialer/components/softphone.tsx +1 -1
  135. package/blocks/enrichment/README.md +16 -6
  136. package/blocks/enrichment/block.json +19 -7
  137. package/blocks/enrichment/components/bulk-enrich-panel.tsx +76 -27
  138. package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
  139. package/blocks/enrichment/components/enrich-button.tsx +1 -1
  140. package/blocks/enrichment/components/enrichment-screen.tsx +70 -17
  141. package/blocks/enrichment/components/waterfall-config.tsx +27 -7
  142. package/blocks/enrichment/lib/enrichment-types.ts +39 -0
  143. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  144. package/blocks/forms/README.md +8 -0
  145. package/blocks/forms/block.json +107 -0
  146. package/blocks/forms/components/form-activity.tsx +71 -0
  147. package/blocks/forms/components/form-backfill.tsx +108 -0
  148. package/blocks/forms/components/form-builder.tsx +1 -1
  149. package/blocks/forms/components/form-embed.tsx +1 -1
  150. package/blocks/forms/components/form-groups.tsx +151 -0
  151. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  152. package/blocks/forms/components/form-route-history.tsx +72 -0
  153. package/blocks/forms/components/form-saved-views.tsx +81 -0
  154. package/blocks/forms/components/forms-screen.tsx +51 -17
  155. package/blocks/forms/components/more-rows.tsx +30 -0
  156. package/blocks/forms/components/submissions-export.tsx +67 -0
  157. package/blocks/forms/components/submissions-table.tsx +15 -6
  158. package/blocks/forms/lib/forms-extra.ts +67 -0
  159. package/blocks/inbox/README.md +5 -0
  160. package/blocks/inbox/block.json +15 -0
  161. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  162. package/blocks/inbox/components/thread-badge.tsx +2 -2
  163. package/blocks/inbox/components/thread-view.tsx +1 -1
  164. package/blocks/integrations/README.md +54 -1
  165. package/blocks/integrations/block.json +189 -15
  166. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  167. package/blocks/integrations/components/connection-health.tsx +59 -0
  168. package/blocks/integrations/components/crm-detail.tsx +20 -1
  169. package/blocks/integrations/components/crm-records.tsx +103 -0
  170. package/blocks/integrations/components/field-mappings.tsx +61 -0
  171. package/blocks/integrations/components/integration-list.tsx +4 -4
  172. package/blocks/integrations/components/integrations-screen.tsx +20 -3
  173. package/blocks/integrations/components/migration-panel.tsx +133 -0
  174. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  175. package/blocks/integrations/components/sync-status.tsx +6 -6
  176. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  177. package/blocks/integrations/components/webhook-detail.tsx +26 -7
  178. package/blocks/integrations/components/webhook-manager.tsx +30 -17
  179. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  180. package/blocks/integrations/lib/crm-utils.ts +39 -0
  181. package/blocks/integrations/lib/facts.tsx +13 -0
  182. package/blocks/integrations/lib/integrations-utils.ts +5 -0
  183. package/blocks/integrations/lib/load-more.tsx +141 -0
  184. package/blocks/intent/components/account-intent-score.tsx +5 -5
  185. package/blocks/intent/components/intent-feed.tsx +8 -8
  186. package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
  187. package/blocks/intent/lib/intent-utils.ts +4 -4
  188. package/blocks/lifecycle/components/account-related.tsx +8 -1
  189. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  190. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  191. package/blocks/lists/README.md +7 -1
  192. package/blocks/lists/block.json +31 -3
  193. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  194. package/blocks/lists/components/add-to-list-button.tsx +17 -7
  195. package/blocks/lists/components/list-picker.tsx +13 -3
  196. package/blocks/lists/components/list-table.tsx +1 -1
  197. package/blocks/lists/components/lists-screen.tsx +4 -2
  198. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  199. package/blocks/lists/components/suppression-manager.tsx +27 -6
  200. package/blocks/lists/components/suppressions-screen.tsx +21 -12
  201. package/blocks/mailboxes/block.json +4 -0
  202. package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
  203. package/blocks/mailboxes/components/domain-detail.tsx +10 -4
  204. package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
  205. package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
  206. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  207. package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
  208. package/blocks/mailboxes/components/warmup-health.tsx +20 -20
  209. package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
  210. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  211. package/blocks/newsletter/README.md +12 -0
  212. package/blocks/newsletter/block.json +117 -0
  213. package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
  214. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  215. package/blocks/newsletter/components/more-rows.tsx +30 -0
  216. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  217. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  218. package/blocks/newsletter/components/newsletter-screen.tsx +36 -13
  219. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  220. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  221. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  222. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  223. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  224. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  225. package/blocks/nurture/components/nurture-builder.tsx +10 -2
  226. package/blocks/nurture/components/nurture-enroll.tsx +6 -7
  227. package/blocks/nurture/components/nurture-form.tsx +5 -8
  228. package/blocks/nurture/components/nurture-screen.tsx +16 -5
  229. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  230. package/blocks/objects/README.md +1 -1
  231. package/blocks/objects/block.json +13 -5
  232. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  233. package/blocks/objects/components/object-record-detail.tsx +8 -1
  234. package/blocks/objects/components/object-schema-editor.tsx +1 -1
  235. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  236. package/blocks/pages/README.md +14 -0
  237. package/blocks/pages/block.json +113 -1
  238. package/blocks/pages/components/more-rows.tsx +30 -0
  239. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  240. package/blocks/pages/components/page-analytics.tsx +176 -0
  241. package/blocks/pages/components/page-clone.tsx +99 -0
  242. package/blocks/pages/components/page-domains.tsx +167 -0
  243. package/blocks/pages/components/page-editor.tsx +1 -1
  244. package/blocks/pages/components/page-generation-status.tsx +48 -0
  245. package/blocks/pages/components/page-tracking.tsx +137 -0
  246. package/blocks/pages/components/pages-screen.tsx +57 -16
  247. package/blocks/pages/components/seo-report.tsx +6 -1
  248. package/blocks/pages/lib/studio.ts +7 -0
  249. package/blocks/phone-numbers/README.md +74 -0
  250. package/blocks/phone-numbers/block.json +262 -0
  251. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  252. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  253. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  254. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  255. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  256. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  257. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  258. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  259. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  260. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  261. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  262. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  263. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  264. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  265. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  266. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  267. package/blocks/pickers/README.md +23 -0
  268. package/blocks/pickers/block.json +103 -0
  269. package/blocks/pickers/components/company-picker.tsx +90 -0
  270. package/blocks/pickers/components/contact-picker.tsx +131 -0
  271. package/blocks/pickers/components/member-picker.tsx +99 -0
  272. package/blocks/pickers/components/search-picker.tsx +98 -0
  273. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  274. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  275. package/blocks/pickers/lib/use-debounced.ts +13 -0
  276. package/blocks/quotes/README.md +13 -0
  277. package/blocks/quotes/block.json +48 -1
  278. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  279. package/blocks/quotes/components/quote-builder.tsx +1 -1
  280. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  281. package/blocks/quotes/components/quote-list.tsx +5 -3
  282. package/blocks/quotes/components/quote-related.tsx +3 -2
  283. package/blocks/quotes/components/quote-viewer.tsx +7 -2
  284. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  285. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  286. package/blocks/radar/components/radar-overview.tsx +4 -4
  287. package/blocks/radar/components/radar-screen.tsx +2 -2
  288. package/blocks/sequencer/README.md +6 -0
  289. package/blocks/sequencer/block.json +28 -1
  290. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  291. package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
  292. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  293. package/blocks/sequencer/components/sequence-builder.tsx +19 -20
  294. package/blocks/sequencer/components/sequence-detail.tsx +4 -2
  295. package/blocks/sequencer/components/sequence-enrollments.tsx +4 -2
  296. package/blocks/sequencer/components/sequence-list.tsx +1 -1
  297. package/blocks/sequencer/components/sequence-stats.tsx +8 -8
  298. package/blocks/sequencer/components/sequences-screen.tsx +3 -0
  299. package/blocks/sequencer/components/step-editor.tsx +11 -11
  300. package/blocks/sequencer/lib/sequencer-utils.ts +5 -5
  301. package/blocks/social-ads/README.md +8 -0
  302. package/blocks/social-ads/block.json +137 -4
  303. package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
  304. package/blocks/social-ads/components/audience-sync.tsx +6 -2
  305. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  306. package/blocks/social-ads/components/campaign-detail.tsx +6 -1
  307. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  308. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  309. package/blocks/social-ads/components/creative-detail.tsx +6 -1
  310. package/blocks/social-ads/components/creative-gallery.tsx +20 -2
  311. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  312. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  313. package/blocks/social-ads/components/post-composer.tsx +4 -2
  314. package/blocks/social-ads/components/post-detail.tsx +3 -0
  315. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  316. package/blocks/social-ads/components/social-performance.tsx +80 -0
  317. package/blocks/social-ads/components/social-screen.tsx +33 -6
  318. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  319. package/blocks/surveys/README.md +91 -0
  320. package/blocks/surveys/block.json +299 -0
  321. package/blocks/surveys/components/account-nps.tsx +69 -0
  322. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  323. package/blocks/surveys/components/org-summary.tsx +82 -0
  324. package/blocks/surveys/components/sending-domains.tsx +113 -0
  325. package/blocks/surveys/components/survey-form.tsx +126 -0
  326. package/blocks/surveys/components/survey-responses.tsx +145 -0
  327. package/blocks/surveys/components/survey-results.tsx +72 -0
  328. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  329. package/blocks/surveys/components/survey-send.tsx +162 -0
  330. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  331. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  332. package/blocks/surveys/lib/surveys-types.ts +228 -0
  333. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  334. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  335. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  336. package/blocks/visitors/components/snippet-install.tsx +18 -9
  337. package/blocks/visitors/components/traffic-chart.tsx +4 -4
  338. package/blocks/visitors/components/visitor-company-card.tsx +24 -14
  339. package/blocks/visitors/components/visitor-feed.tsx +5 -5
  340. package/blocks/webchat/components/chat-widget-config.tsx +1 -1
  341. package/blocks/webchat/components/conversation-list.tsx +1 -1
  342. package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
  343. package/blocks/workbench/components/run-progress.tsx +1 -1
  344. package/blocks/workbench/components/workbench-grid.tsx +1 -1
  345. package/blocks/workbench/components/workbench-screen.tsx +15 -5
  346. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  347. package/blocks/workflows/components/workflow-editor.tsx +1 -1
  348. package/dist/cli.js +536 -392
  349. package/dist/index.browser.js +1 -1
  350. package/dist/index.browser.js.map +1 -1
  351. package/dist/index.browser.mjs +1 -1
  352. package/dist/index.browser.mjs.map +1 -1
  353. package/dist/index.d.mts +2 -2
  354. package/dist/index.d.ts +2 -2
  355. package/dist/index.js +1 -1
  356. package/dist/index.js.map +1 -1
  357. package/dist/index.mjs +1 -1
  358. package/dist/index.mjs.map +1 -1
  359. package/dist/react.browser.js +1 -1
  360. package/dist/react.browser.js.map +1 -1
  361. package/dist/react.browser.mjs +1 -1
  362. package/dist/react.browser.mjs.map +1 -1
  363. package/dist/react.d.mts +2 -2
  364. package/dist/react.d.ts +2 -2
  365. package/dist/react.js +1 -1
  366. package/dist/react.js.map +1 -1
  367. package/dist/react.mjs +1 -1
  368. package/dist/react.mjs.map +1 -1
  369. package/dist/types/blocks/brand.d.ts +11 -2
  370. package/dist/types/blocks/create.d.ts +7 -3
  371. package/dist/types/blocks/doctor.d.ts +1 -0
  372. package/dist/types/blocks/flows.d.ts +7 -1
  373. package/dist/types/blocks/orgBrand.d.ts +34 -0
  374. package/dist/types/cli.d.ts +3 -0
  375. package/dist/types/version.d.ts +2 -2
  376. package/docs/api/desk.d.ts +2 -2
  377. package/docs/api/desk.md +3 -3
  378. package/package.json +1 -1
  379. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  380. package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
  381. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  382. package/stacks/6sense.json +17 -4
  383. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  384. package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
  385. package/stacks/apollo/pages/contacts.tsx.tmpl +29 -95
  386. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  387. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  388. package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
  389. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  390. package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
  391. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  392. package/stacks/apollo.json +36 -8
  393. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  394. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  395. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  396. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  397. package/stacks/calendly.json +8 -4
  398. package/stacks/clay/pages/companies.tsx.tmpl +17 -1
  399. package/stacks/clay/pages/contacts.tsx.tmpl +12 -0
  400. package/stacks/clay/pages/lists.tsx.tmpl +12 -0
  401. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  402. package/stacks/clay.json +16 -3
  403. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  404. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  405. package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
  406. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  407. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  408. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  409. package/stacks/gong.json +23 -6
  410. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  411. package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
  412. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  413. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  414. package/stacks/hootsuite.json +9 -4
  415. package/stacks/hubspot/pages/companies.tsx.tmpl +14 -35
  416. package/stacks/hubspot/pages/contacts.tsx.tmpl +29 -95
  417. package/stacks/hubspot/pages/deals.tsx.tmpl +10 -1
  418. package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
  419. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  420. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  421. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  422. package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
  423. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  424. package/stacks/hubspot.json +21 -5
  425. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  426. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  427. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  428. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  429. package/stacks/intercom.json +8 -4
  430. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  431. package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
  432. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  433. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  434. package/stacks/mailchimp.json +20 -5
  435. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  436. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  437. package/stacks/outreach/pages/contacts.tsx.tmpl +27 -66
  438. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  439. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  440. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  441. package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
  442. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  443. package/stacks/outreach.json +25 -7
  444. package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
  445. package/stacks/pipedrive/pages/contacts.tsx.tmpl +15 -35
  446. package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
  447. package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
  448. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  449. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  450. package/stacks/pipedrive.json +4 -2
  451. package/stacks/salesforce/pages/companies.tsx.tmpl +14 -35
  452. package/stacks/salesforce/pages/contacts.tsx.tmpl +20 -35
  453. package/stacks/salesforce/pages/deals.tsx.tmpl +10 -1
  454. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  455. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  456. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  457. package/stacks/salesforce.json +7 -3
  458. package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
  459. package/stacks/zoominfo/pages/contacts.tsx.tmpl +12 -0
  460. package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
  461. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  462. package/stacks/zoominfo.json +16 -3
  463. package/styles/styles.css +1 -1
  464. package/templates/next-app/app/globals.css.tmpl +349 -64
  465. package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
  466. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
@@ -75,7 +75,7 @@ export declare function renderBrandConfig(f: BrandFields): string;
75
75
  export declare function readBrandConfig(source: string): Partial<BrandFields> | null;
76
76
  /** The config with its region rewritten, or null when the user owns the file (no markers). */
77
77
  export declare function applyBrandConfig(source: string, f: BrandFields): string | null;
78
- export declare const BRAND_USAGE = "Usage: npx @graph8/sdk brand [options]\n\nBrand an app made by `create` (run it in the app's root). Every option is optional; values you leave out stay.\n --name <text> the app's name (sidebar, page title)\n --brand-color <hex> primary colour, e.g. \"#0f62fe\": writes --brand, a 50-900 scale and --primary into\n app/globals.css, with button text checked for WCAG AA (adjusted when needed)\n --dark-brand-color <hex> the colour in dark mode (default: derived from --brand-color, lightened if too dark)\n --logo <path> an svg, png, jpg, webp or gif (max 512 KB), copied to public/brand/ and shown in the sidebar\n --app-url <url> white-label: where link-outs go (default https://app.graph8.com)\n --link-label <text> white-label: what the platform is called in copy and links (default graph8)\n --dir <path> the app's root (default: current directory)\n --dry-run print what would change, write nothing\nRunning it again with the same options changes nothing.";
78
+ export declare const BRAND_USAGE = "Usage: npx @graph8/sdk brand [options]\n\nBrand an app made by `create` (run it in the app's root). Every option is optional; values you leave out stay.\n --name <text> the app's name (sidebar, page title)\n --brand-color <hex> primary colour, e.g. \"#0f62fe\": writes --brand, a 50-900 scale and --primary into\n app/globals.css, with button text checked for WCAG AA (adjusted when needed)\n --dark-brand-color <hex> the colour in dark mode (default: derived from --brand-color, lightened if too dark)\n --logo <path> an svg, png, jpg, webp or gif (max 512 KB), copied to public/brand/ and shown in the sidebar\n --app-url <url> white-label: where link-outs go (default https://app.graph8.com)\n --link-label <text> white-label: what the platform is called in copy and links (default graph8)\n --from-org fill in what you did not pass from the organization's Brand Kit (needs G8_API_KEY in the\n environment or .env.local): name, primary colour, logo. Running brand with no options\n does the same when the kit has something. Values you pass always win; they replace what the app has now.\n --no-org-brand never read the Brand Kit\n --dir <path> the app's root (default: current directory)\n --dry-run print what would change, write nothing\nRunning it again with the same options changes nothing.";
79
79
  export interface BrandOptions {
80
80
  dir: string;
81
81
  name?: string;
@@ -85,6 +85,10 @@ export interface BrandOptions {
85
85
  appUrl?: string;
86
86
  linkLabel?: string;
87
87
  dryRun?: boolean;
88
+ /** `--from-org`, or a bare `brand`: fill the gaps from the org's Brand Kit. */
89
+ fromOrg?: boolean;
90
+ /** `--no-org-brand`. */
91
+ noOrgBrand?: boolean;
88
92
  }
89
93
  /** The brand flags (shared with `create`). Returns true when `argv[i]` was one; `next()` consumes its value. */
90
94
  export declare function takeBrandFlag(o: Partial<BrandOptions>, flag: string, next: () => string): boolean;
@@ -92,6 +96,11 @@ export declare function hasBrandInput(o: Partial<BrandOptions>): boolean;
92
96
  export declare function parseBrandArgs(argv: string[]): BrandOptions;
93
97
  export declare const LOGO_TYPES: string[];
94
98
  export declare const MAX_LOGO_BYTES: number;
99
+ export type OrgBrandValues = Pick<BrandOptions, "name" | "color" | "logo">;
100
+ /** Explicit options win per field; org values fill only what the user left out. */
101
+ export declare function mergeOrgBrand<T extends Partial<BrandOptions>>(explicit: T | undefined, org: OrgBrandValues, skip?: {
102
+ name?: boolean;
103
+ }): T;
95
104
  export interface BrandIO {
96
105
  out: (line: string) => void;
97
106
  err: (line: string) => void;
@@ -109,5 +118,5 @@ export declare function planBrand(o: BrandOptions, root: string, cwd: string, fa
109
118
  notes: string[];
110
119
  };
111
120
  /** Run `brand`. Returns the exit code. */
112
- export declare function runBrand(o: BrandOptions, io: BrandIO, cwd: string, fallbackName?: string): number;
121
+ export declare function runBrand(given: BrandOptions, io: BrandIO, cwd: string, fallbackName?: string, org?: OrgBrandValues): number;
113
122
  export {};
@@ -7,11 +7,11 @@
7
7
  * Node only. Reads `templates/next-app/**` (shipped in the package) and the
8
8
  * block registry; writes only inside `<dir>`.
9
9
  */
10
- import { type BrandOptions } from "./brand";
10
+ import { type OrgBrandValues, type BrandOptions } from "./brand";
11
11
  import { type AddIO, type PackageManager } from "./add";
12
12
  import { componentImportPath, navParts, navUsesExtras, pageFileFor, render } from "./navfile";
13
13
  import { type Registry, type Stack, type StackNav } from "./registry";
14
- export declare const CREATE_USAGE = "Usage: npx @graph8/sdk create <stack> <dir> [options]\n\nScaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and\ninstall the stack's blocks into it. Run `graph8-sdk list` to see the stacks.\n --package-manager <npm|pnpm|yarn|bun> default: the one running the command, else npm\n --no-install write the files and blocks, do not install dependencies\n --dry-run print what would be written, write and install nothing\n --force allow a non-empty <dir> (template files are overwritten, and graph8.json\n is reset: blocks you added to that app earlier are no longer recorded)\nThen: `npm run dev` in <dir>, open http://localhost:3000 and paste a graph8 API key (local preview), and run\n`npx @graph8/sdk doctor --stack <stack>` (add --production before going live).\n\nOr pick the features yourself, for any app (also: `create custom --blocks ...`):\n npx @graph8/sdk create --blocks <id,id,...> [--title \"Acme CRM\"] <dir> [options]\nEvery requested block's dependencies come with it. The app gets one route and one sidebar entry per finished\nScreen of the requested blocks, plus the user-facing Screens of their dependencies (asking for deals also gives\nyou crm's screens). `/` opens the analytics screen if you asked for one, else the first screen.\n --title <text> the app's name (default: graph8 app)\n --with-deps-screens also add the plain fallback page of dependency blocks that have no Screen\n --no-deps-screens add screens only for the blocks you named\nRun `graph8-sdk list` to see each block and which have Screens.\n\nBrand it (any stack or --blocks; the same as running `graph8-sdk brand` in the new app afterwards):\n --name <text> the app's name (with --blocks, the same as --title)\n --brand-color <hex> primary colour, AA-checked; --dark-brand-color <hex> for dark mode\n --logo <path> svg/png/jpg/webp/gif, shown in the sidebar\n --app-url <url> white-label: where \"Open in graph8\" links go; --link-label <text>: what they call it\nA directory literally named `custom` would read as the alias: pass `--blocks crm ./custom`.";
14
+ export declare const CREATE_USAGE = "Usage: npx @graph8/sdk create <stack> <dir> [options]\n\nScaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and\ninstall the stack's blocks into it. Run `graph8-sdk list` to see the stacks.\n --package-manager <npm|pnpm|yarn|bun> default: the one running the command, else npm\n --no-install write the files and blocks, do not install dependencies\n --dry-run print what would be written, write and install nothing\n --force allow a non-empty <dir> (template files are overwritten, and graph8.json\n is reset: blocks you added to that app earlier are no longer recorded)\nThen: `npm run dev` in <dir>, open http://localhost:3000 and paste a graph8 API key (local preview), and run\n`npx @graph8/sdk doctor --stack <stack>` (add --production before going live).\n\nOr pick the features yourself, for any app (also: `create custom --blocks ...`):\n npx @graph8/sdk create --blocks <id,id,...> [--title \"Acme CRM\"] <dir> [options]\nEvery requested block's dependencies come with it. The app gets one route and one sidebar entry per finished\nScreen of the requested blocks, plus the user-facing Screens of their dependencies (asking for deals also gives\nyou crm's screens). `/` opens the analytics screen if you asked for one, else the first screen.\n --title <text> the app's name (default: graph8 app)\n --with-deps-screens also add the plain fallback page of dependency blocks that have no Screen\n --no-deps-screens add screens only for the blocks you named\nRun `graph8-sdk list` to see each block and which have Screens.\n\nBrand it (any stack or --blocks; the same as running `graph8-sdk brand` in the new app afterwards):\n --name <text> the app's name (with --blocks, the same as --title)\n --brand-color <hex> primary colour, AA-checked; --dark-brand-color <hex> for dark mode\n --logo <path> svg/png/jpg/webp/gif, shown in the sidebar\n --app-url <url> white-label: where \"Open in graph8\" links go; --link-label <text>: what they call it\nWith a graph8 API key (G8_API_KEY in the environment or .env.local) the org's Brand Kit fills in what you did not\npass: the app name, the primary colour and the logo (downloaded into public/brand/). No key, no kit or an error:\nthe default look, one info line, never a failure. --no-org-brand skips it.\nA directory literally named `custom` would read as the alias: pass `--blocks crm ./custom`.";
15
15
  export interface CreateOptions {
16
16
  stack: string;
17
17
  dir: string;
@@ -26,6 +26,8 @@ export interface CreateOptions {
26
26
  depsScreens?: "default" | "with" | "none";
27
27
  /** `--name`, `--brand-color`, `--logo`, ...: applied with `graph8-sdk brand` once the app is written. */
28
28
  brand?: Omit<BrandOptions, "dir" | "dryRun">;
29
+ /** false with `--no-org-brand`: do not pull the org's Brand Kit. */
30
+ orgBrand?: false;
29
31
  }
30
32
  export declare function parseCreateArgs(argv: string[]): CreateOptions;
31
33
  /** What `graph8.json` records about a custom app, so `add` and `doctor` know its routes. */
@@ -45,6 +47,8 @@ export interface CreateIO extends AddIO {
45
47
  sdkPackageJson?: string;
46
48
  /** npm_config_user_agent, to default the package manager. */
47
49
  userAgent?: string;
50
+ /** Name, colour and logo pulled from the org's Brand Kit (see orgBrand.ts); explicit brand flags win. */
51
+ orgBrand?: OrgBrandValues;
48
52
  }
49
53
  export declare function packageManagerFromUserAgent(ua: string | undefined): PackageManager;
50
54
  /** A valid npm package name from a directory name. */
@@ -59,5 +63,5 @@ export declare function stackProblem(reg: Registry, stack: Stack): string | null
59
63
  export declare function stackPageFile(reg: Registry, stack: Stack, n: StackNav): string | null;
60
64
  export declare function planCreate(reg: Registry, stack: Stack, appName: string, pm: PackageManager, io: CreateIO, app?: AppRecord): OutFile[];
61
65
  /** Run `create`. Returns the process exit code. */
62
- export declare function runCreate(opts: CreateOptions, reg: Registry, io: CreateIO, cwd: string): number;
66
+ export declare function runCreate(given: CreateOptions, reg: Registry, io: CreateIO, cwd: string): number;
63
67
  export { componentImportPath, navParts, navUsesExtras, pageFileFor, render };
@@ -25,6 +25,7 @@ export interface DoctorIO {
25
25
  env: Record<string, string | undefined>;
26
26
  fetch?: typeof fetch;
27
27
  }
28
+ export declare function readSetting(name: string, root: string, env: DoctorIO["env"]): string | undefined;
28
29
  /** Same rules as the server's `key_has_scope`. */
29
30
  export declare function scopeGranted(granted: string[], required: string): boolean;
30
31
  /** Run doctor. Returns the exit code: 0 when every check passes. */
@@ -14,7 +14,13 @@ export interface UnreachedComponent {
14
14
  component: string;
15
15
  }
16
16
  /**
17
- * Components of the stack's own blocks (the foundation's shared UI is exempt) that no
17
+ * Library blocks hold shared building blocks that other blocks import, not pages of their own: the foundation's shared
18
+ * UI and the pickers. A stack installs them whole but each screen uses only some, so they are never "unreached".
19
+ * Every other block is judged component by component.
20
+ */
21
+ export declare const LIBRARY_BLOCKS: ReadonlySet<string>;
22
+ /**
23
+ * Components of the stack's own blocks (library blocks are exempt) that no
18
24
  * generated page reaches, directly or through a Screen that imports them.
19
25
  */
20
26
  export declare function unreachedComponents(reg: Registry, stack: Stack): UnreachedComponent[];
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pull the organization's Brand Kit into a new app: `create` and `brand --from-org`.
3
+ *
4
+ * Reads two Developer API operations with the project's key (the same resolution `doctor` uses: G8_API_KEY from
5
+ * the environment, then .env.local; G8_API_URL for a non-default API):
6
+ * - get_brand_kit_overview_brand_kit_get -> logo_url, design_tokens.colors, source_url (scope campaigns:read)
7
+ * - describe_current_key_me_get -> org_name
8
+ * The kit has ONE logo URL (no light/dark/icon variant), a measured colour palette and fonts, a tagline and voice
9
+ * traits. Only the name, the primary colour and the logo map onto the brand inputs; fonts, tagline and voice have no
10
+ * SDK brand input and are ignored.
11
+ *
12
+ * Never fails the caller: no key, a 4xx/5xx, a network error, a timeout or an empty kit all give `{}` plus one info
13
+ * line. The key is never printed or written. The logo is downloaded to a temp file (https or data: URL, an image type,
14
+ * at most 512 KB) so `brand` copies it into public/brand/ like any `--logo` file: nothing is hot-linked.
15
+ */
16
+ import { mergeOrgBrand, type OrgBrandValues } from "./brand";
17
+ export { mergeOrgBrand, type OrgBrandValues };
18
+ export interface OrgBrandPull {
19
+ values: OrgBrandValues;
20
+ /** One line for the CLI to print. */
21
+ info: string;
22
+ /** Remove the temp logo file. */
23
+ cleanup: () => void;
24
+ }
25
+ export interface OrgBrandIO {
26
+ env: Record<string, string | undefined>;
27
+ fetch?: typeof fetch;
28
+ }
29
+ /** The primary colour of the kit's measured tokens, or null. */
30
+ export declare function pickColor(tokens: unknown): string | null;
31
+ /** A display name that is safe to write into generated files. */
32
+ export declare function cleanName(v: unknown): string | null;
33
+ /** Pull name, primary colour and logo from the org's Brand Kit. Never throws. */
34
+ export declare function pullOrgBrand(io: OrgBrandIO, root: string): Promise<OrgBrandPull>;
@@ -10,6 +10,7 @@
10
10
  * Node only. Built as a separate entry (dist/cli.js), so nothing here reaches
11
11
  * the browser bundle or the package's main exports.
12
12
  */
13
+ import type { OrgBrandValues } from "./blocks/brand";
13
14
  export interface CliIO {
14
15
  cwd: string;
15
16
  out: (line: string) => void;
@@ -26,6 +27,8 @@ export interface CliIO {
26
27
  env?: Record<string, string | undefined>;
27
28
  /** fetch for `doctor`. Default: global fetch. */
28
29
  fetch?: typeof fetch;
30
+ /** Name, colour and logo pulled from the org's Brand Kit by `runCliAsync` for `create` and `brand`. */
31
+ orgBrand?: OrgBrandValues;
29
32
  }
30
33
  export declare function runCli(argv: string[], io: CliIO): number;
31
34
  /** Like `runCli`, plus the commands that talk to the network (`doctor`). */
@@ -5,6 +5,6 @@
5
5
  * both together (the version-parity gate already pins package.json, the
6
6
  * lockfile and the CHANGELOG to each other).
7
7
  */
8
- export declare const SDK_VERSION = "0.370.0";
8
+ export declare const SDK_VERSION = "0.373.0";
9
9
  /** Value of the `X-G8-Client` header every SDK request carries (I-13). */
10
- export declare const SDK_CLIENT_ID = "js-sdk/0.370.0";
10
+ export declare const SDK_CLIENT_ID = "js-sdk/0.373.0";
@@ -155,7 +155,7 @@ export interface DeskOperations {
155
155
  };
156
156
  /**
157
157
  * POST /desk/cards
158
- * Create Desk Card
158
+ * Put one planned outreach touch (an email, LinkedIn or call step for one contact) on a rep's desk with its why, facts and draft. The rep edits and sends it; nothing sends by itself. owner_id must be the caller's own email unless the key is an org or machine key: a personal key or an OAuth connection is a person and gets 403 owner_not_self for anyone else. Posting the same external_key again updates the open card instead of adding a second one. Needs deals:write and a key allowed to see the contact
159
159
  * tier: write · scope: deals:write
160
160
  */
161
161
  create_desk_card_desk_cards_post: {
@@ -1582,7 +1582,7 @@ export interface DeskCardCreateRequest {
1582
1582
  external_key?: string | null;
1583
1583
  list_id?: number | null;
1584
1584
  origin?: DeskCardOrigin | null;
1585
- /** Whose desk: a team member's email, id or user id. */
1585
+ /** Whose desk: a team member's email, id or user id. With a personal key or an OAuth connection this must be the caller's own email (the key is a person; anyone else is 403 owner_not_self). Only an org or machine key may name another rep. */
1586
1586
  owner_id: string;
1587
1587
  /** low, normal, high or urgent. */
1588
1588
  priority?: string;
package/docs/api/desk.md CHANGED
@@ -26,7 +26,7 @@ Exact TypeScript types for every operation on this page, and every schema they u
26
26
  | [`askDesk`](#op-askdesk) | `POST /api/v1/desk/ask` | billable | `deals:run` | Ask Desk |
27
27
  | [`checkCommitteePlay`](#op-checkcommitteeplay) | `GET /api/v1/desk/accounts/{account_id}/committee/plays/{action_id}/check` | read | `deals:read` | Check Committee Play |
28
28
  | [`checkDeskPlanStageSweep`](#op-checkdeskplanstagesweep) | `GET /api/v1/desk/org-settings/plan-stages/check` | read | `deals:read` | Check Desk Plan Stage Sweep |
29
- | [`createDeskCard`](#op-createdeskcard) | `POST /api/v1/desk/cards` | write | `deals:write` | Create Desk Card |
29
+ | [`createDeskCard`](#op-createdeskcard) | `POST /api/v1/desk/cards` | write | `deals:write` | Put one planned outreach touch (an email, LinkedIn or call step for one contact) on a rep's desk with its why, facts an… |
30
30
  | [`createOrReplanAccountPlan`](#op-createorreplanaccountplan) | `POST /api/v1/desk/accounts/{account_id}/plan` | write | `deals:write` | Create Or Replan Account Plan |
31
31
  | [`decideDeskPlayApproval`](#op-decidedeskplayapproval) | `POST /api/v1/desk/plays/approvals/{execution_id}/decide` | external | `deals:run` | Decide Desk Play Approval |
32
32
  | [`draftAccountPlan`](#op-draftaccountplan) | `POST /api/v1/desk/accounts/{account_id}/plan/draft` | billable | `deals:run` | Draft Account Plan |
@@ -261,7 +261,7 @@ const result = await g8.api.desk.checkDeskPlanStageSweep();
261
261
 
262
262
  ### `g8.api.desk.createDeskCard(input)`
263
263
 
264
- **Create Desk Card**
264
+ **Put one planned outreach touch (an email, LinkedIn or call step for one contact) on a rep's desk with its why, facts and draft. The rep edits and sends it; nothing sends by itself. owner_id must be t…**
265
265
 
266
266
  - HTTP: `POST /api/v1/desk/cards`
267
267
  - operationId: `create_desk_card_desk_cards_post` (also callable as `g8.api.call("create_desk_card_desk_cards_post", input)`)
@@ -297,7 +297,7 @@ Body fields (`DeskCardCreateRequest`):
297
297
  |---|---|---|---|
298
298
  | `body.channel` | `string` | yes | email, linkedin or call. min length 1, max length 32 |
299
299
  | `body.contact_id` | `integer` | yes | The contact the touch is for. max 9223372036854776000, > 0 |
300
- | `body.owner_id` | `string` | yes | Whose desk: a team member's email, id or user id. min length 1, max length 320 |
300
+ | `body.owner_id` | `string` | yes | Whose desk: a team member's email, id or user id. With a personal key or an OAuth connection this must be the caller's own email (the key is a person; anyone else is 403 owner_not_self). Only an org or machine key may name another rep. min length 1, max length 320 |
301
301
  | `body.account_id` | `integer \| null` | no | The contact's company, when it is not the contact's own. max 9223372036854776000, > 0 |
302
302
  | `body.context` | `DeskCardContext \| null` | no | |
303
303
  | `body.draft` | `DeskCardDraft \| null` | no | |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graph8/sdk",
3
- "version": "0.370.0",
3
+ "version": "0.373.0",
4
4
  "description": "graph8 SDK - CRM, enrichment, sequences, campaigns, and tracking for B2B",
5
5
  "license": "MIT",
6
6
  "author": "graph8 <support@graph8.com> (https://graph8.com)",
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
+ // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
+ import { CompaniesScreen } from "@/components/g8/crm/companies-screen";
6
+ import { CompanyIntelligenceTab } from "@/components/g8/company-intelligence/company-intelligence-tab";
7
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
8
+ import { useRouter } from "next/navigation";
9
+
10
+ export default function Page() {
11
+ const router = useRouter();
12
+ return (
13
+ <CompaniesScreen
14
+ onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
15
+ extraDetailTabs={[
16
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
17
+ { id: "intelligence", label: "Intelligence", render: (c) => <CompanyIntelligenceTab company={c} onOpenIntelligence={() => router.push("/company-intelligence")} /> },
18
+ ]}
19
+ />
20
+ );
21
+ }
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
+ // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
+ import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
7
+ import { useRouter } from "next/navigation";
8
+
9
+ export default function Page() {
10
+ const router = useRouter();
11
+ return (
12
+ <ContactsScreen
13
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
14
+ extraDetailTabs={[
15
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
16
+ ]}
17
+ />
18
+ );
19
+ }
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
+ // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
+ import { useRouter } from "next/navigation";
6
+ import { TasksScreen } from "@/components/g8/crm/tasks-screen";
7
+
8
+ /** A task's linked record opens in the screen that owns it. */
9
+ export default function Page() {
10
+ const router = useRouter();
11
+ return (
12
+ <TasksScreen
13
+ onOpenRecord={(record) => {
14
+ if (record.entity_type === "contact") router.push(`/contacts?id=${encodeURIComponent(record.entity_id)}`);
15
+ if (record.entity_type === "company") router.push(`/companies?id=${encodeURIComponent(record.entity_id)}`);
16
+ }}
17
+ />
18
+ );
19
+ }
@@ -9,7 +9,8 @@
9
9
  "crm",
10
10
  "intent",
11
11
  "visitors",
12
- "radar"
12
+ "radar",
13
+ "company-intelligence"
13
14
  ],
14
15
  "home": "/intent",
15
16
  "nav": [
@@ -47,7 +48,17 @@
47
48
  "component": "CompaniesScreen",
48
49
  "icon": "Building2",
49
50
  "group": "Prospect",
50
- "description": "Accounts and the people at them"
51
+ "description": "Accounts and the people at them",
52
+ "page": "pages/companies.tsx.tmpl"
53
+ },
54
+ {
55
+ "label": "Company intelligence",
56
+ "path": "/company-intelligence",
57
+ "block": "company-intelligence",
58
+ "component": "CompanyIntelligenceScreen",
59
+ "icon": "Telescope",
60
+ "group": "Signals",
61
+ "description": "Research your website: profile, competitors, news, documents and reports"
51
62
  },
52
63
  {
53
64
  "label": "Contacts",
@@ -56,7 +67,8 @@
56
67
  "component": "ContactsScreen",
57
68
  "icon": "Users",
58
69
  "group": "Prospect",
59
- "description": "People you sell to, with notes, tasks and activity"
70
+ "description": "People you sell to, with notes, tasks and activity",
71
+ "page": "pages/contacts.tsx.tmpl"
60
72
  },
61
73
  {
62
74
  "label": "Tasks",
@@ -65,7 +77,8 @@
65
77
  "component": "TasksScreen",
66
78
  "icon": "CheckSquare",
67
79
  "group": "Work",
68
- "description": "Follow-ups due across your records"
80
+ "description": "Follow-ups due across your records",
81
+ "page": "pages/tasks.tsx.tmpl"
69
82
  }
70
83
  ]
71
84
  }
@@ -0,0 +1,23 @@
1
+ "use client";
2
+
3
+ // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
+ // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
+ import { useRouter } from "next/navigation";
6
+ import { Button } from "@graph8/sdk/ui";
7
+ import { UserRound } from "lucide-react";
8
+ import { CallLogScreen } from "@/components/g8/dialer/call-log-screen";
9
+
10
+ /** A call opens the person it was made to. */
11
+ export default function Page() {
12
+ const router = useRouter();
13
+ return (
14
+ <CallLogScreen
15
+ renderDetailActions={(call) => call.contact_id ? (
16
+ <Button size="sm" variant="outline" onClick={() => router.push(`/contacts?id=${encodeURIComponent(call.contact_id as string)}`)}>
17
+ <UserRound className="mr-1 size-4" aria-hidden />
18
+ Open contact
19
+ </Button>
20
+ ) : null}
21
+ />
22
+ );
23
+ }
@@ -2,49 +2,32 @@
2
2
 
3
3
  // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
4
  // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
- import Link from "next/link";
6
- import { Skeleton } from "@graph8/sdk/ui";
7
- import { Handshake } from "lucide-react";
8
- import { ApiError } from "@/components/g8/_foundation/api-error";
9
- import { EmptyState } from "@/components/g8/_foundation/empty-state";
10
- import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
11
- import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
12
5
  import { CompaniesScreen } from "@/components/g8/crm/companies-screen";
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
7
+ import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
13
8
  import { EnrichButton } from "@/components/g8/enrichment/enrich-button";
14
- import { dataOf, formatMoney } from "@/components/g8/deals/lib/deals-types";
15
-
16
- /** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
17
- interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
18
-
19
- /** The deals linked to one contact or company, each opening in the Deals page. */
20
- function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
21
- const q = useG8Query(async (signal) => {
22
- const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
23
- return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
24
- }, [kind, id]);
25
- if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
26
- if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
27
- if (q.data.length === 0) return <EmptyState compact icon={<Handshake className="size-8" />} title="No deals yet" description="Deals linked to this record show up here." />;
28
- return (
29
- <ul className="divide-y rounded-md border" aria-label="Deals">
30
- {q.data.map((d) => (
31
- <li key={d.deal_id}>
32
- <Link href={`/deals?id=${encodeURIComponent(d.deal_id as string)}`} className="flex items-center justify-between gap-3 p-3 text-sm hover:bg-muted">
33
- <span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
34
- <span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
35
- </Link>
36
- </li>
37
- ))}
38
- </ul>
39
- );
40
- }
9
+ import { AddCompanyPeopleToListButton } from "@/components/g8/lists/add-company-people-to-list-button";
10
+ import { CompanyIntelligenceTab } from "@/components/g8/company-intelligence/company-intelligence-tab";
11
+ import { useRouter } from "next/navigation";
41
12
 
42
13
  export default function Page() {
14
+ const router = useRouter();
43
15
  return (
44
16
  <CompaniesScreen
17
+ onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
45
18
  renderRowActions={(c, { reload }) => <EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" size="sm" variant="ghost" />}
46
- renderDetailActions={(c, { reload }) => <EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />}
47
- extraDetailTabs={[{ id: "deals", label: "Deals", render: (c) => <LinkedDeals kind="companies" id={c.id} /> }]}
19
+ renderBulkActions={({ ids, clear }) => <AddCompanyPeopleToListButton companyIds={ids} onAdded={clear} />}
20
+ renderDetailActions={(c, { reload }) => (
21
+ <>
22
+ <EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />
23
+ <AddCompanyPeopleToListButton companyIds={[c.id]} />
24
+ </>
25
+ )}
26
+ extraDetailTabs={[
27
+ { id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="companies" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
28
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
29
+ { id: "intelligence", label: "Intelligence", render: (c) => <CompanyIntelligenceTab company={c} onOpenIntelligence={() => router.push("/company-intelligence")} /> },
30
+ ]}
48
31
  />
49
32
  );
50
33
  }
@@ -2,116 +2,50 @@
2
2
 
3
3
  // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
4
  // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
- import { useState } from "react";
6
- import Link from "next/link";
7
- import { Button, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Skeleton } from "@graph8/sdk/ui";
8
- import { Handshake, ListPlus } from "lucide-react";
9
- import { ApiError } from "@/components/g8/_foundation/api-error";
10
- import { EmptyState } from "@/components/g8/_foundation/empty-state";
11
- import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
12
- import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
5
+ import { RecordMeetingsTab } from "@/components/g8/appointments/record-meetings-tab";
13
6
  import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
7
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
8
+ import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
14
9
  import { CallContactButton } from "@/components/g8/dialer/call-contact-button";
10
+ import { RecordCallsTab } from "@/components/g8/dialer/record-calls-tab";
15
11
  import { EnrichButton } from "@/components/g8/enrichment/enrich-button";
16
- import { dataOf, formatMoney } from "@/components/g8/deals/lib/deals-types";
17
- import { EnrollDialog } from "@/components/g8/sequencer/enroll-dialog";
18
- import { listOf, statusGroup, type SequenceRow } from "@/components/g8/sequencer/lib/sequencer-utils";
19
-
20
- /** Every active sequence, paging through the whole list (the API answers one page at a time). */
21
- async function activeSequences(signal: AbortSignal): Promise<SequenceRow[]> {
22
- const out: SequenceRow[] = [];
23
- for (let page = 1; page <= 50; page++) {
24
- const env = await g8Get("/sequences", { page, limit: 100 }, { signal });
25
- const rows = listOf<SequenceRow>(env);
26
- out.push(...rows.filter((s) => statusGroup(s.status) === "live"));
27
- const more = (env as { pagination?: { has_next?: boolean } }).pagination?.has_next;
28
- if (!more || rows.length === 0) break;
29
- }
30
- return out;
31
- }
32
-
33
- /** Pick a sequence, then enroll the contacts in it. The enroll dialog shows the sending preflight before anything is sent. */
34
- function AddToSequence({ contactIds, onEnrolled, variant = "outline" }: { contactIds: number[]; onEnrolled?: () => void; variant?: "outline" | "default" }) {
35
- const [step, setStep] = useState<"closed" | "pick" | "enroll">("closed");
36
- const [sequenceId, setSequenceId] = useState("");
37
- const sequences = useG8Query(activeSequences, [], { enabled: step === "pick" });
38
- const chosen = (sequences.data ?? []).find((s) => s.id === sequenceId);
39
- return (
40
- <>
41
- <Button size="sm" variant={variant} disabled={contactIds.length === 0} onClick={() => { setSequenceId(""); setStep("pick"); }}>
42
- <ListPlus className="mr-1 size-4" aria-hidden />
43
- Add to sequence
44
- </Button>
45
- <Sheet open={step === "pick"} onOpenChange={(o) => { if (!o) setStep("closed"); }}>
46
- <SheetContent className="w-full sm:max-w-md">
47
- <SheetHeader>
48
- <SheetTitle>Add to sequence</SheetTitle>
49
- <SheetDescription>{contactIds.length === 1 ? "1 contact" : `${contactIds.length} contacts`} will be enrolled in the sequence you pick.</SheetDescription>
50
- </SheetHeader>
51
- <div className="space-y-3 p-4">
52
- {sequences.error ? <ApiError error={sequences.error} title="Could not load sequences" onRetry={() => void sequences.refetch()} /> : null}
53
- {sequences.loading && !sequences.data ? <Skeleton className="h-9 w-full" aria-label="Loading sequences" /> : null}
54
- {sequences.data && sequences.data.length === 0 ? <EmptyState compact title="No active sequences" description="Launch a sequence on the Sequences page first." /> : null}
55
- {sequences.data && sequences.data.length > 0 ? (
56
- <>
57
- <label htmlFor="enroll-sequence" className="text-sm font-medium">Sequence</label>
58
- <select id="enroll-sequence" className="h-9 w-full rounded-md border bg-background px-2 text-sm" value={sequenceId} onChange={(e) => setSequenceId(e.target.value)}>
59
- <option value="">Choose a sequence</option>
60
- {sequences.data.map((s) => <option key={s.id} value={s.id}>{s.name || s.id}</option>)}
61
- </select>
62
- <Button disabled={!sequenceId} onClick={() => setStep("enroll")}>Continue</Button>
63
- </>
64
- ) : null}
65
- </div>
66
- </SheetContent>
67
- </Sheet>
68
- {step === "enroll" && chosen ? (
69
- <EnrollDialog open sequenceId={chosen.id} sequenceName={chosen.name ?? undefined} contactIds={contactIds} onClose={() => setStep("closed")} onEnrolled={() => { setStep("closed"); onEnrolled?.(); }} />
70
- ) : null}
71
- </>
72
- );
73
- }
74
-
75
- /** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
76
- interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
77
-
78
- /** The deals linked to one contact or company, each opening in the Deals page. */
79
- function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
80
- const q = useG8Query(async (signal) => {
81
- const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
82
- return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
83
- }, [kind, id]);
84
- if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
85
- if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
86
- if (q.data.length === 0) return <EmptyState compact icon={<Handshake className="size-8" />} title="No deals yet" description="Deals linked to this record show up here." />;
87
- return (
88
- <ul className="divide-y rounded-md border" aria-label="Deals">
89
- {q.data.map((d) => (
90
- <li key={d.deal_id}>
91
- <Link href={`/deals?id=${encodeURIComponent(d.deal_id as string)}`} className="flex items-center justify-between gap-3 p-3 text-sm hover:bg-muted">
92
- <span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
93
- <span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
94
- </Link>
95
- </li>
96
- ))}
97
- </ul>
98
- );
99
- }
12
+ import { RecordConversationsTab } from "@/components/g8/inbox/record-conversations-tab";
13
+ import { AddToListButton } from "@/components/g8/lists/add-to-list-button";
14
+ import { RecordListsTab } from "@/components/g8/lists/record-lists-tab";
15
+ import { AddToSequenceButton } from "@/components/g8/sequencer/add-to-sequence-button";
16
+ import { RecordSequencesTab } from "@/components/g8/sequencer/record-sequences-tab";
17
+ import { useRouter } from "next/navigation";
100
18
 
101
19
  export default function Page() {
20
+ const router = useRouter();
102
21
  return (
103
22
  <ContactsScreen
23
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
104
24
  renderRowActions={(c, { reload }) => <EnrichButton kind="person" lookup={{ email: c.work_email ?? undefined, linkedin_url: c.linkedin_url ?? undefined, first_name: c.first_name ?? undefined, last_name: c.last_name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" size="sm" variant="ghost" />}
105
- renderBulkActions={({ ids, clear }) => <AddToSequence contactIds={ids} onEnrolled={clear} />}
25
+ renderBulkActions={({ ids, clear }) => (
26
+ <>
27
+ <AddToSequenceButton contactIds={ids} onEnrolled={clear} />
28
+ <AddToListButton contactIds={ids} onAdded={clear} />
29
+ </>
30
+ )}
106
31
  renderDetailActions={(c, { reload }) => (
107
32
  <>
108
33
  <EnrichButton kind="person" lookup={{ email: c.work_email ?? undefined, linkedin_url: c.linkedin_url ?? undefined, first_name: c.first_name ?? undefined, last_name: c.last_name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />
109
- <AddToSequence contactIds={[c.id]} />
34
+ <AddToSequenceButton contactIds={[c.id]} />
35
+ <AddToListButton contactIds={[c.id]} />
110
36
  {/* One call to this contact through the dialer; confirmed, and your softphone joins it. */}
111
37
  <CallContactButton contact={{ id: c.id, first_name: c.first_name, last_name: c.last_name, full_name: c.full_name, email: c.work_email, company: c.company_name, mobile_phone: c.mobile_phone, direct_phone: c.direct_phone }} />
112
38
  </>
113
39
  )}
114
- extraDetailTabs={[{ id: "deals", label: "Deals", render: (c) => <LinkedDeals kind="contacts" id={c.id} /> }]}
40
+ extraDetailTabs={[
41
+ { id: "lists", label: "Lists", render: (c) => <RecordListsTab contactId={c.id} onOpenList={(id) => router.push(`/lists?id=${id}`)} /> },
42
+ { id: "sequences", label: "Sequences", render: (c) => <RecordSequencesTab contactId={c.id} onOpenSequence={(id) => router.push(`/sequences?id=${encodeURIComponent(id)}`)} /> },
43
+ { id: "meetings", label: "Meetings", render: (c) => <RecordMeetingsTab contactId={c.id} onOpenMeeting={(sel) => router.push(`/meetings?sel=${encodeURIComponent(sel)}`)} /> },
44
+ { id: "calls", label: "Calls", render: (c) => <RecordCallsTab contact={{ id: c.id, first_name: c.first_name, last_name: c.last_name, full_name: c.full_name, email: c.work_email, company: c.company_name, mobile_phone: c.mobile_phone, direct_phone: c.direct_phone }} onOpenCall={(room) => router.push(`/calls?id=${encodeURIComponent(room)}`)} /> },
45
+ { id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="contacts" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
46
+ { id: "conversations", label: "Conversations", render: (c) => <RecordConversationsTab email={c.work_email} alsoEmails={[c.personal_emails]} onOpenThread={(sel) => router.push(`/inbox?id=${encodeURIComponent(sel)}`)} /> },
47
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
48
+ ]}
115
49
  />
116
50
  );
117
51
  }
@@ -0,0 +1,17 @@
1
+ "use client";
2
+
3
+ // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
+ // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
+ import { useRouter } from "next/navigation";
6
+ import { DealsScreen } from "@/components/g8/deals/deals-screen";
7
+
8
+ /** A deal opens its quotes, its contacts and (from a contact) their company. */
9
+ export default function Page() {
10
+ const router = useRouter();
11
+ return (
12
+ <DealsScreen
13
+ onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
14
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,13 @@
1
+ "use client";
2
+
3
+ // Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
4
+ // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
+ import { useRouter } from "next/navigation";
6
+ import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
7
+ import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
8
+
9
+ /** A conversation opens the sender when they are a contact in your CRM. */
10
+ export default function Page() {
11
+ const router = useRouter();
12
+ return <InboxScreen renderDetailActions={(thread) => <ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />} />;
13
+ }