@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
@@ -0,0 +1,126 @@
1
+ "use client";
2
+
3
+ /**
4
+ * RecordSequencesTab: every sequence one contact is or was enrolled in, with its state and step, "Add to sequence"
5
+ * (the sending preflight and confirmation live in the enroll dialog) and a confirmed "Withdraw" per active enrollment.
6
+ * Use it as an `extraDetailTabs` entry of ContactsScreen:
7
+ * { id: "sequences", label: "Sequences", render: (c) => <RecordSequencesTab contactId={c.id} onOpenSequence={(id) => router.push(`/sequences?id=${id}`)} /> }
8
+ *
9
+ * SDK operations, in order:
10
+ * 1. getContactSequences GET /contacts/{id}/sequences (read; the API answers the 50 most recent enrollments)
11
+ * 2. AddToSequenceButton: listSequences, getSafeToSend, getSequence, addContactsToSequence (external: confirmed in the dialog)
12
+ * 3. withdrawContactsFromSequences POST /contacts/withdraw-from-sequences (write; stops the contact's steps, confirmed here)
13
+ * Withdraw is contract-tier `write`, but it stops a contact's outreach, so it is confirmed with the destructive dialog anyway.
14
+ * Withdraw names ONE sequence; it never uses the API's remove-from-all switch.
15
+ */
16
+
17
+ import { useState } from "react";
18
+ import { Button, Skeleton } from "@graph8/sdk/ui";
19
+ import { Workflow } from "lucide-react";
20
+ import { ApiError } from "@/g8/components/api-error";
21
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
22
+ import { EmptyState } from "@/g8/components/empty-state";
23
+ import { StatusPill } from "@/g8/components/screen/status-pill";
24
+ import type { Tone } from "@/g8/components/screen/status-pill";
25
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
26
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
27
+ import { g8Get, g8Post } from "@/g8/lib/g8-client";
28
+ import { AddToSequenceButton } from "./add-to-sequence-button";
29
+ import { listOf } from "../lib/sequencer-utils";
30
+
31
+ /** One row of GET /contacts/{id}/sequences. */
32
+ interface Enrollment { id?: number | null; sequence_id?: string | null; sequence_name?: string | null; sequence_status?: string | null; state?: string | null; current_step_order?: number | null; created_at?: string | null }
33
+
34
+ /** The server returns at most this many enrollments, newest first. */
35
+ export const SEQUENCES_TAB_API_LIMIT = 50;
36
+
37
+ const TONE: Record<string, Tone> = { waiting: "info", paused: "warning", completed: "positive", replied: "positive", bounced: "negative", skipped: "neutral" };
38
+ /** Enrollments that are already over cannot be withdrawn. */
39
+ const OVER = new Set(["completed", "replied", "bounced", "skipped", "withdrawn", "removed"]);
40
+
41
+ function when(iso: string | null | undefined): string {
42
+ const d = iso ? new Date(iso) : null;
43
+ return d && !Number.isNaN(d.getTime()) ? d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }) : "";
44
+ }
45
+
46
+ export interface RecordSequencesTabProps {
47
+ contactId: number;
48
+ /** Route to the sequence in your sequences screen. Without it the rows link to `${sequencesPath}?id=<sequence id>`. */
49
+ onOpenSequence?: (sequenceId: string) => void;
50
+ /** Path of your sequences screen for the plain links. Default "/sequences". */
51
+ sequencesPath?: string;
52
+ }
53
+
54
+ export function RecordSequencesTab({ contactId, onOpenSequence, sequencesPath = "/sequences" }: RecordSequencesTabProps) {
55
+ const [withdrawing, setWithdrawing] = useState<Enrollment | null>(null);
56
+ const q = useG8Query(async (signal) => listOf<Enrollment>(await g8Get(`/contacts/${contactId}/sequences`, undefined, { signal })), [contactId]);
57
+ const withdraw = useG8Mutation(async (e: Enrollment) => {
58
+ await g8Post("/contacts/withdraw-from-sequences", { contact_ids: [contactId], sequence_id: e.sequence_id });
59
+ setWithdrawing(null);
60
+ await q.refetch();
61
+ }, { invalidates: [["sequences"]], onError: () => setWithdrawing(null) });
62
+
63
+ if (q.error) return <ApiError error={q.error} title="Could not load sequences" onRetry={() => void q.refetch()} />;
64
+ if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading sequences" />;
65
+ const rows = q.data;
66
+ return (
67
+ <div className="space-y-3">
68
+ <div className="flex items-center justify-between gap-2">
69
+ <p className="text-sm text-muted-foreground">{rows.length === 1 ? "1 enrollment" : `${rows.length.toLocaleString()} enrollments`}</p>
70
+ <AddToSequenceButton contactIds={[contactId]} onEnrolled={() => void q.refetch()} />
71
+ </div>
72
+ {withdraw.error ? <ApiError error={withdraw.error} title="Could not withdraw the contact" /> : null}
73
+ {rows.length === 0 ? (
74
+ <EmptyState compact icon={<Workflow className="size-8" />} title="Not in any sequence" description="Add this contact to a sequence to start its steps." />
75
+ ) : (
76
+ <ul className="divide-y rounded-md border" aria-label="Sequences">
77
+ {rows.map((e, i) => {
78
+ const state = (e.state ?? "").toLowerCase();
79
+ return (
80
+ <li key={e.id ?? `${e.sequence_id}-${i}`} className="flex items-center justify-between gap-3 p-3 text-sm">
81
+ <div className="min-w-0">
82
+ {e.sequence_id ? (
83
+ <a
84
+ href={`${sequencesPath}?id=${encodeURIComponent(e.sequence_id)}`}
85
+ className="block truncate font-medium hover:underline"
86
+ onClick={(ev) => {
87
+ if (!onOpenSequence || ev.defaultPrevented || ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
88
+ ev.preventDefault();
89
+ onOpenSequence(e.sequence_id as string);
90
+ }}
91
+ >
92
+ {e.sequence_name || "Untitled sequence"}
93
+ </a>
94
+ ) : <span className="font-medium">{e.sequence_name || "Untitled sequence"}</span>}
95
+ <p className="text-xs text-muted-foreground">
96
+ {[e.current_step_order != null ? `Step ${e.current_step_order}` : null, e.created_at ? `Enrolled ${when(e.created_at)}` : null, e.sequence_status ? `Sequence ${e.sequence_status.toLowerCase()}` : null].filter(Boolean).join(" · ")}
97
+ </p>
98
+ </div>
99
+ <div className="flex shrink-0 items-center gap-2">
100
+ <StatusPill tone={TONE[state] ?? "neutral"} dot>{state || "unknown"}</StatusPill>
101
+ {e.sequence_id && !OVER.has(state) ? (
102
+ <Button size="sm" variant="ghost" loading={withdraw.loading && withdrawing?.id === e.id} onClick={() => setWithdrawing(e)}>Withdraw</Button>
103
+ ) : null}
104
+ </div>
105
+ </li>
106
+ );
107
+ })}
108
+ </ul>
109
+ )}
110
+ {rows.length >= SEQUENCES_TAB_API_LIMIT ? (
111
+ <p role="note" className="text-xs text-muted-foreground">Showing the {SEQUENCES_TAB_API_LIMIT} most recent enrollments. Older ones are in the sequence itself.</p>
112
+ ) : null}
113
+ <ConfirmSpendDialog
114
+ open={withdrawing !== null}
115
+ tier="destructive"
116
+ title={`Withdraw from ${withdrawing?.sequence_name || "this sequence"}`}
117
+ description="The contact stops receiving this sequence's steps. Their other sequences are not changed."
118
+ affected={1}
119
+ confirmLabel="Withdraw"
120
+ confirming={withdraw.loading}
121
+ onConfirm={() => withdrawing && void withdraw.mutate(withdrawing)}
122
+ onCancel={() => setWithdrawing(null)}
123
+ />
124
+ </div>
125
+ );
126
+ }
@@ -34,6 +34,8 @@ import { g8Delete, g8Get, g8Patch, g8Post } from "@/g8/lib/g8-client";
34
34
  import { useG8Query } from "@/g8/hooks/use-g8-query";
35
35
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
36
36
  import { ApiError } from "@/g8/components/api-error";
37
+ import { ListSelect } from "@/g8/crm/components/list-select";
38
+ import { FirstNotice } from "@/g8/components/paging-notices";
37
39
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
38
40
  import type { ConfirmTier } from "@/g8/components/confirm-spend-dialog";
39
41
  import { StepEditor } from "./step-editor";
@@ -102,11 +104,8 @@ const PHONE_KEYS = ["phone_numbers", "active_phone_numbers", "drafted_phone_numb
102
104
  const LINKEDIN_KEYS = ["linkedin_accounts", "active_linkedin_accounts", "drafted_linkedin_accounts"];
103
105
  const phoneFits = (p: PhoneOption, type: "ai" | "dialer") => !p.inbound_type || p.inbound_type === type;
104
106
  const linkedinLabel = (a: LinkedinOption) => a.name || a.display_name || a.public_identifier || a.email || `Account ${a.id}`;
105
- interface ListOption {
106
- id: number;
107
- title: string;
108
- contact_count?: number | null;
109
- }
107
+ /** The mailboxes API has no paging: a full read is announced under the sender dropdown. */
108
+ const MAILBOX_LIMIT = 200;
110
109
  type Pending = { kind: "create" } | { kind: "update" | "delete"; step: DraftStep };
111
110
 
112
111
  function describe(p: Pending, live: boolean): { tier: ConfirmTier; title: string; description: string; label: string } {
@@ -157,8 +156,7 @@ export function SequenceBuilder({ sequenceId, defaultListId, ownerEmail, onSaved
157
156
  // Senders already attached in edit mode by an earlier (failed) append: never attach them twice.
158
157
  const attached = useRef<Set<string>>(new Set());
159
158
 
160
- const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: 200 }, { signal }), [], { enabled: !editing });
161
- const lists = useG8Query((signal) => g8Get("/lists", { limit: 100 }, { signal }), [], { enabled: !editing });
159
+ const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: MAILBOX_LIMIT }, { signal }), [], { enabled: !editing });
162
160
  const existing = useG8Query(
163
161
  async (signal) => {
164
162
  const [seq, list] = await Promise.all([
@@ -323,15 +321,17 @@ export function SequenceBuilder({ sequenceId, defaultListId, ownerEmail, onSaved
323
321
  );
324
322
  })}
325
323
  </select>
326
- <select aria-label="Audience list" className={select} value={listId} onChange={(e) => setListId(e.target.value)}>
327
- <option value="">Audience list (required)</option>
328
- {listOf<ListOption>(lists.data).map((l) => (
329
- <option key={l.id} value={l.id}>
330
- {l.title}
331
- {l.contact_count != null ? ` (${l.contact_count})` : ""}
332
- </option>
333
- ))}
334
- </select>
324
+ <ListSelect
325
+ label="Audience list"
326
+ emptyOption="Audience list (required)"
327
+ optionLabel={(l) => `${l.title}${l.contact_count != null ? ` (${l.contact_count})` : ""}`}
328
+ pageSize={100}
329
+ value={listId}
330
+ valueLabel={defaultListId ? `List ${defaultListId}` : undefined}
331
+ selectClassName={select}
332
+ onChange={(l) => setListId(l ? String(l.id) : "")}
333
+ />
334
+ <FirstNotice className="sm:col-span-3" shown={listOf<MailboxOption>(mailboxes.data).length} limit={MAILBOX_LIMIT} noun="mailboxes" />
335
335
  </div>
336
336
  ) : null}
337
337
  {need.aiPhone || need.dialerPhone || need.linkedin ? (
@@ -373,12 +373,11 @@ export function SequenceBuilder({ sequenceId, defaultListId, ownerEmail, onSaved
373
373
  {String(objectOf(linkedin.data).message ?? "No LinkedIn account is connected.")} Connect one in the {brand.linkLabel} app.
374
374
  </p>
375
375
  ) : null}
376
- {mailboxes.error || lists.error || phones.error || linkedin.error ? (
376
+ {mailboxes.error || phones.error || linkedin.error ? (
377
377
  <ApiError
378
- error={mailboxes.error ?? lists.error ?? phones.error ?? linkedin.error}
378
+ error={mailboxes.error ?? phones.error ?? linkedin.error}
379
379
  onRetry={() => {
380
380
  void mailboxes.refetch();
381
- void lists.refetch();
382
381
  if (need.aiPhone || need.dialerPhone) void phones.refetch();
383
382
  if (need.linkedin) void linkedin.refetch();
384
383
  }}
@@ -449,7 +448,7 @@ export function SequenceBuilder({ sequenceId, defaultListId, ownerEmail, onSaved
449
448
  {run.error ? <ApiError error={run.error} onRetry={run.reset} /> : null}
450
449
  {append.error ? <ApiError error={append.error} onRetry={append.reset} /> : null}
451
450
  {saved ? (
452
- <p role="status" className="text-sm text-green-700">
451
+ <p role="status" className="text-sm text-positive-foreground">
453
452
  {saved}
454
453
  </p>
455
454
  ) : null}
@@ -143,6 +143,8 @@ export interface SequenceDetailProps {
143
143
  refreshKey?: number;
144
144
  /** Open another sequence (the duplicate). */
145
145
  onOpenSequence?: (sequenceId: string) => void;
146
+ /** Open an enrolled person in your contacts screen. */
147
+ onOpenContact?: (contactId: number) => void;
146
148
  renderActions?: (sequence: SequenceRow) => ReactNode;
147
149
  extraTabs?: { id: string; label: string; render: (sequence: SequenceRow) => ReactNode }[];
148
150
  className?: string;
@@ -211,7 +213,7 @@ export function SequenceDetail(props: SequenceDetailProps) {
211
213
  description={
212
214
  <>
213
215
  <p>{COPY[p.action].description}</p>
214
- {p.warnings.length > 0 ? <ul className="mt-2 list-disc pl-5 text-amber-800">{p.warnings.map((w) => <li key={w}>{w}</li>)}</ul> : null}
216
+ {p.warnings.length > 0 ? <ul className="mt-2 list-disc pl-5 text-warning-foreground">{p.warnings.map((w) => <li key={w}>{w}</li>)}</ul> : null}
215
217
  </>
216
218
  }
217
219
  recipients={p.action === "archive" ? undefined : (row.contact_count ?? undefined)}
@@ -250,7 +252,7 @@ export function SequenceDetail(props: SequenceDetailProps) {
250
252
  ),
251
253
  },
252
254
  { id: "steps", label: "Steps", count: row.step_count ?? undefined, content: <SequenceStepsPanel sequenceId={id} stepId={props.stepId} onStepChange={props.onStepChange} onChanged={changed} /> },
253
- { id: "contacts", label: "Contacts", count: row.contact_count ?? undefined, content: <SequenceEnrollments key={statsKey} sequenceId={id} contactId={props.contactId} onContactChange={props.onContactChange} /> },
255
+ { id: "contacts", label: "Contacts", count: row.contact_count ?? undefined, content: <SequenceEnrollments key={statsKey} sequenceId={id} contactId={props.contactId} onContactChange={props.onContactChange} onOpenContact={props.onOpenContact} /> },
254
256
  { id: "results", label: "Results", content: <SequenceStats key={`${id}:${statsKey}`} sequenceId={id} refreshSeconds={group === "live" ? 60 : undefined} /> },
255
257
  ...(props.extraTabs ?? []).map((t) => ({ id: t.id, label: t.label, content: t.render(row) })),
256
258
  ]}
@@ -31,6 +31,8 @@ export interface SequenceEnrollmentsProps {
31
31
  /** The open enrollment, by contact id. Controlled so a screen can deep-link it. */
32
32
  contactId?: string;
33
33
  onContactChange?: (contactId: string | null) => void;
34
+ /** Open the enrolled person in your contacts screen (adds an "Open in contacts" button). */
35
+ onOpenContact?: (contactId: number) => void;
34
36
  pageSize?: number;
35
37
  className?: string;
36
38
  }
@@ -41,7 +43,7 @@ const STATES = ["", "waiting", "paused", "completed", "replied", "bounced", "ski
41
43
  const TONE: Record<string, Tone> = { waiting: "info", paused: "warning", completed: "positive", replied: "positive", bounced: "negative", skipped: "neutral" };
42
44
  const nameOf = (e: Enrollment) => e.name || e.email || (e.contact_id != null ? `Contact ${e.contact_id}` : "Contact");
43
45
 
44
- export function SequenceEnrollments({ sequenceId, contactId, onContactChange, pageSize = 25, className }: SequenceEnrollmentsProps) {
46
+ export function SequenceEnrollments({ sequenceId, contactId, onContactChange, onOpenContact, pageSize = 25, className }: SequenceEnrollmentsProps) {
45
47
  const [state, setState] = useState("");
46
48
  const [page, setPage] = useState(1);
47
49
  const [resume, setResume] = useState<Enrollment | null>(null);
@@ -89,7 +91,7 @@ export function SequenceEnrollments({ sequenceId, contactId, onContactChange, pa
89
91
  <Button size="sm" variant="outline" disabled={rows.length < pageSize} onClick={() => setPage((p) => p + 1)}>Next</Button>
90
92
  </div>
91
93
  {open ? (
92
- <SectionCard title={`${nameOf(open)} in this sequence`} actions={<>{actions(open)}<Button size="sm" variant="ghost" onClick={() => onContactChange?.(null)}>Close</Button></>}>
94
+ <SectionCard title={`${nameOf(open)} in this sequence`} actions={<>{actions(open)}{onOpenContact && open.contact_id != null ? <Button size="sm" variant="outline" onClick={() => onOpenContact(open.contact_id as number)}>Open in contacts</Button> : null}<Button size="sm" variant="ghost" onClick={() => onContactChange?.(null)}>Close</Button></>}>
93
95
  <KeyValueList columns={2} items={fieldItems(open as Record<string, unknown>)} />
94
96
  {open.contact_id != null ? (
95
97
  <SectionCard title="Contact" description="The full contact record.">
@@ -204,7 +204,7 @@ export function SequenceList({ status, pageSize = 25, onOpen, onCreate, onChange
204
204
  <>
205
205
  <p>{COPY[pending.action].description}</p>
206
206
  {pending.warnings.length > 0 ? (
207
- <ul className="mt-2 list-disc pl-5 text-amber-800">
207
+ <ul className="mt-2 list-disc pl-5 text-warning-foreground">
208
208
  {pending.warnings.map((w) => (
209
209
  <li key={w}>{w}</li>
210
210
  ))}
@@ -50,9 +50,9 @@ const STATES = ["", "waiting", "paused", "completed", "replied", "bounced", "ski
50
50
 
51
51
  function Tile({ label, value }: { label: string; value: string | number }) {
52
52
  return (
53
- <div className="rounded border border-gray-200 p-3">
54
- <div className="text-xs capitalize text-gray-500">{label}</div>
55
- <div className="text-xl font-semibold">{value}</div>
53
+ <div className="rounded border border-border p-3">
54
+ <div className="text-xs capitalize text-muted-foreground">{label}</div>
55
+ <div className="text-xl font-medium">{value}</div>
56
56
  </div>
57
57
  );
58
58
  }
@@ -145,7 +145,7 @@ export function SequenceStats({ sequenceId, pageSize = 25, refreshSeconds, class
145
145
  <TableCell>{st.step_order}</TableCell>
146
146
  <TableCell>{st.step_type}</TableCell>
147
147
  <TableCell>{st.total_contacts ?? 0}</TableCell>
148
- <TableCell className="text-xs text-gray-600">
148
+ <TableCell className="text-xs text-muted-foreground">
149
149
  {Object.entries(st.counts ?? {})
150
150
  .filter(([, n]) => n > 0)
151
151
  .map(([k, n]) => `${k}: ${n}`)
@@ -156,11 +156,11 @@ export function SequenceStats({ sequenceId, pageSize = 25, refreshSeconds, class
156
156
  </TableBody>
157
157
  </Table>
158
158
  ) : (
159
- <p className="text-sm text-gray-500">No step activity yet.</p>
159
+ <p className="text-sm text-muted-foreground">No step activity yet.</p>
160
160
  )}
161
161
 
162
162
  {skips.length > 0 ? (
163
- <p className="text-sm text-gray-600" data-testid="skip-reasons">
163
+ <p className="text-sm text-muted-foreground" data-testid="skip-reasons">
164
164
  Skipped contacts, counted once each: {skips.map(([k, n]) => `${k.replace(/^skipped_/, "").replace(/_/g, " ")} ${n}`).join(", ")}
165
165
  </p>
166
166
  ) : null}
@@ -171,7 +171,7 @@ export function SequenceStats({ sequenceId, pageSize = 25, refreshSeconds, class
171
171
  Contacts in
172
172
  <select
173
173
  aria-label="Contact state"
174
- className="ml-2 h-8 rounded border border-gray-300 px-1"
174
+ className="ml-2 h-8 rounded border border-input px-1"
175
175
  value={state}
176
176
  onChange={(e) => {
177
177
  setState(e.target.value);
@@ -201,7 +201,7 @@ export function SequenceStats({ sequenceId, pageSize = 25, refreshSeconds, class
201
201
  <TableBody>
202
202
  {rows.length === 0 ? (
203
203
  <TableRow>
204
- <TableCell colSpan={4} className="text-sm text-gray-500">
204
+ <TableCell colSpan={4} className="text-sm text-muted-foreground">
205
205
  {contacts.loading ? "Loading..." : "No contacts in this state."}
206
206
  </TableCell>
207
207
  </TableRow>
@@ -52,6 +52,8 @@ export interface SequencesScreenProps {
52
52
  renderDetailActions?: (sequence: SequenceRow) => ReactNode;
53
53
  /** Extra tabs in the open sequence's panel. */
54
54
  extraDetailTabs?: { id: string; label: string; render: (sequence: SequenceRow) => ReactNode }[];
55
+ /** Open an enrolled person in your contacts screen; without it the Contacts tab has no such button. */
56
+ onOpenContact?: (contactId: number) => void;
55
57
  className?: string;
56
58
  }
57
59
 
@@ -135,6 +137,7 @@ export function SequencesScreen(props: SequencesScreenProps) {
135
137
  onStepChange={(v) => setStepId(v)}
136
138
  contactId={contactId || undefined}
137
139
  onContactChange={(v) => setContactId(v)}
140
+ onOpenContact={props.onOpenContact}
138
141
  onClose={close}
139
142
  onChanged={refreshList}
140
143
  refreshKey={version}
@@ -52,9 +52,9 @@ function LinkedinReadiness() {
52
52
  const c = conn.data?.data;
53
53
  if (conn.loading || conn.error || !c) return null;
54
54
  if (c.connected && c.is_token_valid !== false && !c.requires_manual_login)
55
- return <p className="text-xs text-green-700">LinkedIn sender connected.</p>;
55
+ return <p className="text-xs text-positive-foreground">LinkedIn sender connected.</p>;
56
56
  return (
57
- <p role="alert" className="text-xs text-amber-800">
57
+ <p role="alert" className="text-xs text-warning-foreground">
58
58
  LinkedIn is not ready to send. Reconnect the LinkedIn account in the {brand.linkLabel} app before launching.
59
59
  </p>
60
60
  );
@@ -72,13 +72,13 @@ export function StepEditor({ step, index, onChange, onRemove, disabled, classNam
72
72
  const phoneOptions = Array.isArray(data.phone_options) ? (data.phone_options as string[]) : [];
73
73
 
74
74
  return (
75
- <fieldset disabled={disabled} className={`space-y-3 rounded border border-gray-200 p-3 ${className ?? ""}`} data-step-kind={step.kind}>
75
+ <fieldset disabled={disabled} className={`space-y-3 rounded border border-border p-3 ${className ?? ""}`} data-step-kind={step.kind}>
76
76
  <div className="flex items-center justify-between">
77
- <legend className="text-sm font-semibold">
77
+ <legend className="text-sm font-medium">
78
78
  Step {index}: {KIND_LABEL[step.kind]}
79
79
  </legend>
80
80
  {onRemove ? (
81
- <button type="button" className="text-xs text-red-700 hover:underline" onClick={onRemove}>
81
+ <button type="button" className="text-xs text-error-foreground hover:underline" onClick={onRemove}>
82
82
  Remove step
83
83
  </button>
84
84
  ) : null}
@@ -105,7 +105,7 @@ export function StepEditor({ step, index, onChange, onRemove, disabled, classNam
105
105
  Copy
106
106
  <select
107
107
  aria-label="Email copy"
108
- className="ml-2 h-8 rounded border border-gray-300 px-1 text-sm"
108
+ className="ml-2 h-8 rounded border border-input px-1 text-sm"
109
109
  value={step.input_type}
110
110
  onChange={(e) => onChange({ ...step, dirty: true, input_type: e.target.value as InputType })}
111
111
  >
@@ -141,7 +141,7 @@ export function StepEditor({ step, index, onChange, onRemove, disabled, classNam
141
141
  <>
142
142
  <select
143
143
  aria-label="LinkedIn action"
144
- className="h-8 rounded border border-gray-300 px-1 text-sm"
144
+ className="h-8 rounded border border-input px-1 text-sm"
145
145
  value={action}
146
146
  onChange={(e) => set({ linkedin_action_type: e.target.value })}
147
147
  >
@@ -160,7 +160,7 @@ export function StepEditor({ step, index, onChange, onRemove, disabled, classNam
160
160
  onChange={(e) => set({ [copyKey]: e.target.value })}
161
161
  />
162
162
  ) : (
163
- <p className="text-xs text-gray-500">This action sends no text.</p>
163
+ <p className="text-xs text-muted-foreground">This action sends no text.</p>
164
164
  )}
165
165
  {action === "INMAIL" ? (
166
166
  <Input
@@ -176,7 +176,7 @@ export function StepEditor({ step, index, onChange, onRemove, disabled, classNam
176
176
 
177
177
  {step.kind === "call" || step.kind === "task" ? (
178
178
  <div className="space-y-1 text-sm">
179
- <p className="text-gray-600">
179
+ <p className="text-muted-foreground">
180
180
  {step.kind === "call" ? "An automatic call attempt." : "Creates a call task for a person to work in the dialer."}
181
181
  </p>
182
182
  <div className="flex flex-wrap gap-3">
@@ -199,12 +199,12 @@ export function StepEditor({ step, index, onChange, onRemove, disabled, classNam
199
199
  ) : null}
200
200
 
201
201
  {broken ? (
202
- <p role="alert" className="text-xs text-amber-800">
202
+ <p role="alert" className="text-xs text-warning-foreground">
203
203
  A {"{{merge token}}"} is not closed. Unclosed tokens are deleted when the email sends.
204
204
  </p>
205
205
  ) : null}
206
206
  {issues.length > 0 ? (
207
- <ul className="text-xs text-red-700" aria-label="Step problems">
207
+ <ul className="text-xs text-error-foreground" aria-label="Step problems">
208
208
  {issues.map((i) => (
209
209
  <li key={i}>{i}</li>
210
210
  ))}
@@ -142,11 +142,11 @@ export function statusGroup(status: string | null | undefined): "draft" | "live"
142
142
  }
143
143
 
144
144
  export const STATUS_CLASS: Record<ReturnType<typeof statusGroup>, string> = {
145
- draft: "bg-gray-100 text-gray-700",
146
- live: "bg-green-100 text-green-800",
147
- paused: "bg-amber-100 text-amber-800",
148
- ended: "bg-slate-100 text-slate-600",
149
- busy: "bg-blue-100 text-blue-800",
145
+ draft: "bg-muted text-foreground",
146
+ live: "bg-positive text-positive-foreground",
147
+ paused: "bg-warning text-warning-foreground",
148
+ ended: "bg-muted text-muted-foreground",
149
+ busy: "bg-info text-info-foreground",
150
150
  };
151
151
 
152
152
  export function listOf<T>(envelope: unknown): T[] {
@@ -137,3 +137,11 @@ Ask the person: campaign daily budget, dates and destination URL (always typed b
137
137
  | updateAdCreative | PATCH /ad-creatives/{creative_id} | write | CreativeGallery |
138
138
  | updateAudienceSync | PATCH /audience-syncs/{config_id} | external | AudienceSync |
139
139
  | updateSocialPost | PATCH /social/posts/{post_id} | write | PostComposer |
140
+
141
+ ## Depth added (C7c)
142
+
143
+ - `CampaignDetail` gains **Ads** (`CampaignAds`: list, edit, pause or activate, archive, add; all external or destructive confirms) and **Edit** (`CampaignEditor`: name, daily budget, end date, spend cap, pause or resume; previewed, then an external confirm; fields the platform refuses are disabled with its reason).
144
+ - `CreativeDetail` gains **AI tools** (`CreativeTools`: edit by instruction, rewrite a field with undo, review and apply, other sizes, retry, move to draft, save as template; billable passes are confirmed) and **Revisions** (`CreativeRevisions`). `CreativeGallery` loads past 50 and has a **Concept board** (`CreativeConcepts`, billable confirm).
145
+ - `SocialScreen` gains **Ideas** (`SocialIdeas`, Draft a post opens the composer with the brief), **Performance** (`SocialPerformance`), a per-post Performance tab, and Disconnect on an account (destructive confirm).
146
+
147
+ Not covered (and why): `createAdCreativeSet` and `readAdCreativeThroughPersona` need persona pickers; video clips, image-point edits, reskin, set-image and library ops need pickers or media editing; `uploadAdImage` is multipart; social autopilot (`saveSocialAutopilot`) auto-publishes and engagers (`addSocialEngagersToAudience`, `enrollSocialEngagers`) need list and sequence pickers; `getAudienceSync` is already covered by the list read.