@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
@@ -2,46 +2,22 @@
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";
13
- import { dataOf, formatMoney } from "@/components/g8/deals/lib/deals-types";
14
-
15
- /** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
16
- interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
17
-
18
- /** The deals linked to one contact or company, each opening in the Deals page. */
19
- function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
20
- const q = useG8Query(async (signal) => {
21
- const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
22
- return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
23
- }, [kind, id]);
24
- if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
25
- if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
26
- 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." />;
27
- return (
28
- <ul className="divide-y rounded-md border" aria-label="Deals">
29
- {q.data.map((d) => (
30
- <li key={d.deal_id}>
31
- <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">
32
- <span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
33
- <span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
34
- </Link>
35
- </li>
36
- ))}
37
- </ul>
38
- );
39
- }
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
7
+ import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
8
+ import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
9
+ import { useRouter } from "next/navigation";
40
10
 
41
11
  export default function Page() {
12
+ const router = useRouter();
42
13
  return (
43
14
  <CompaniesScreen
44
- extraDetailTabs={[{ id: "deals", label: "Deals", render: (c) => <LinkedDeals kind="companies" id={c.id} /> }]}
15
+ onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
16
+ extraDetailTabs={[
17
+ { id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="companies" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
18
+ { id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="companies" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
19
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
20
+ ]}
45
21
  />
46
22
  );
47
23
  }
@@ -2,46 +2,26 @@
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";
5
+ import { RecordMeetingsTab } from "@/components/g8/appointments/record-meetings-tab";
12
6
  import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
13
- import { dataOf, formatMoney } from "@/components/g8/deals/lib/deals-types";
14
-
15
- /** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
16
- interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
17
-
18
- /** The deals linked to one contact or company, each opening in the Deals page. */
19
- function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
20
- const q = useG8Query(async (signal) => {
21
- const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
22
- return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
23
- }, [kind, id]);
24
- if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
25
- if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
26
- 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." />;
27
- return (
28
- <ul className="divide-y rounded-md border" aria-label="Deals">
29
- {q.data.map((d) => (
30
- <li key={d.deal_id}>
31
- <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">
32
- <span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
33
- <span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
34
- </Link>
35
- </li>
36
- ))}
37
- </ul>
38
- );
39
- }
7
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
8
+ import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
9
+ import { RecordConversationsTab } from "@/components/g8/inbox/record-conversations-tab";
10
+ import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
11
+ import { useRouter } from "next/navigation";
40
12
 
41
13
  export default function Page() {
14
+ const router = useRouter();
42
15
  return (
43
16
  <ContactsScreen
44
- extraDetailTabs={[{ id: "deals", label: "Deals", render: (c) => <LinkedDeals kind="contacts" id={c.id} /> }]}
17
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
18
+ extraDetailTabs={[
19
+ { id: "meetings", label: "Meetings", render: (c) => <RecordMeetingsTab contactId={c.id} onOpenMeeting={(sel) => router.push(`/meetings?sel=${encodeURIComponent(sel)}`)} /> },
20
+ { id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="contacts" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
21
+ { id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="contacts" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
22
+ { id: "conversations", label: "Conversations", render: (c) => <RecordConversationsTab email={c.work_email} alsoEmails={[c.personal_emails]} onOpenThread={(sel) => router.push(`/inbox?id=${encodeURIComponent(sel)}`)} /> },
23
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
24
+ ]}
45
25
  />
46
26
  );
47
27
  }
@@ -4,8 +4,17 @@
4
4
  // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
5
  import { useRouter } from "next/navigation";
6
6
  import { DealsScreen } from "@/components/g8/deals/deals-screen";
7
+ import { CreateQuoteFromDeal } from "@/components/g8/quotes/create-quote-from-deal";
7
8
 
9
+ /** A deal opens its quotes, its contacts and (from a contact) their company. */
8
10
  export default function Page() {
9
11
  const router = useRouter();
10
- return <DealsScreen onOpenQuote={(quoteId) => router.push(`/quotes?id=${encodeURIComponent(quoteId)}`)} />;
12
+ return (
13
+ <DealsScreen
14
+ onOpenQuote={(quoteId) => router.push(`/quotes?id=${encodeURIComponent(quoteId)}`)}
15
+ renderQuoteActions={(deal) => <CreateQuoteFromDeal dealId={deal.id} />}
16
+ onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
17
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
18
+ />
19
+ );
11
20
  }
@@ -10,6 +10,7 @@ import { ApiError } from "@/components/g8/_foundation/api-error";
10
10
  import { EmptyState } from "@/components/g8/_foundation/empty-state";
11
11
  import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
12
12
  import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
13
+ import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
13
14
  import { DealDetail } from "@/components/g8/deals/deal-detail";
14
15
  import { useDefaultOwner } from "@/components/g8/deals/hooks/use-default-owner";
15
16
  import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
@@ -88,5 +89,15 @@ function CreateDeal({ thread }: { thread: InboxThread }) {
88
89
  }
89
90
 
90
91
  export default function Page() {
91
- return <InboxScreen renderDetailActions={(thread) => <CreateDeal thread={thread} />} />;
92
+ const router = useRouter();
93
+ return (
94
+ <InboxScreen
95
+ renderDetailActions={(thread) => (
96
+ <>
97
+ <ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
98
+ <CreateDeal thread={thread} />
99
+ </>
100
+ )}
101
+ />
102
+ );
92
103
  }
@@ -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 { MeetingContactLinks } from "@/components/g8/appointments/meeting-contact-links";
7
+ import { MeetingsScreen } from "@/components/g8/appointments/meetings-screen";
8
+
9
+ /** A meeting opens the attendees who are contacts in your CRM. */
10
+ export default function Page() {
11
+ const router = useRouter();
12
+ return <MeetingsScreen renderDetailActions={(selection) => <MeetingContactLinks selection={selection} onOpenContact={(id) => router.push(`/contacts?id=${id}`)} />} />;
13
+ }
@@ -0,0 +1,20 @@
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
+ if (record.entity_type === "deal") router.push(`/deals?id=${encodeURIComponent(record.entity_id)}`);
17
+ }}
18
+ />
19
+ );
20
+ }
@@ -82,7 +82,8 @@
82
82
  "component": "MeetingsScreen",
83
83
  "icon": "CalendarCheck",
84
84
  "group": "Engage",
85
- "description": "Booked, held and missed meetings"
85
+ "description": "Booked, held and missed meetings",
86
+ "page": "pages/meetings.tsx.tmpl"
86
87
  },
87
88
  {
88
89
  "label": "Scheduling",
@@ -100,7 +101,8 @@
100
101
  "component": "TasksScreen",
101
102
  "icon": "CheckSquare",
102
103
  "group": "Work",
103
- "description": "Follow-ups due across your records"
104
+ "description": "Follow-ups due across your records",
105
+ "page": "pages/tasks.tsx.tmpl"
104
106
  },
105
107
  {
106
108
  "label": "Mailboxes",
@@ -2,46 +2,25 @@
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";
13
- import { dataOf, formatMoney } from "@/components/g8/deals/lib/deals-types";
14
-
15
- /** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
16
- interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
17
-
18
- /** The deals linked to one contact or company, each opening in the Deals page. */
19
- function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
20
- const q = useG8Query(async (signal) => {
21
- const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
22
- return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
23
- }, [kind, id]);
24
- if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
25
- if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
26
- 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." />;
27
- return (
28
- <ul className="divide-y rounded-md border" aria-label="Deals">
29
- {q.data.map((d) => (
30
- <li key={d.deal_id}>
31
- <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">
32
- <span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
33
- <span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
34
- </Link>
35
- </li>
36
- ))}
37
- </ul>
38
- );
39
- }
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
7
+ import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
8
+ import { AddCompanyPeopleToListButton } from "@/components/g8/lists/add-company-people-to-list-button";
9
+ import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
10
+ import { useRouter } from "next/navigation";
40
11
 
41
12
  export default function Page() {
13
+ const router = useRouter();
42
14
  return (
43
15
  <CompaniesScreen
44
- extraDetailTabs={[{ id: "deals", label: "Deals", render: (c) => <LinkedDeals kind="companies" id={c.id} /> }]}
16
+ onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
17
+ renderBulkActions={({ ids, clear }) => <AddCompanyPeopleToListButton companyIds={ids} onAdded={clear} />}
18
+ renderDetailActions={(c) => <AddCompanyPeopleToListButton companyIds={[c.id]} />}
19
+ extraDetailTabs={[
20
+ { id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="companies" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
21
+ { id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="companies" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
22
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
23
+ ]}
45
24
  />
46
25
  );
47
26
  }
@@ -2,46 +2,31 @@
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 { ContactsScreen } from "@/components/g8/crm/contacts-screen";
13
- import { dataOf, formatMoney } from "@/components/g8/deals/lib/deals-types";
14
-
15
- /** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
16
- interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
17
-
18
- /** The deals linked to one contact or company, each opening in the Deals page. */
19
- function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
20
- const q = useG8Query(async (signal) => {
21
- const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
22
- return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
23
- }, [kind, id]);
24
- if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
25
- if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
26
- 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." />;
27
- return (
28
- <ul className="divide-y rounded-md border" aria-label="Deals">
29
- {q.data.map((d) => (
30
- <li key={d.deal_id}>
31
- <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">
32
- <span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
33
- <span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
34
- </Link>
35
- </li>
36
- ))}
37
- </ul>
38
- );
39
- }
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
7
+ import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
8
+ import { AddToListButton } from "@/components/g8/lists/add-to-list-button";
9
+ import { RecordListsTab } from "@/components/g8/lists/record-lists-tab";
10
+ import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
11
+ import { useRouter } from "next/navigation";
40
12
 
41
13
  export default function Page() {
14
+ const router = useRouter();
42
15
  return (
43
16
  <ContactsScreen
44
- extraDetailTabs={[{ id: "deals", label: "Deals", render: (c) => <LinkedDeals kind="contacts" id={c.id} /> }]}
17
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
18
+ renderBulkActions={({ ids, clear }) => <AddToListButton contactIds={ids} onAdded={clear} />}
19
+ renderDetailActions={(c) => (
20
+ <>
21
+ <AddToListButton contactIds={[c.id]} />
22
+ </>
23
+ )}
24
+ extraDetailTabs={[
25
+ { id: "lists", label: "Lists", render: (c) => <RecordListsTab contactId={c.id} onOpenList={(id) => router.push(`/lists?id=${id}`)} /> },
26
+ { id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="contacts" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
27
+ { id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="contacts" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
28
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
29
+ ]}
45
30
  />
46
31
  );
47
32
  }
@@ -4,8 +4,17 @@
4
4
  // other blocks of this app plugged into its slots. Edit freely: this file is yours.
5
5
  import { useRouter } from "next/navigation";
6
6
  import { DealsScreen } from "@/components/g8/deals/deals-screen";
7
+ import { CreateQuoteFromDeal } from "@/components/g8/quotes/create-quote-from-deal";
7
8
 
9
+ /** A deal opens its quotes, its contacts and (from a contact) their company. */
8
10
  export default function Page() {
9
11
  const router = useRouter();
10
- return <DealsScreen onOpenQuote={(quoteId) => router.push(`/quotes?id=${encodeURIComponent(quoteId)}`)} />;
12
+ return (
13
+ <DealsScreen
14
+ onOpenQuote={(quoteId) => router.push(`/quotes?id=${encodeURIComponent(quoteId)}`)}
15
+ renderQuoteActions={(deal) => <CreateQuoteFromDeal dealId={deal.id} />}
16
+ onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
17
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
18
+ />
19
+ );
11
20
  }
@@ -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 { Building2, UserRound } from "lucide-react";
8
+ import { LifecycleScreen } from "@/components/g8/lifecycle/lifecycle-screen";
9
+
10
+ /** A lifecycle record opens its company or contact record. */
11
+ export default function Page() {
12
+ const router = useRouter();
13
+ return (
14
+ <LifecycleScreen
15
+ renderDetailActions={(record) => (
16
+ <Button size="sm" variant="outline" onClick={() => router.push(`/${record.kind === "company" ? "companies" : "contacts"}?id=${record.id}`)}>
17
+ {record.kind === "company" ? <Building2 className="mr-1 size-4" aria-hidden /> : <UserRound className="mr-1 size-4" aria-hidden />}
18
+ {record.kind === "company" ? "Open company" : "Open contact"}
19
+ </Button>
20
+ )}
21
+ />
22
+ );
23
+ }
@@ -0,0 +1,12 @@
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 { ListsScreen } from "@/components/g8/lists/lists-screen";
7
+
8
+ /** A list member opens in Contacts. */
9
+ export default function Page() {
10
+ const router = useRouter();
11
+ return <ListsScreen onOpenContact={(id) => router.push(`/contacts?id=${id}`)} />;
12
+ }
@@ -0,0 +1,20 @@
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
+ if (record.entity_type === "deal") router.push(`/deals?id=${encodeURIComponent(record.entity_id)}`);
17
+ }}
18
+ />
19
+ );
20
+ }
@@ -6,6 +6,7 @@
6
6
  "status": "available",
7
7
  "blocks": [
8
8
  "foundation",
9
+ "pickers",
9
10
  "crm",
10
11
  "objects",
11
12
  "lists",
@@ -55,7 +56,8 @@
55
56
  "component": "ListsScreen",
56
57
  "icon": "ListChecks",
57
58
  "group": "Prospect",
58
- "description": "Saved audiences you can sequence or sync"
59
+ "description": "Saved audiences you can sequence or sync",
60
+ "page": "pages/lists.tsx.tmpl"
59
61
  },
60
62
  {
61
63
  "label": "Deals",
@@ -84,7 +86,8 @@
84
86
  "component": "LifecycleScreen",
85
87
  "icon": "GitBranch",
86
88
  "group": "Pipeline",
87
- "description": "Move records through lifecycle stages"
89
+ "description": "Move records through lifecycle stages",
90
+ "page": "pages/lifecycle.tsx.tmpl"
88
91
  },
89
92
  {
90
93
  "label": "Tasks",
@@ -93,7 +96,8 @@
93
96
  "component": "TasksScreen",
94
97
  "icon": "CheckSquare",
95
98
  "group": "Work",
96
- "description": "Follow-ups due across your records"
99
+ "description": "Follow-ups due across your records",
100
+ "page": "pages/tasks.tsx.tmpl"
97
101
  },
98
102
  {
99
103
  "label": "Workflows",
@@ -3,13 +3,29 @@
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
5
  import { CompaniesScreen } from "@/components/g8/crm/companies-screen";
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
6
7
  import { EnrichButton } from "@/components/g8/enrichment/enrich-button";
8
+ import { AddCompanyPeopleToListButton } from "@/components/g8/lists/add-company-people-to-list-button";
9
+ import { CompanyIntelligenceTab } from "@/components/g8/company-intelligence/company-intelligence-tab";
10
+ import { useRouter } from "next/navigation";
7
11
 
8
12
  export default function Page() {
13
+ const router = useRouter();
9
14
  return (
10
15
  <CompaniesScreen
16
+ onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
11
17
  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" />}
12
- renderDetailActions={(c, { reload }) => <EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />}
18
+ renderBulkActions={({ ids, clear }) => <AddCompanyPeopleToListButton companyIds={ids} onAdded={clear} />}
19
+ renderDetailActions={(c, { reload }) => (
20
+ <>
21
+ <EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />
22
+ <AddCompanyPeopleToListButton companyIds={[c.id]} />
23
+ </>
24
+ )}
25
+ extraDetailTabs={[
26
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
27
+ { id: "intelligence", label: "Intelligence", render: (c) => <CompanyIntelligenceTab company={c} onOpenIntelligence={() => router.push("/company-intelligence")} /> },
28
+ ]}
13
29
  />
14
30
  );
15
31
  }
@@ -3,17 +3,29 @@
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
5
  import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
6
+ import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
6
7
  import { EnrichButton } from "@/components/g8/enrichment/enrich-button";
8
+ import { AddToListButton } from "@/components/g8/lists/add-to-list-button";
9
+ import { RecordListsTab } from "@/components/g8/lists/record-lists-tab";
10
+ import { useRouter } from "next/navigation";
7
11
 
8
12
  export default function Page() {
13
+ const router = useRouter();
9
14
  return (
10
15
  <ContactsScreen
16
+ onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
11
17
  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" />}
18
+ renderBulkActions={({ ids, clear }) => <AddToListButton contactIds={ids} onAdded={clear} />}
12
19
  renderDetailActions={(c, { reload }) => (
13
20
  <>
14
21
  <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" />
22
+ <AddToListButton contactIds={[c.id]} />
15
23
  </>
16
24
  )}
25
+ extraDetailTabs={[
26
+ { id: "lists", label: "Lists", render: (c) => <RecordListsTab contactId={c.id} onOpenList={(id) => router.push(`/lists?id=${id}`)} /> },
27
+ { id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
28
+ ]}
17
29
  />
18
30
  );
19
31
  }
@@ -0,0 +1,12 @@
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 { ListsScreen } from "@/components/g8/lists/lists-screen";
7
+
8
+ /** A list member opens in Contacts. */
9
+ export default function Page() {
10
+ const router = useRouter();
11
+ return <ListsScreen onOpenContact={(id) => router.push(`/contacts?id=${id}`)} />;
12
+ }
@@ -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
+ }
@@ -6,12 +6,14 @@
6
6
  "status": "available",
7
7
  "blocks": [
8
8
  "foundation",
9
+ "pickers",
9
10
  "crm",
10
11
  "lists",
11
12
  "search",
12
13
  "enrichment",
13
14
  "intent",
14
- "visitors"
15
+ "visitors",
16
+ "company-intelligence"
15
17
  ],
16
18
  "home": "/prospect-search",
17
19
  "nav": [
@@ -44,6 +46,15 @@
44
46
  "description": "Accounts and the people at them",
45
47
  "page": "pages/companies.tsx.tmpl"
46
48
  },
49
+ {
50
+ "label": "Company intelligence",
51
+ "path": "/company-intelligence",
52
+ "block": "company-intelligence",
53
+ "component": "CompanyIntelligenceScreen",
54
+ "icon": "Telescope",
55
+ "group": "Setup",
56
+ "description": "Research your website: profile, competitors, news, documents and reports"
57
+ },
47
58
  {
48
59
  "label": "Tasks",
49
60
  "path": "/tasks",
@@ -51,7 +62,8 @@
51
62
  "component": "TasksScreen",
52
63
  "icon": "CheckSquare",
53
64
  "group": "Work",
54
- "description": "Follow-ups due across your records"
65
+ "description": "Follow-ups due across your records",
66
+ "page": "pages/tasks.tsx.tmpl"
55
67
  },
56
68
  {
57
69
  "label": "Lists",
@@ -60,7 +72,8 @@
60
72
  "component": "ListsScreen",
61
73
  "icon": "ListChecks",
62
74
  "group": "Prospect",
63
- "description": "Saved audiences you can sequence or sync"
75
+ "description": "Saved audiences you can sequence or sync",
76
+ "page": "pages/lists.tsx.tmpl"
64
77
  },
65
78
  {
66
79
  "label": "Enrichment",