@graph8/sdk 0.370.0 → 0.374.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (646) hide show
  1. package/README.md +22 -3
  2. package/blocks/_foundation/README.md +61 -0
  3. package/blocks/_foundation/block.json +57 -0
  4. package/blocks/_foundation/components/auth-gate.tsx +363 -0
  5. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  6. package/blocks/_foundation/components/empty-state.tsx +5 -2
  7. package/blocks/_foundation/components/list-select.tsx +112 -0
  8. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  9. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  10. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  11. package/blocks/_foundation/components/screen/index.ts +1 -0
  12. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  13. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  14. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  15. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  16. package/blocks/_foundation/lib/paging.ts +57 -0
  17. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  18. package/blocks/_foundation/server/auth.ts +815 -0
  19. package/blocks/agents/README.md +81 -4
  20. package/blocks/agents/block.json +311 -2
  21. package/blocks/agents/components/agent-activity.tsx +112 -0
  22. package/blocks/agents/components/agent-approvals.tsx +135 -0
  23. package/blocks/agents/components/agent-builder.tsx +17 -3
  24. package/blocks/agents/components/agent-conversations.tsx +143 -0
  25. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
  26. package/blocks/agents/components/agent-operators.tsx +164 -0
  27. package/blocks/agents/components/agent-run-detail.tsx +20 -3
  28. package/blocks/agents/components/agent-runs.tsx +3 -3
  29. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  30. package/blocks/agents/components/agent-test-history.tsx +121 -0
  31. package/blocks/agents/components/agents-screen.tsx +53 -11
  32. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  33. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  34. package/blocks/agents/components/from-template-button.tsx +65 -0
  35. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  36. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  37. package/blocks/agents/components/knowledge-form.tsx +69 -0
  38. package/blocks/agents/components/name-dialog.tsx +32 -0
  39. package/blocks/agents/components/receptionist-button.tsx +46 -0
  40. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  41. package/blocks/agents/components/skill-editor.tsx +147 -0
  42. package/blocks/agents/components/skill-library.tsx +111 -0
  43. package/blocks/agents/lib/agent-types.ts +7 -1
  44. package/blocks/agents/lib/facts.tsx +13 -0
  45. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  46. package/blocks/agents/lib/load-more.tsx +141 -0
  47. package/blocks/agents/lib/skill-utils.ts +91 -0
  48. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  49. package/blocks/analytics/components/analytics-screen.tsx +16 -3
  50. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  51. package/blocks/analytics/components/report-builder.tsx +15 -2
  52. package/blocks/appointments/README.md +6 -0
  53. package/blocks/appointments/block.json +27 -0
  54. package/blocks/appointments/components/booking-page.tsx +2 -2
  55. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  56. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  57. package/blocks/appointments/components/meeting-list.tsx +29 -13
  58. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  59. package/blocks/campaigns/README.md +1 -1
  60. package/blocks/campaigns/block.json +34 -1
  61. package/blocks/campaigns/components/campaign-assets.tsx +1 -1
  62. package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
  63. package/blocks/campaigns/components/campaign-links.tsx +180 -0
  64. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  65. package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
  66. package/blocks/campaigns/components/icp-detail.tsx +11 -2
  67. package/blocks/campaigns/components/icp-editor.tsx +8 -3
  68. package/blocks/campaigns/components/persona-detail.tsx +11 -2
  69. package/blocks/campaigns/components/persona-editor.tsx +6 -1
  70. package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
  71. package/blocks/coach/components/call-scorecard.tsx +7 -7
  72. package/blocks/company-intelligence/README.md +132 -0
  73. package/blocks/company-intelligence/block.json +318 -0
  74. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  75. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  76. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  77. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  78. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  79. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  80. package/blocks/company-intelligence/components/research-run.tsx +165 -0
  81. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  82. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  83. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  84. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  85. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  86. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  87. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  88. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  89. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  90. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  91. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  92. package/blocks/copilot/components/copilot-panel.tsx +1 -1
  93. package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
  94. package/blocks/crm/README.md +25 -0
  95. package/blocks/crm/block.json +257 -9
  96. package/blocks/crm/components/bulk-update.tsx +119 -0
  97. package/blocks/crm/components/companies-screen.tsx +23 -9
  98. package/blocks/crm/components/company-detail.tsx +2 -2
  99. package/blocks/crm/components/contact-detail.tsx +4 -4
  100. package/blocks/crm/components/contact-documents-tab.tsx +144 -0
  101. package/blocks/crm/components/contact-insights-tab.tsx +118 -0
  102. package/blocks/crm/components/contact-lookup-link.tsx +85 -0
  103. package/blocks/crm/components/contact-row-actions.tsx +43 -0
  104. package/blocks/crm/components/contact-tag-chips.tsx +113 -0
  105. package/blocks/crm/components/contacts-screen.tsx +77 -18
  106. package/blocks/crm/components/dedupe-review.tsx +6 -1
  107. package/blocks/crm/components/import-wizard.tsx +3 -7
  108. package/blocks/crm/components/list-select.tsx +20 -0
  109. package/blocks/crm/components/owner-assign.tsx +132 -0
  110. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  111. package/blocks/crm/components/record-form.tsx +1 -1
  112. package/blocks/crm/components/record-open-links.tsx +79 -0
  113. package/blocks/crm/components/tag-assign.tsx +112 -0
  114. package/blocks/crm/components/tag-manager.tsx +178 -0
  115. package/blocks/crm/components/tasks-screen.tsx +35 -15
  116. package/blocks/crm/components/unlock-info-button.tsx +82 -0
  117. package/blocks/crm/components/verify-email-button.tsx +93 -0
  118. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  119. package/blocks/crm/hooks/use-contact-tags.ts +29 -0
  120. package/blocks/crm/hooks/use-page-query.ts +3 -1
  121. package/blocks/crm/lib/crm-types.ts +14 -0
  122. package/blocks/crm/lib/read-lists.ts +16 -0
  123. package/blocks/crm/lib/record-actions.ts +102 -0
  124. package/blocks/deals/README.md +25 -6
  125. package/blocks/deals/block.json +168 -2
  126. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  127. package/blocks/deals/components/create-deal-button.tsx +109 -0
  128. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  129. package/blocks/deals/components/deal-detail.tsx +7 -5
  130. package/blocks/deals/components/deal-filters.tsx +65 -0
  131. package/blocks/deals/components/deal-line-items.tsx +120 -0
  132. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  133. package/blocks/deals/components/deal-related.tsx +30 -9
  134. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  135. package/blocks/deals/components/deals-screen.tsx +57 -22
  136. package/blocks/deals/components/forecast-view.tsx +4 -4
  137. package/blocks/deals/components/pipeline-board.tsx +8 -6
  138. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  139. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  140. package/blocks/deals/components/stage-editor.tsx +1 -1
  141. package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
  142. package/blocks/deals/lib/deal-extras.ts +156 -0
  143. package/blocks/dialer/README.md +14 -5
  144. package/blocks/dialer/block.json +17 -2
  145. package/blocks/dialer/components/call-contact-button.tsx +26 -9
  146. package/blocks/dialer/components/call-log-screen.tsx +10 -2
  147. package/blocks/dialer/components/call-log.tsx +16 -4
  148. package/blocks/dialer/components/call-queue.tsx +3 -3
  149. package/blocks/dialer/components/dialer-screen.tsx +69 -27
  150. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  151. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  152. package/blocks/dialer/components/session-start-form.tsx +58 -11
  153. package/blocks/dialer/components/softphone.tsx +1 -1
  154. package/blocks/documents/README.md +86 -0
  155. package/blocks/documents/block.json +378 -0
  156. package/blocks/documents/components/account-bar.tsx +48 -0
  157. package/blocks/documents/components/correspondence-form.tsx +95 -0
  158. package/blocks/documents/components/correspondence-import.tsx +106 -0
  159. package/blocks/documents/components/correspondence-panel.tsx +191 -0
  160. package/blocks/documents/components/documents-screen.tsx +76 -0
  161. package/blocks/documents/components/library-document-detail.tsx +175 -0
  162. package/blocks/documents/components/library-document-form.tsx +105 -0
  163. package/blocks/documents/components/library-folder-form.tsx +74 -0
  164. package/blocks/documents/components/library-panel.tsx +235 -0
  165. package/blocks/documents/components/library-upload.tsx +160 -0
  166. package/blocks/documents/components/question-row.tsx +130 -0
  167. package/blocks/documents/components/questionnaire-detail.tsx +197 -0
  168. package/blocks/documents/components/questionnaires-panel.tsx +173 -0
  169. package/blocks/documents/components/record-documents-tab.tsx +109 -0
  170. package/blocks/documents/lib/documents-types.ts +252 -0
  171. package/blocks/documents/lib/download.ts +64 -0
  172. package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
  173. package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
  174. package/blocks/documents/recipes/organize-the-library.ts +25 -0
  175. package/blocks/email-signatures/README.md +123 -0
  176. package/blocks/email-signatures/block.json +341 -0
  177. package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
  178. package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
  179. package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
  180. package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
  181. package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
  182. package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
  183. package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
  184. package/blocks/email-signatures/components/signature-policy.tsx +130 -0
  185. package/blocks/email-signatures/components/signature-preview.tsx +119 -0
  186. package/blocks/email-signatures/components/signature-report.tsx +77 -0
  187. package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
  188. package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
  189. package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
  190. package/blocks/email-signatures/lib/signatures-types.ts +392 -0
  191. package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
  192. package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
  193. package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
  194. package/blocks/enrichment/README.md +38 -10
  195. package/blocks/enrichment/block.json +164 -12
  196. package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
  197. package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
  198. package/blocks/enrichment/components/bulk-enrich-panel.tsx +79 -30
  199. package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
  200. package/blocks/enrichment/components/enrich-button.tsx +1 -1
  201. package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
  202. package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
  203. package/blocks/enrichment/components/enrichment-screen.tsx +103 -19
  204. package/blocks/enrichment/components/list-pipelines.tsx +97 -0
  205. package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
  206. package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
  207. package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
  208. package/blocks/enrichment/components/waterfall-config.tsx +27 -7
  209. package/blocks/enrichment/lib/enrichment-types.ts +51 -0
  210. package/blocks/enrichment/lib/pipeline-types.ts +234 -0
  211. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  212. package/blocks/forms/README.md +10 -0
  213. package/blocks/forms/block.json +107 -0
  214. package/blocks/forms/components/form-activity.tsx +71 -0
  215. package/blocks/forms/components/form-backfill.tsx +108 -0
  216. package/blocks/forms/components/form-builder.tsx +1 -1
  217. package/blocks/forms/components/form-embed.tsx +1 -1
  218. package/blocks/forms/components/form-groups.tsx +151 -0
  219. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  220. package/blocks/forms/components/form-route-history.tsx +72 -0
  221. package/blocks/forms/components/form-saved-views.tsx +81 -0
  222. package/blocks/forms/components/forms-screen.tsx +57 -20
  223. package/blocks/forms/components/more-rows.tsx +30 -0
  224. package/blocks/forms/components/submissions-export.tsx +67 -0
  225. package/blocks/forms/components/submissions-table.tsx +15 -6
  226. package/blocks/forms/lib/forms-extra.ts +67 -0
  227. package/blocks/inbox/README.md +45 -4
  228. package/blocks/inbox/block.json +145 -8
  229. package/blocks/inbox/components/blocked-senders.tsx +103 -0
  230. package/blocks/inbox/components/compose-message.tsx +193 -0
  231. package/blocks/inbox/components/inbox-screen.tsx +54 -6
  232. package/blocks/inbox/components/inbox-search.tsx +106 -0
  233. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  234. package/blocks/inbox/components/tag-manager.tsx +136 -0
  235. package/blocks/inbox/components/thread-actions.tsx +133 -0
  236. package/blocks/inbox/components/thread-badge.tsx +2 -2
  237. package/blocks/inbox/components/thread-view.tsx +3 -1
  238. package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
  239. package/blocks/inbox/lib/inbox-manage.ts +227 -0
  240. package/blocks/integrations/README.md +54 -1
  241. package/blocks/integrations/block.json +189 -15
  242. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  243. package/blocks/integrations/components/connection-health.tsx +59 -0
  244. package/blocks/integrations/components/crm-detail.tsx +20 -1
  245. package/blocks/integrations/components/crm-records.tsx +103 -0
  246. package/blocks/integrations/components/field-mappings.tsx +61 -0
  247. package/blocks/integrations/components/integration-list.tsx +4 -4
  248. package/blocks/integrations/components/integrations-screen.tsx +20 -3
  249. package/blocks/integrations/components/migration-panel.tsx +133 -0
  250. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  251. package/blocks/integrations/components/sync-status.tsx +6 -6
  252. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  253. package/blocks/integrations/components/webhook-detail.tsx +26 -7
  254. package/blocks/integrations/components/webhook-manager.tsx +30 -17
  255. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  256. package/blocks/integrations/lib/crm-utils.ts +39 -0
  257. package/blocks/integrations/lib/facts.tsx +13 -0
  258. package/blocks/integrations/lib/integrations-utils.ts +5 -0
  259. package/blocks/integrations/lib/load-more.tsx +141 -0
  260. package/blocks/intent/components/account-intent-score.tsx +5 -5
  261. package/blocks/intent/components/intent-feed.tsx +8 -8
  262. package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
  263. package/blocks/intent/lib/intent-utils.ts +4 -4
  264. package/blocks/lifecycle/components/account-related.tsx +8 -1
  265. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  266. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  267. package/blocks/lists/README.md +7 -1
  268. package/blocks/lists/block.json +31 -3
  269. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  270. package/blocks/lists/components/add-to-list-button.tsx +17 -7
  271. package/blocks/lists/components/list-picker.tsx +13 -3
  272. package/blocks/lists/components/list-table.tsx +1 -1
  273. package/blocks/lists/components/lists-screen.tsx +4 -2
  274. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  275. package/blocks/lists/components/suppression-manager.tsx +27 -6
  276. package/blocks/lists/components/suppressions-screen.tsx +21 -12
  277. package/blocks/mailboxes/block.json +4 -0
  278. package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
  279. package/blocks/mailboxes/components/domain-detail.tsx +10 -4
  280. package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
  281. package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
  282. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  283. package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
  284. package/blocks/mailboxes/components/warmup-health.tsx +20 -20
  285. package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
  286. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  287. package/blocks/newsletter/README.md +12 -0
  288. package/blocks/newsletter/block.json +117 -0
  289. package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
  290. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  291. package/blocks/newsletter/components/more-rows.tsx +30 -0
  292. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  293. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  294. package/blocks/newsletter/components/newsletter-screen.tsx +38 -13
  295. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  296. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  297. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  298. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  299. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  300. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  301. package/blocks/nurture/components/nurture-builder.tsx +10 -2
  302. package/blocks/nurture/components/nurture-enroll.tsx +6 -7
  303. package/blocks/nurture/components/nurture-form.tsx +5 -8
  304. package/blocks/nurture/components/nurture-screen.tsx +16 -5
  305. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  306. package/blocks/objects/README.md +1 -1
  307. package/blocks/objects/block.json +13 -5
  308. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  309. package/blocks/objects/components/object-record-detail.tsx +8 -1
  310. package/blocks/objects/components/object-schema-editor.tsx +1 -1
  311. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  312. package/blocks/pages/README.md +14 -0
  313. package/blocks/pages/block.json +123 -2
  314. package/blocks/pages/components/more-rows.tsx +30 -0
  315. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  316. package/blocks/pages/components/page-analytics.tsx +176 -0
  317. package/blocks/pages/components/page-clone.tsx +99 -0
  318. package/blocks/pages/components/page-domains.tsx +167 -0
  319. package/blocks/pages/components/page-editor.tsx +1 -1
  320. package/blocks/pages/components/page-generation-status.tsx +48 -0
  321. package/blocks/pages/components/page-tracking.tsx +137 -0
  322. package/blocks/pages/components/pages-screen.tsx +57 -16
  323. package/blocks/pages/components/seo-report.tsx +6 -1
  324. package/blocks/pages/hooks/use-landing-page-paths.ts +60 -0
  325. package/blocks/pages/lib/studio.ts +7 -0
  326. package/blocks/phone-numbers/README.md +74 -0
  327. package/blocks/phone-numbers/block.json +262 -0
  328. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  329. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  330. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  331. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  332. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  333. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  334. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  335. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  336. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  337. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  338. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  339. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  340. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  341. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  342. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  343. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  344. package/blocks/pickers/README.md +23 -0
  345. package/blocks/pickers/block.json +103 -0
  346. package/blocks/pickers/components/company-picker.tsx +90 -0
  347. package/blocks/pickers/components/contact-picker.tsx +131 -0
  348. package/blocks/pickers/components/member-picker.tsx +99 -0
  349. package/blocks/pickers/components/search-picker.tsx +98 -0
  350. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  351. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  352. package/blocks/pickers/lib/use-debounced.ts +13 -0
  353. package/blocks/programmatic-pages/README.md +82 -0
  354. package/blocks/programmatic-pages/block.json +292 -0
  355. package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
  356. package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
  357. package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
  358. package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
  359. package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
  360. package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
  361. package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
  362. package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
  363. package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
  364. package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
  365. package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
  366. package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
  367. package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
  368. package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
  369. package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
  370. package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
  371. package/blocks/quotes/README.md +38 -1
  372. package/blocks/quotes/block.json +170 -6
  373. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  374. package/blocks/quotes/components/manual-products.tsx +144 -0
  375. package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
  376. package/blocks/quotes/components/quote-builder.tsx +1 -1
  377. package/blocks/quotes/components/quote-documents.tsx +94 -0
  378. package/blocks/quotes/components/quote-evidence.tsx +95 -0
  379. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  380. package/blocks/quotes/components/quote-list.tsx +28 -8
  381. package/blocks/quotes/components/quote-related.tsx +3 -2
  382. package/blocks/quotes/components/quote-settings.tsx +184 -0
  383. package/blocks/quotes/components/quote-templates.tsx +189 -0
  384. package/blocks/quotes/components/quote-viewer.tsx +23 -5
  385. package/blocks/quotes/components/quotes-screen.tsx +15 -5
  386. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  387. package/blocks/quotes/lib/quote-admin.ts +264 -0
  388. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  389. package/blocks/radar/components/radar-overview.tsx +4 -4
  390. package/blocks/radar/components/radar-screen.tsx +2 -2
  391. package/blocks/sequencer/README.md +61 -0
  392. package/blocks/sequencer/block.json +200 -6
  393. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  394. package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
  395. package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
  396. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  397. package/blocks/sequencer/components/sequence-builder.tsx +32 -23
  398. package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
  399. package/blocks/sequencer/components/sequence-detail.tsx +10 -2
  400. package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
  401. package/blocks/sequencer/components/sequence-enrollments.tsx +5 -2
  402. package/blocks/sequencer/components/sequence-list.tsx +1 -1
  403. package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
  404. package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
  405. package/blocks/sequencer/components/sequence-stats.tsx +8 -8
  406. package/blocks/sequencer/components/sequence-templates.tsx +160 -0
  407. package/blocks/sequencer/components/sequences-screen.tsx +39 -6
  408. package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
  409. package/blocks/sequencer/components/step-editor.tsx +21 -12
  410. package/blocks/sequencer/components/step-templates.tsx +224 -0
  411. package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
  412. package/blocks/sequencer/lib/sequencer-utils.ts +9 -5
  413. package/blocks/signals/README.md +39 -0
  414. package/blocks/signals/block.json +287 -0
  415. package/blocks/signals/components/auto-seed-panel.tsx +111 -0
  416. package/blocks/signals/components/listener-form.tsx +184 -0
  417. package/blocks/signals/components/matched-posts.tsx +195 -0
  418. package/blocks/signals/components/post-engagements.tsx +114 -0
  419. package/blocks/signals/components/record-social-tab.tsx +49 -0
  420. package/blocks/signals/components/social-listeners-screen.tsx +250 -0
  421. package/blocks/signals/components/social-settings.tsx +128 -0
  422. package/blocks/signals/components/source-lists.tsx +83 -0
  423. package/blocks/signals/components/top-engagers.tsx +68 -0
  424. package/blocks/signals/components/x-integration.tsx +129 -0
  425. package/blocks/signals/lib/signals-types.ts +228 -0
  426. package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
  427. package/blocks/signals/recipes/spend-against-caps.ts +14 -0
  428. package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
  429. package/blocks/social-ads/README.md +8 -0
  430. package/blocks/social-ads/block.json +137 -4
  431. package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
  432. package/blocks/social-ads/components/audience-sync.tsx +6 -2
  433. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  434. package/blocks/social-ads/components/campaign-detail.tsx +6 -1
  435. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  436. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  437. package/blocks/social-ads/components/creative-detail.tsx +6 -1
  438. package/blocks/social-ads/components/creative-gallery.tsx +20 -2
  439. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  440. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  441. package/blocks/social-ads/components/post-composer.tsx +4 -2
  442. package/blocks/social-ads/components/post-detail.tsx +3 -0
  443. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  444. package/blocks/social-ads/components/social-performance.tsx +80 -0
  445. package/blocks/social-ads/components/social-screen.tsx +33 -6
  446. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  447. package/blocks/surveys/README.md +91 -0
  448. package/blocks/surveys/block.json +299 -0
  449. package/blocks/surveys/components/account-nps.tsx +69 -0
  450. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  451. package/blocks/surveys/components/org-summary.tsx +82 -0
  452. package/blocks/surveys/components/sending-domains.tsx +113 -0
  453. package/blocks/surveys/components/survey-form.tsx +126 -0
  454. package/blocks/surveys/components/survey-responses.tsx +145 -0
  455. package/blocks/surveys/components/survey-results.tsx +72 -0
  456. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  457. package/blocks/surveys/components/survey-send.tsx +162 -0
  458. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  459. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  460. package/blocks/surveys/lib/surveys-types.ts +228 -0
  461. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  462. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  463. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  464. package/blocks/visitors/components/snippet-install.tsx +18 -9
  465. package/blocks/visitors/components/traffic-chart.tsx +7 -6
  466. package/blocks/visitors/components/visitor-company-card.tsx +24 -14
  467. package/blocks/visitors/components/visitor-feed.tsx +5 -5
  468. package/blocks/web-analytics/README.md +102 -0
  469. package/blocks/web-analytics/block.json +292 -0
  470. package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
  471. package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
  472. package/blocks/web-analytics/components/audience-tab.tsx +135 -0
  473. package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
  474. package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
  475. package/blocks/web-analytics/components/overview-tab.tsx +102 -0
  476. package/blocks/web-analytics/components/pages-tab.tsx +148 -0
  477. package/blocks/web-analytics/components/ranked-table.tsx +103 -0
  478. package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
  479. package/blocks/web-analytics/components/report-card.tsx +71 -0
  480. package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
  481. package/blocks/web-analytics/hooks/use-report.ts +48 -0
  482. package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
  483. package/blocks/web-analytics/lib/page-path.ts +14 -0
  484. package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
  485. package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
  486. package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
  487. package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
  488. package/blocks/webchat/components/chat-widget-config.tsx +1 -1
  489. package/blocks/webchat/components/conversation-list.tsx +1 -1
  490. package/blocks/workbench/README.md +54 -2
  491. package/blocks/workbench/block.json +234 -5
  492. package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
  493. package/blocks/workbench/components/run-progress.tsx +1 -1
  494. package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
  495. package/blocks/workbench/components/workbench-automation.tsx +36 -0
  496. package/blocks/workbench/components/workbench-connections.tsx +136 -0
  497. package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
  498. package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
  499. package/blocks/workbench/components/workbench-grid.tsx +71 -8
  500. package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
  501. package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
  502. package/blocks/workbench/components/workbench-schedule.tsx +191 -0
  503. package/blocks/workbench/components/workbench-screen.tsx +65 -22
  504. package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
  505. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  506. package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
  507. package/blocks/workbench/components/workbench-triggers.tsx +178 -0
  508. package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
  509. package/blocks/workbench/lib/api-status.ts +8 -0
  510. package/blocks/workbench/lib/automation-types.ts +257 -0
  511. package/blocks/workflows/components/workflow-editor.tsx +1 -1
  512. package/dist/cli.js +744 -394
  513. package/dist/index.browser.js +1 -1
  514. package/dist/index.browser.js.map +1 -1
  515. package/dist/index.browser.mjs +1 -1
  516. package/dist/index.browser.mjs.map +1 -1
  517. package/dist/index.d.mts +2 -2
  518. package/dist/index.d.ts +2 -2
  519. package/dist/index.js +1 -1
  520. package/dist/index.js.map +1 -1
  521. package/dist/index.mjs +1 -1
  522. package/dist/index.mjs.map +1 -1
  523. package/dist/react.browser.js +1 -1
  524. package/dist/react.browser.js.map +1 -1
  525. package/dist/react.browser.mjs +1 -1
  526. package/dist/react.browser.mjs.map +1 -1
  527. package/dist/react.d.mts +2 -2
  528. package/dist/react.d.ts +2 -2
  529. package/dist/react.js +1 -1
  530. package/dist/react.js.map +1 -1
  531. package/dist/react.mjs +1 -1
  532. package/dist/react.mjs.map +1 -1
  533. package/dist/types/blocks/auth.d.ts +45 -0
  534. package/dist/types/blocks/brand.d.ts +11 -2
  535. package/dist/types/blocks/create.d.ts +11 -4
  536. package/dist/types/blocks/doctor.d.ts +1 -0
  537. package/dist/types/blocks/flows.d.ts +7 -1
  538. package/dist/types/blocks/orgBrand.d.ts +34 -0
  539. package/dist/types/cli.d.ts +3 -0
  540. package/dist/types/version.d.ts +2 -2
  541. package/docs/api/desk.d.ts +2 -2
  542. package/docs/api/desk.md +3 -3
  543. package/docs/api/forms.md +1 -1
  544. package/package.json +1 -1
  545. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  546. package/stacks/6sense/pages/contacts.tsx.tmpl +21 -0
  547. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  548. package/stacks/6sense.json +37 -4
  549. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  550. package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
  551. package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
  552. package/stacks/apollo/pages/contacts.tsx.tmpl +35 -95
  553. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  554. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  555. package/stacks/apollo/pages/lists.tsx.tmpl +24 -0
  556. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  557. package/stacks/apollo/pages/sequences.tsx.tmpl +18 -0
  558. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  559. package/stacks/apollo.json +68 -9
  560. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  561. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  562. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  563. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  564. package/stacks/calendly.json +8 -4
  565. package/stacks/clay/pages/companies.tsx.tmpl +17 -1
  566. package/stacks/clay/pages/contacts.tsx.tmpl +14 -0
  567. package/stacks/clay/pages/lists.tsx.tmpl +24 -0
  568. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  569. package/stacks/clay.json +16 -3
  570. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  571. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  572. package/stacks/gong/pages/contacts.tsx.tmpl +34 -0
  573. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  574. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  575. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  576. package/stacks/gong.json +23 -6
  577. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  578. package/stacks/hootsuite/pages/contacts.tsx.tmpl +30 -0
  579. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  580. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  581. package/stacks/hootsuite.json +19 -4
  582. package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
  583. package/stacks/hubspot/pages/companies.tsx.tmpl +16 -35
  584. package/stacks/hubspot/pages/contacts.tsx.tmpl +35 -95
  585. package/stacks/hubspot/pages/deals.tsx.tmpl +14 -1
  586. package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
  587. package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
  588. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  589. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  590. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  591. package/stacks/hubspot/pages/sequences.tsx.tmpl +18 -0
  592. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  593. package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
  594. package/stacks/hubspot.json +66 -7
  595. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  596. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  597. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  598. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  599. package/stacks/intercom.json +18 -4
  600. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  601. package/stacks/mailchimp/pages/contacts.tsx.tmpl +30 -0
  602. package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
  603. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  604. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  605. package/stacks/mailchimp.json +22 -6
  606. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  607. package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
  608. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  609. package/stacks/outreach/pages/contacts.tsx.tmpl +29 -66
  610. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  611. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  612. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  613. package/stacks/outreach/pages/sequences.tsx.tmpl +18 -0
  614. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  615. package/stacks/outreach.json +38 -9
  616. package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
  617. package/stacks/pipedrive/pages/contacts.tsx.tmpl +17 -35
  618. package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
  619. package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
  620. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  621. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  622. package/stacks/pipedrive.json +14 -2
  623. package/stacks/salesforce/pages/companies.tsx.tmpl +16 -35
  624. package/stacks/salesforce/pages/contacts.tsx.tmpl +26 -35
  625. package/stacks/salesforce/pages/deals.tsx.tmpl +14 -1
  626. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  627. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  628. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  629. package/stacks/salesforce.json +17 -3
  630. package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
  631. package/stacks/zoominfo/pages/contacts.tsx.tmpl +16 -0
  632. package/stacks/zoominfo/pages/lists.tsx.tmpl +24 -0
  633. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  634. package/stacks/zoominfo.json +36 -3
  635. package/styles/styles.css +1 -1
  636. package/templates/auth/auth-route.ts.tmpl +16 -0
  637. package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
  638. package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
  639. package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
  640. package/templates/auth/g8.auth.password.ts.tmpl +18 -0
  641. package/templates/auth/g8.auth.session.ts.tmpl +23 -0
  642. package/templates/auth/middleware.ts.tmpl +14 -0
  643. package/templates/next-app/README.md.tmpl +5 -0
  644. package/templates/next-app/app/globals.css.tmpl +349 -64
  645. package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
  646. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
@@ -98,7 +98,7 @@ export function BookingPage({ eventTypeId, daysAhead = 14, timeZone, defaultAtte
98
98
  if (book.data) {
99
99
  return (
100
100
  <div className={`space-y-2 rounded-lg border p-4 ${className ?? ""}`} role="status">
101
- <h2 className="text-base font-semibold">You are booked</h2>
101
+ <h2 className="text-base font-medium">You are booked</h2>
102
102
  <p className="text-sm">
103
103
  {eventType.data?.title ?? "Meeting"} on {formatDateTime(bookedAt ?? slot?.time, zone)}. A calendar invite was sent to {email}.
104
104
  </p>
@@ -116,7 +116,7 @@ export function BookingPage({ eventTypeId, daysAhead = 14, timeZone, defaultAtte
116
116
  return (
117
117
  <div className={`space-y-4 ${className ?? ""}`}>
118
118
  <header>
119
- <h2 className="text-base font-semibold">{eventType.data?.title ?? "Book a meeting"}</h2>
119
+ <h2 className="text-base font-medium">{eventType.data?.title ?? "Book a meeting"}</h2>
120
120
  <p className="text-sm text-muted-foreground">
121
121
  {eventType.data ? `${eventType.data.length} minutes · ` : ""}times shown in {zone}
122
122
  </p>
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ /**
4
+ * MeetingContactLinks: "Open <attendee>" buttons for a booked or recorded meeting, one per attendee (at most three)
5
+ * who is a contact in your CRM. Use it in MeetingsScreen's `renderDetailActions`:
6
+ * renderDetailActions={(selection) => <MeetingContactLinks selection={selection} onOpenContact={(id) => router.push(`/contacts?id=${id}`)} />}
7
+ *
8
+ * SDK operations, in order:
9
+ * 1. getBooking GET /appointments/bookings/{uid} (read) for a booking
10
+ * getMeeting GET /inbox/meetings/{id} (read) for a recorded meeting
11
+ * 2. listContacts GET /contacts?email= (read) once per attendee, via the crm ContactLookupLink
12
+ * Nothing here is billable, external or destructive.
13
+ */
14
+
15
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
16
+ import { g8Get } from "@/g8/lib/g8-client";
17
+ import { ContactLookupLink } from "@/g8/crm/components/contact-lookup-link";
18
+ import { unwrap } from "../lib/appointments-types";
19
+ import type { MeetingSelection } from "./meeting-list";
20
+
21
+ interface Attendee { email?: string | null; name?: string | null }
22
+
23
+ export interface MeetingContactLinksProps {
24
+ selection: MeetingSelection;
25
+ /** Route to the contact in your contacts screen. */
26
+ onOpenContact: (contactId: number) => void;
27
+ }
28
+
29
+ export function MeetingContactLinks({ selection, onOpenContact }: MeetingContactLinksProps) {
30
+ const { kind, id } = selection;
31
+ const q = useG8Query(async (signal) => {
32
+ const path = kind === "booking" ? `/appointments/bookings/${encodeURIComponent(id)}` : `/inbox/meetings/${encodeURIComponent(id)}`;
33
+ return unwrap<{ attendees?: Attendee[] | null }>(await g8Get(path, undefined, { signal })).attendees ?? [];
34
+ }, [kind, id]);
35
+ const seen = new Set<string>();
36
+ const people = (q.data ?? []).filter((a) => {
37
+ const email = a.email?.trim().toLowerCase();
38
+ if (!email || seen.has(email)) return false;
39
+ seen.add(email);
40
+ return true;
41
+ }).slice(0, 3);
42
+ return (
43
+ <>
44
+ {people.map((a) => (
45
+ <ContactLookupLink key={a.email} email={a.email} label={`Open ${a.name || a.email}`} onOpen={onOpenContact} />
46
+ ))}
47
+ </>
48
+ );
49
+ }
@@ -48,7 +48,7 @@ function RecordedDetail({ id, className }: { id: string; className?: string }) {
48
48
  const m = q.data;
49
49
  return (
50
50
  <section className={`space-y-3 ${className ?? ""}`}>
51
- <h2 className="text-base font-semibold">{m.subject || "Untitled meeting"}</h2>
51
+ <h2 className="text-base font-medium">{m.subject || "Untitled meeting"}</h2>
52
52
  <dl className="grid gap-3 sm:grid-cols-2">
53
53
  <Field label="When">{formatDateTime(m.start_time)}</Field>
54
54
  <Field label="Attendees">{(m.attendees ?? []).map((a) => a.name || a.email).join(", ") || "Unknown"}</Field>
@@ -94,7 +94,7 @@ function BookingDetail({ uid, onChanged, className }: { uid: string; onChanged?:
94
94
 
95
95
  return (
96
96
  <section className={`space-y-4 ${className ?? ""}`}>
97
- <h2 className="text-base font-semibold">{b.title || b.event_type_title || "Meeting"}</h2>
97
+ <h2 className="text-base font-medium">{b.title || b.event_type_title || "Meeting"}</h2>
98
98
  <dl className="grid gap-3 sm:grid-cols-2">
99
99
  <Field label="When">{formatDateTime(b.start_time)}</Field>
100
100
  <Field label="Status"><span className="capitalize">{b.status}</span></Field>
@@ -18,6 +18,8 @@ import { ApiError } from "@/g8/components/api-error";
18
18
  import { EmptyState } from "@/g8/components/empty-state";
19
19
  import { g8Get } from "@/g8/lib/g8-client";
20
20
  import { useG8Query } from "@/g8/hooks/use-g8-query";
21
+ import { useUrlFilters } from "@/g8/hooks/use-url-filters";
22
+ import { SelectFilter, TextFilter } from "@/g8/components/screen/filter-controls";
21
23
  import { formatDateTime, listOf, type Booking, type RecordedMeeting } from "../lib/appointments-types";
22
24
  import { rowActivationProps } from "@/g8/hooks/use-row-activation";
23
25
  import { RecordLink } from "@/g8/components/screen/record-link";
@@ -45,8 +47,8 @@ const VIEWS: ReadonlyArray<{ value: MeetingView; label: string }> = [
45
47
  { value: "recorded", label: "Recorded" },
46
48
  ];
47
49
 
48
- function bookingQuery(view: Exclude<MeetingView, "recorded">, now: string, page: number, take: number) {
49
- const base = { skip: page * take, take };
50
+ function bookingQuery(view: Exclude<MeetingView, "recorded">, now: string, page: number, take: number, attendeeEmail?: string) {
51
+ const base = { skip: page * take, take, attendee_email: attendeeEmail || undefined };
50
52
  if (view === "upcoming") return { ...base, after_start: now, sort_by: "start_time", sort_order: "asc" };
51
53
  if (view === "past") return { ...base, before_end: now, sort_by: "start_time", sort_order: "desc" };
52
54
  return { ...base, status: "cancelled", sort_by: "start_time", sort_order: "desc" };
@@ -56,36 +58,38 @@ function attendeeNames(b: Booking): string {
56
58
  return b.attendees.map((a) => a.name || a.email).join(", ") || "No attendees";
57
59
  }
58
60
 
59
- function useDebouncedText(value: string, ms = 300): string {
60
- const [v, setV] = useState(value);
61
- useEffect(() => { const t = setTimeout(() => setV(value.trim()), ms); return () => clearTimeout(t); }, [value, ms]);
62
- return v;
63
- }
61
+ /** Every filter the meetings (/inbox/meetings) and bookings APIs take besides the view tab. */
62
+ const MEETING_FILTERS = ["search", "participant_email", "timeframe", "has_transcript", "attendee_email"] as const;
64
63
 
65
64
  export function MeetingList({ linkParam, onSelect, initialView = "upcoming", pageSize = 20, refreshKey, selectedId, className }: MeetingListProps) {
66
65
  const [view, setView] = useState<MeetingView>(initialView);
67
66
  const [page, setPage] = useState(0);
68
- const [search, setSearch] = useState("");
69
- const query = useDebouncedText(search);
67
+ // Every filter the meetings and bookings APIs take lives in the URL (`m_participant_email=...`).
68
+ const uf = useUrlFilters(MEETING_FILTERS, { prefix: "m_" });
69
+ const search = uf.draft.search;
70
+ const query = uf.filters.search.trim();
71
+ const { participant_email, timeframe, has_transcript, attendee_email } = uf.filters;
70
72
  const now = useMemo(() => new Date().toISOString(), [view]); // eslint-disable-line react-hooks/exhaustive-deps
71
73
 
72
74
  const rows = useG8Query(
73
75
  async (signal) => {
74
76
  if (view === "recorded") {
75
- const env = await g8Get("/inbox/meetings", { search: query || undefined, page: page + 1, page_size: pageSize }, { signal });
77
+ const env = await g8Get("/inbox/meetings", { search: query || undefined, participant_email: participant_email ? participant_email.split(",").map((e) => e.trim()).filter(Boolean) : undefined, timeframe: timeframe || undefined, has_transcript: has_transcript ? has_transcript === "yes" : undefined, page: page + 1, page_size: pageSize }, { signal });
76
78
  return { kind: "meeting" as const, items: listOf<RecordedMeeting>(env), more: Boolean((env as { pagination?: { has_next?: boolean } }).pagination?.has_next) };
77
79
  }
78
- const env = await g8Get("/appointments/bookings", bookingQuery(view, now, page, pageSize), { signal });
80
+ const env = await g8Get("/appointments/bookings", bookingQuery(view, now, page, pageSize, attendee_email.trim()), { signal });
79
81
  const items = listOf<Booking>(env, "bookings");
80
82
  return { kind: "booking" as const, items, more: items.length === pageSize };
81
83
  },
82
- [view, page, query, pageSize, now, refreshKey],
84
+ [view, page, query, participant_email, timeframe, has_transcript, attendee_email, pageSize, now, refreshKey],
85
+ { enabled: uf.ready },
83
86
  );
84
87
 
85
88
  const switchView = (next: MeetingView) => {
86
89
  setView(next);
87
90
  setPage(0);
88
91
  };
92
+ useEffect(() => { setPage(0); }, [query, participant_email, timeframe, has_transcript, attendee_email]);
89
93
  const result = rows.data;
90
94
 
91
95
  return (
@@ -98,7 +102,19 @@ export function MeetingList({ linkParam, onSelect, initialView = "upcoming", pag
98
102
  </Button>
99
103
  ))}
100
104
  </div>
101
- {view === "recorded" ? <Input aria-label="Search meetings" className="w-56" placeholder="Search recorded meetings" value={search} onChange={(e) => { setSearch(e.target.value); setPage(0); }} /> : null}
105
+ {view === "recorded" ? <Input aria-label="Search meetings" className="w-56" placeholder="Search recorded meetings" value={search} onChange={(e) => uf.set("search", e.target.value)} /> : null}
106
+ </div>
107
+ <div className="flex flex-wrap items-center gap-2">
108
+ {view === "recorded" ? (
109
+ <>
110
+ <TextFilter label="participant email" placeholder="Participant email" type="email" value={uf.draft.participant_email} onChange={(v) => uf.set("participant_email", v)} className="w-52" />
111
+ <SelectFilter label="timeframe" any="Past (default)" value={uf.draft.timeframe} onChange={(v) => uf.set("timeframe", v)} options={[{ value: "upcoming", label: "Upcoming" }, { value: "all", label: "All" }]} />
112
+ <SelectFilter label="transcript" any="With or without transcript" value={uf.draft.has_transcript} onChange={(v) => uf.set("has_transcript", v)} options={[{ value: "yes", label: "Has a transcript" }, { value: "no", label: "No transcript" }]} />
113
+ </>
114
+ ) : (
115
+ <TextFilter label="attendee email" placeholder="Attendee email" type="email" value={uf.draft.attendee_email} onChange={(v) => uf.set("attendee_email", v)} className="w-52" />
116
+ )}
117
+ {uf.active ? <Button size="sm" variant="ghost" onClick={uf.clear}>Clear filters</Button> : null}
102
118
  </div>
103
119
 
104
120
  {rows.error ? (
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ /**
4
+ * RecordMeetingsTab: the meetings booked with one contact, past and upcoming, newest first, each opening in the meetings
5
+ * screen. Use it as an `extraDetailTabs` entry of ContactsScreen:
6
+ * { id: "meetings", label: "Meetings", render: (c) => <RecordMeetingsTab contactId={c.id} onOpenMeeting={(sel) => router.push(`/meetings?sel=${sel}`)} /> }
7
+ *
8
+ * SDK operations, in order:
9
+ * 1. getContactMeetings GET /contacts/{id}/meetings (read; booking events, the API answers the 50 most recent)
10
+ * Nothing here is billable, external or destructive. Book a new meeting from the scheduling screen.
11
+ */
12
+
13
+ import { Skeleton } from "@graph8/sdk/ui";
14
+ import { CalendarDays } from "lucide-react";
15
+ import { ApiError } from "@/g8/components/api-error";
16
+ import { EmptyState } from "@/g8/components/empty-state";
17
+ import { StatusPill } from "@/g8/components/screen/status-pill";
18
+ import type { Tone } from "@/g8/components/screen/status-pill";
19
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
20
+ import { g8Get } from "@/g8/lib/g8-client";
21
+ import { listOf } from "../lib/appointments-types";
22
+
23
+ /** One row of GET /contacts/{id}/meetings: a booking event. */
24
+ interface BookingEvent { event?: string | null; timestamp?: string | null; booking_id?: string | null; title?: string | null; start_time?: string | null; end_time?: string | null; attendees?: unknown }
25
+
26
+ /** The server returns at most this many events, newest first. */
27
+ export const MEETINGS_TAB_API_LIMIT = 50;
28
+
29
+ const EVENT: Record<string, { label: string; tone: Tone }> = {
30
+ "appointments.booking_created": { label: "Booked", tone: "info" },
31
+ "appointments.booking_rescheduled": { label: "Rescheduled", tone: "warning" },
32
+ "appointments.booking_cancelled": { label: "Cancelled", tone: "negative" },
33
+ };
34
+
35
+ function when(iso: string | null | undefined): string {
36
+ const d = iso ? new Date(iso) : null;
37
+ return d && !Number.isNaN(d.getTime()) ? d.toLocaleString(undefined, { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit" }) : "";
38
+ }
39
+
40
+ function attendeeNames(a: unknown): string {
41
+ if (!Array.isArray(a)) return "";
42
+ return a.map((x) => (typeof x === "string" ? x : ((x as { name?: string; email?: string } | null)?.name || (x as { email?: string } | null)?.email || ""))).filter(Boolean).join(", ");
43
+ }
44
+
45
+ export interface RecordMeetingsTabProps {
46
+ contactId: number;
47
+ /** Route to the booking in your meetings screen; receives the `sel` value ("booking:<id>"). Without it the rows link to `${meetingsPath}?sel=booking:<id>`. */
48
+ onOpenMeeting?: (selection: string) => void;
49
+ /** Path of your meetings screen for the plain links. Default "/meetings". */
50
+ meetingsPath?: string;
51
+ }
52
+
53
+ export function RecordMeetingsTab({ contactId, onOpenMeeting, meetingsPath = "/meetings" }: RecordMeetingsTabProps) {
54
+ const q = useG8Query(async (signal) => listOf<BookingEvent>(await g8Get(`/contacts/${contactId}/meetings`, undefined, { signal }), "items"), [contactId]);
55
+ if (q.error) return <ApiError error={q.error} title="Could not load meetings" onRetry={() => void q.refetch()} />;
56
+ if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading meetings" />;
57
+ if (q.data.length === 0) return <EmptyState compact icon={<CalendarDays className="size-8" />} title="No meetings yet" description="Meetings booked with this contact show up here." />;
58
+ return (
59
+ <div className="space-y-2">
60
+ <ul className="divide-y rounded-md border" aria-label="Meetings">
61
+ {q.data.map((m, i) => {
62
+ const kind = EVENT[m.event ?? ""] ?? { label: "Booking", tone: "neutral" as Tone };
63
+ const selection = m.booking_id ? `booking:${m.booking_id}` : null;
64
+ const title = m.title || "Meeting";
65
+ const detail = [when(m.start_time ?? m.timestamp), attendeeNames(m.attendees)].filter(Boolean).join(" · ");
66
+ return (
67
+ <li key={`${m.booking_id ?? "event"}-${m.event}-${i}`} className="flex items-center justify-between gap-3 p-3 text-sm">
68
+ <div className="min-w-0">
69
+ {selection ? (
70
+ <a
71
+ href={`${meetingsPath}?sel=${encodeURIComponent(selection)}`}
72
+ className="block truncate font-medium hover:underline"
73
+ onClick={(e) => {
74
+ if (!onOpenMeeting || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
75
+ e.preventDefault();
76
+ onOpenMeeting(selection);
77
+ }}
78
+ >
79
+ {title}
80
+ </a>
81
+ ) : <span className="font-medium">{title}</span>}
82
+ {detail ? <p className="truncate text-xs text-muted-foreground">{detail}</p> : null}
83
+ </div>
84
+ <StatusPill tone={kind.tone} dot>{kind.label}</StatusPill>
85
+ </li>
86
+ );
87
+ })}
88
+ </ul>
89
+ {q.data.length >= MEETINGS_TAB_API_LIMIT ? <p role="note" className="text-xs text-muted-foreground">Showing the {MEETINGS_TAB_API_LIMIT} most recent booking events. Older ones are in the meetings screen.</p> : null}
90
+ </div>
91
+ );
92
+ }
@@ -18,7 +18,7 @@ deep-linkable (`?tab=campaigns|profile|personas`, `?id=` the record, `?dtab=` th
18
18
  - **Company profile tab**: the profile plus ICPs; `IcpDetail` shows every field and edits, pins or archives.
19
19
  - **Personas tab**: `PersonaDetail` shows every field, the campaigns that target it, and edits, pins or archives.
20
20
 
21
- Slot props: `toolbarActions`, `renderDetailActions(campaign)`, `extraDetailTabs`, `websiteUrl`. Prerequisites: a
21
+ Slot props: `toolbarActions`, `renderDetailActions(campaign)`, `extraDetailTabs`, `websiteUrl`. `CampaignLinksTab` (`components/campaign-links.tsx`) is a ready `extraDetailTabs` entry: the campaign's landing pages, workflows and newsletters, each opening in your app (`onOpenLandingPage`, `onOpenWorkflow`, `onOpenNewsletter`), with link and unlink for landing pages and newsletters and unlink only for workflows (the API has no link operation for them; a newsletter that belongs to another campaign is not offered, because linking moves it). Prerequisites: a
22
22
  company profile with the real website, an active mailbox, a contact list, and credits for AI generation.
23
23
  `IcpEditor` and `PersonaEditor` gained optional `onOpen` and `refreshKey`; `CampaignAssets` optional
24
24
  `documentId` and `onDocumentChange`.
@@ -42,16 +42,25 @@
42
42
  "get_persona_personas__persona_id__get",
43
43
  "get_safe_to_send_deliverability_safe_to_send_get",
44
44
  "launch_campaign_campaigns__campaign_id__launch_post",
45
+ "link_landing_page_to_campaign_campaigns__campaign_id__landing_pages_post",
46
+ "link_newsletter_campaign_newsletters__newsletter_id__campaign_put",
45
47
  "list_campaign_documents_campaigns__campaign_id__documents_get",
48
+ "list_campaign_landing_pages_campaigns__campaign_id__landing_pages_get",
49
+ "list_campaign_workflows_campaigns__campaign_id__workflows_get",
46
50
  "list_campaigns_campaigns_get",
47
51
  "list_campaigns_for_picker_campaigns_picker_get",
48
52
  "list_icps_icps_get",
53
+ "list_landing_pages_landing_pages_get",
49
54
  "list_mailboxes_mailboxes_get",
55
+ "list_newsletters_newsletters_get",
50
56
  "list_personas_personas_get",
51
57
  "pin_icp_icps__icp_id__pin_post",
52
58
  "pin_persona_personas__persona_id__pin_post",
53
59
  "recommend_campaign_personas_campaigns_recommend_personas_post",
54
60
  "regenerate_campaign_document_campaigns__campaign_id__documents__document_id__regenerate_post",
61
+ "unlink_campaign_workflow_campaigns__campaign_id__workflows__link_id__delete",
62
+ "unlink_landing_page_from_campaign_campaigns__campaign_id__landing_pages__landing_page_id__delete",
63
+ "unlink_newsletter_campaign_newsletters__newsletter_id__campaign_delete",
55
64
  "unpin_icp_icps__icp_id__unpin_post",
56
65
  "unpin_persona_personas__persona_id__unpin_post",
57
66
  "update_campaign_campaigns__campaign_id__patch",
@@ -307,6 +316,27 @@
307
316
  "write",
308
317
  "destructive"
309
318
  ]
319
+ },
320
+ {
321
+ "name": "CampaignLinksTab",
322
+ "file": "components/campaign-links.tsx",
323
+ "purpose": "The landing pages, workflows and newsletters tied to one campaign: each row opens the record in your app, landing pages and newsletters can be linked and unlinked (workflows only unlinked, the API has no link operation). Use as an extraDetailTabs entry of CampaignsScreen.",
324
+ "operations": [
325
+ "list_campaign_landing_pages_campaigns__campaign_id__landing_pages_get",
326
+ "link_landing_page_to_campaign_campaigns__campaign_id__landing_pages_post",
327
+ "unlink_landing_page_from_campaign_campaigns__campaign_id__landing_pages__landing_page_id__delete",
328
+ "list_landing_pages_landing_pages_get",
329
+ "list_campaign_workflows_campaigns__campaign_id__workflows_get",
330
+ "unlink_campaign_workflow_campaigns__campaign_id__workflows__link_id__delete",
331
+ "list_newsletters_newsletters_get",
332
+ "link_newsletter_campaign_newsletters__newsletter_id__campaign_put",
333
+ "unlink_newsletter_campaign_newsletters__newsletter_id__campaign_delete"
334
+ ],
335
+ "tiers": [
336
+ "read",
337
+ "write",
338
+ "destructive"
339
+ ]
310
340
  }
311
341
  ],
312
342
  "hooks": [
@@ -378,7 +408,10 @@
378
408
  "campaigns:launch",
379
409
  "campaigns:read",
380
410
  "campaigns:run",
381
- "campaigns:write"
411
+ "campaigns:write",
412
+ "newsletter:delete",
413
+ "newsletter:read",
414
+ "newsletter:write"
382
415
  ],
383
416
  "defaults": [
384
417
  {
@@ -86,7 +86,7 @@ function Editor({ campaignId, doc, onChanged, onClosed }: { campaignId: string;
86
86
  {full.data ? (
87
87
  <>
88
88
  <Textarea aria-label="Document content" className="min-h-48 font-mono text-sm" value={body} onChange={(e) => setContent(e.target.value)} />
89
- {tooBig ? <p role="alert" className="text-sm text-red-700">This document is over 5 MiB and cannot be saved through the proxy.</p> : null}
89
+ {tooBig ? <p role="alert" className="text-sm text-error-foreground">This document is over 5 MiB and cannot be saved through the proxy.</p> : null}
90
90
  <div className="flex gap-2">
91
91
  <Button disabled={content === null || tooBig || poll.polling} onClick={() => setPending({ kind: "save" })}>Save changes</Button>
92
92
  <Button variant="outline" disabled={poll.polling} onClick={() => setPending({ kind: "regenerate" })}>Rewrite with AI</Button>
@@ -34,6 +34,7 @@
34
34
  import { useEffect, useState } from "react";
35
35
  import { Button, Skeleton } from "@graph8/sdk/ui";
36
36
  import { ApiError } from "@/g8/components/api-error";
37
+ import { FirstNotice } from "@/g8/components/paging-notices";
37
38
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
38
39
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
39
40
  import { useG8Query } from "@/g8/hooks/use-g8-query";
@@ -55,7 +56,7 @@ interface Mailbox { id: string | number; email?: string | null; connection_statu
55
56
 
56
57
  function Check({ ok, children, detail }: { ok: boolean | undefined; children: string; detail?: string }) {
57
58
  return (
58
- <li className={ok ? "text-green-700" : "text-amber-800"}>
59
+ <li className={ok ? "text-positive-foreground" : "text-warning-foreground"}>
59
60
  <span aria-hidden>{ok ? "Ready: " : "Missing: "}</span>{children}
60
61
  {!ok && detail ? <span className="block pl-4 text-xs text-muted-foreground">{detail}</span> : null}
61
62
  </li>
@@ -64,11 +65,14 @@ function Check({ ok, children, detail }: { ok: boolean | undefined; children: st
64
65
 
65
66
  const day = (d: Date) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
66
67
 
68
+ /** The mailboxes API has no paging: a full read is announced under the sender list. */
69
+ const MAILBOX_LIMIT = 200;
70
+
67
71
  export function CampaignLaunchStep({ campaignId, audienceSize, onLaunched, className }: CampaignLaunchStepProps) {
68
72
  const brand = useBrand();
69
73
  const campaign = useG8Query((signal) => g8Get(`/campaigns/${campaignId}/full`, undefined, { signal }), [campaignId]);
70
74
  const generation = useG8Query((signal) => g8Get(`/campaigns/${campaignId}/status`, undefined, { signal }), [campaignId]);
71
- const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: 200 }, { signal }), []);
75
+ const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: MAILBOX_LIMIT }, { signal }), []);
72
76
  const verdict = useG8Query((signal) => g8Get("/deliverability/safe-to-send", undefined, { signal }), []);
73
77
  const [chosen, setChosen] = useState<Set<string> | null>(null);
74
78
  const [confirm, setConfirm] = useState(false);
@@ -136,20 +140,20 @@ export function CampaignLaunchStep({ campaignId, audienceSize, onLaunched, class
136
140
  if (result) {
137
141
  const channels = (result.channels_attached as Array<{ email?: string }> | undefined) ?? [];
138
142
  return (
139
- <section aria-label="Launch result" className={`space-y-2 rounded-md border border-green-200 bg-green-50 p-4 ${className ?? ""}`}>
143
+ <section aria-label="Launch result" className={`space-y-2 rounded-md border border-positive-foreground/25 bg-positive p-4 ${className ?? ""}`}>
140
144
  <h3 className="font-medium">Campaign {String(result.status ?? "launched")}</h3>
141
145
  <p className="text-sm">{String(result.message ?? "")}</p>
142
146
  <p className="text-sm text-muted-foreground">
143
147
  Sequence {String(result.sequence_id ?? "")} with {String(result.steps_created ?? 0)} steps, sending from {channels.map((m) => m.email).join(", ") || "the chosen mailboxes"}.
144
148
  </p>
145
- {Array.isArray(result.warnings) && result.warnings.length > 0 ? <ul className="list-disc pl-5 text-sm text-amber-900">{result.warnings.map((w) => <li key={String(w)}>{String(w)}</li>)}</ul> : null}
149
+ {Array.isArray(result.warnings) && result.warnings.length > 0 ? <ul className="list-disc pl-5 text-sm text-warning-foreground">{result.warnings.map((w) => <li key={String(w)}>{String(w)}</li>)}</ul> : null}
146
150
  </section>
147
151
  );
148
152
  }
149
153
  return (
150
154
  <section aria-label="Launch" className={`space-y-3 ${className ?? ""}`}>
151
155
  <h3 className="font-medium">Ready to launch {String(c.name ?? "")}</h3>
152
- {alreadyLaunched ? <p role="status" className="rounded bg-green-50 p-2 text-sm text-green-900">This campaign has already been launched. Manage it in the sequencer.</p> : null}
156
+ {alreadyLaunched ? <p role="status" className="rounded bg-positive p-2 text-sm text-positive-foreground">This campaign has already been launched. Manage it in the sequencer.</p> : null}
153
157
  <ul className="space-y-1 text-sm">
154
158
  <Check ok={hasAudience}>An audience list is attached</Check>
155
159
  <Check ok={hasSteps}>The sequence has at least one step</Check>
@@ -157,23 +161,23 @@ export function CampaignLaunchStep({ campaignId, audienceSize, onLaunched, class
157
161
  <Check ok={senders.size > 0}>At least one active sending mailbox is selected</Check>
158
162
  </ul>
159
163
  {genError && !copy.ready ? (
160
- <p role="alert" className="rounded bg-red-50 p-2 text-sm text-red-800">Writing the campaign documents failed: {genError}</p>
164
+ <p role="alert" className="rounded bg-error p-2 text-sm text-error-foreground">Writing the campaign documents failed: {genError}</p>
161
165
  ) : null}
162
166
  {copy.known && !copy.ready ? (
163
167
  <div className="space-y-1">
164
168
  {writeCopy.error ? <ApiError error={writeCopy.error} title="Could not write the copy" /> : null}
165
- {copyPoll.done && !copyStateOf(copyPoll.status).ready && !writeCopy.loading ? <p role="status" className="text-sm text-amber-900">The copy did not finish. Try again, or write it in Campaign assets.</p> : null}
166
- {!copyPoll.polling && !copyPoll.done && copyPoll.attempts > 0 ? <p role="status" className="text-sm text-amber-900">Still writing. Check back in a minute.</p> : null}
169
+ {copyPoll.done && !copyStateOf(copyPoll.status).ready && !writeCopy.loading ? <p role="status" className="text-sm text-warning-foreground">The copy did not finish. Try again, or write it in Campaign assets.</p> : null}
170
+ {!copyPoll.polling && !copyPoll.done && copyPoll.attempts > 0 ? <p role="status" className="text-sm text-warning-foreground">Still writing. Check back in a minute.</p> : null}
167
171
  <Button variant="outline" loading={writeCopy.loading} disabled={writingCopy} onClick={() => setConfirmCopy(true)}>Write the email copy with AI</Button>
168
172
  </div>
169
173
  ) : null}
170
174
  {unsafe ? (
171
- <p role="alert" className="rounded bg-amber-50 p-2 text-sm text-amber-900">
175
+ <p role="alert" className="rounded bg-warning p-2 text-sm text-warning-foreground">
172
176
  Deliverability warning: {typeof v.reason === "string" ? v.reason : "sending is not recommended right now."}
173
177
  </p>
174
178
  ) : null}
175
179
  {held.length > 0 ? (
176
- <div role="status" className="rounded bg-amber-50 p-2 text-sm text-amber-900">
180
+ <div role="status" className="rounded bg-warning p-2 text-sm text-warning-foreground">
177
181
  <p>New mailboxes send nothing for their first {WARMUP_HOLD_DAYS} days while they warm up. The campaign goes live, but no email goes out from:</p>
178
182
  <ul className="list-disc pl-5">{held.map(({ m, until }) => <li key={m.id}>{m.email ?? `Mailbox ${m.id}`} until {day(until as Date)}</li>)}</ul>
179
183
  </div>
@@ -195,11 +199,12 @@ export function CampaignLaunchStep({ campaignId, audienceSize, onLaunched, class
195
199
  {m.email ?? `Mailbox ${m.id}`}
196
200
  </label>
197
201
  ))}
202
+ <FirstNotice shown={listOf<Mailbox>(mailboxes.data).length} limit={MAILBOX_LIMIT} noun="mailboxes" />
198
203
  </fieldset>
199
204
  {launch.error ? (
200
205
  <div className="space-y-1">
201
206
  <ApiError error={launch.error} title="The launch did not complete" />
202
- <p className="text-sm text-amber-900">Do not retry blindly. A sequence may already exist; check the campaign before launching again.</p>
207
+ <p className="text-sm text-warning-foreground">Do not retry blindly. A sequence may already exist; check the campaign before launching again.</p>
203
208
  </div>
204
209
  ) : null}
205
210
  <Button disabled={!ready} loading={launch.loading} onClick={() => setConfirm(true)}>Launch campaign</Button>