@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
@@ -25,6 +25,7 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
25
25
  import { ApiError } from "@/g8/components/api-error";
26
26
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
27
27
  import { DEFAULT_MAILBOX_CREDITS, GRAPH8_APP_MAILBOXES_PATH, listOf, newIdempotencyKey, objectOf } from "../lib/mailboxes-utils";
28
+ import { useProviderDomains } from "../lib/use-provider-domains";
28
29
  import type { DomainRow } from "../lib/mailboxes-utils";
29
30
  import { plural, pluralWord } from "@/g8/lib/plural";
30
31
  import { brandUrl, useBrand } from "@/g8/hooks/use-brand";
@@ -135,7 +136,7 @@ function ImapForm({ onConnected }: { onConnected?: (id: number | string) => void
135
136
  </Button>
136
137
  {connect.error ? <ApiError error={connect.error} onRetry={connect.reset} /> : null}
137
138
  {result ? (
138
- <p role="status" className="text-sm text-gray-700">
139
+ <p role="status" className="text-sm text-foreground">
139
140
  {result}
140
141
  </p>
141
142
  ) : null}
@@ -145,7 +146,7 @@ function ImapForm({ onConnected }: { onConnected?: (id: number | string) => void
145
146
 
146
147
  function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id: number | string) => void; domainId?: number | string }) {
147
148
  const brand = useBrand();
148
- const domains = useG8Query((signal) => g8Get("/provider-domains", { page: 1, limit: 100 }, { signal }), []);
149
+ const domains = useProviderDomains([]);
149
150
  const pricing = useG8Query((signal) => g8Get("/provider-domains/pricing", undefined, { signal }), []);
150
151
  const [domainId, setDomainId] = useState(initialDomainId != null ? String(initialDomainId) : "");
151
152
  const [usernames, setUsernames] = useState("");
@@ -170,19 +171,19 @@ function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id
170
171
  setDone(`Accepted ${plural(Number(res.count ?? names.length), "mailbox", "mailboxes")}; provisioning continues in the background.`);
171
172
  onPurchased?.(domainId);
172
173
  });
173
- const connected = listOf<DomainRow>(domains.data).filter((d) => d.provider_connected);
174
+ const connected = domains.items.filter((d) => d.provider_connected);
174
175
  const ready = domainId && names.length > 0 && person.first_name.trim() && person.last_name.trim();
175
176
 
176
177
  return (
177
178
  <div className="space-y-3">
178
179
  {domains.error ? <ApiError error={domains.error} onRetry={() => void domains.refetch()} /> : null}
179
180
  {!domains.loading && connected.length === 0 ? (
180
- <p className="text-sm text-gray-600">No connected domain yet. Buy and connect one with DomainPurchase first.</p>
181
+ <p className="text-sm text-muted-foreground">No connected domain yet. Buy and connect one with DomainPurchase first.</p>
181
182
  ) : (
182
183
  <div className="grid gap-2 sm:grid-cols-2">
183
184
  <select
184
185
  aria-label="Domain"
185
- className="h-9 rounded-md border border-gray-300 px-2 text-sm"
186
+ className="h-9 rounded-md border border-input px-2 text-sm"
186
187
  value={domainId}
187
188
  onChange={(e) => setDomainId(e.target.value)}
188
189
  >
@@ -195,7 +196,7 @@ function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id
195
196
  </select>
196
197
  <select
197
198
  aria-label="Platform"
198
- className="h-9 rounded-md border border-gray-300 px-2 text-sm"
199
+ className="h-9 rounded-md border border-input px-2 text-sm"
199
200
  value={platform}
200
201
  onChange={(e) => setPlatform(e.target.value as "GOOGLE" | "MICROSOFT")}
201
202
  >
@@ -231,7 +232,7 @@ function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id
231
232
  </Button>
232
233
  {buy.error ? <ApiError error={buy.error} onRetry={buy.reset} /> : null}
233
234
  {done ? (
234
- <p role="status" className="text-sm text-green-700">
235
+ <p role="status" className="text-sm text-positive-foreground">
235
236
  {done}
236
237
  </p>
237
238
  ) : null}
@@ -276,7 +277,7 @@ export function ConnectMailbox({ appUrl: appUrlProp, onConnected, onPurchased, i
276
277
  </div>
277
278
  {mode === "imap" ? <ImapForm onConnected={onConnected} /> : null}
278
279
  {mode === "oauth" ? (
279
- <div className="space-y-2 rounded border border-gray-200 p-3 text-sm">
280
+ <div className="space-y-2 rounded border border-border p-3 text-sm">
280
281
  <p>
281
282
  Google and Microsoft mailboxes connect through a sign-in screen that only runs inside the {brand.linkLabel} app. Connect it there, then
282
283
  come back: it appears in your mailbox list.
@@ -18,6 +18,7 @@ import { useState } from "react";
18
18
  import type { ReactNode } from "react";
19
19
  import { Button, Skeleton } from "@graph8/sdk/ui";
20
20
  import { ApiError } from "@/g8/components/api-error";
21
+ import { FirstNotice } from "@/g8/components/paging-notices";
21
22
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
22
23
  import { DetailPanel } from "@/g8/components/screen/detail-panel";
23
24
  import { KeyValueList } from "@/g8/components/screen/key-value-list";
@@ -27,6 +28,7 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
27
28
  import { useG8Query } from "@/g8/hooks/use-g8-query";
28
29
  import { g8Get, g8Post } from "@/g8/lib/g8-client";
29
30
  import { fieldItems, listOf, objectOf } from "../lib/mailboxes-utils";
31
+ import { useProviderDomains } from "../lib/use-provider-domains";
30
32
  import type { DomainRow, MailboxRow } from "../lib/mailboxes-utils";
31
33
 
32
34
  export interface DomainDetailProps {
@@ -46,13 +48,16 @@ export interface DomainDetailProps {
46
48
 
47
49
  type Pending = "connect" | "propagation";
48
50
 
51
+ /** The mailboxes API has no paging: a full read is announced where the mailboxes are listed. */
52
+ const MAILBOX_LIMIT = 200;
53
+
49
54
  export function DomainDetail({ domainId, onClose, onExpand, onCollapse, onChanged, onBuyMailboxes, onOpenMailbox, renderActions, className }: DomainDetailProps) {
50
55
  const [pending, setPending] = useState<Pending | null>(null);
51
56
  const [note, setNote] = useState<string | null>(null);
52
- const domains = useG8Query((signal) => g8Get("/provider-domains", { page: 1, limit: 100 }, { signal }), [domainId]);
53
- const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: 200 }, { signal }), [domainId]);
57
+ const domains = useProviderDomains([domainId]);
58
+ const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: MAILBOX_LIMIT }, { signal }), [domainId]);
54
59
 
55
- const row = listOf<DomainRow>(domains.data).find((d) => String(d.id) === String(domainId));
60
+ const row = domains.items.find((d) => String(d.id) === String(domainId));
56
61
  const run = useG8Mutation(async (p: Pending) => {
57
62
  if (p === "connect") {
58
63
  await g8Post(`/provider-domains/${domainId}/connect-provider`, {});
@@ -67,7 +72,7 @@ export function DomainDetail({ domainId, onClose, onExpand, onCollapse, onChange
67
72
  }, { onError: () => setPending(null) });
68
73
 
69
74
  if (domains.error) return <ApiError error={domains.error} title="Could not load the domain" onRetry={() => void domains.refetch()} className={className} />;
70
- if (domains.loading && !domains.data) return <Skeleton className={`h-48 w-full ${className ?? ""}`} />;
75
+ if (domains.loading && !domains.loaded) return <Skeleton className={`h-48 w-full ${className ?? ""}`} />;
71
76
  if (!row) return <p className={`p-4 text-sm ${className ?? ""}`}>This domain no longer exists.</p>;
72
77
 
73
78
  const onDomain = listOf<MailboxRow>(mailboxes.data).filter((m) => (m.email ?? "").toLowerCase().endsWith(`@${row.domain.toLowerCase()}`));
@@ -133,6 +138,7 @@ export function DomainDetail({ domainId, onClose, onExpand, onCollapse, onChange
133
138
  ))}
134
139
  </ul>
135
140
  )}
141
+ <FirstNotice shown={listOf<MailboxRow>(mailboxes.data).length} limit={MAILBOX_LIMIT} noun="mailboxes" />
136
142
  </SectionCard>
137
143
  ),
138
144
  },
@@ -24,9 +24,11 @@ import { g8Get, g8Post } from "@/g8/lib/g8-client";
24
24
  import { useG8Query } from "@/g8/hooks/use-g8-query";
25
25
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
26
26
  import { ApiError } from "@/g8/components/api-error";
27
+ import { FirstNotice } from "@/g8/components/paging-notices";
27
28
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
28
29
  import { EmptyState } from "@/g8/components/empty-state";
29
30
  import { DEFAULT_DOMAIN_CREDITS, listOf, newIdempotencyKey, objectOf, statusClass } from "../lib/mailboxes-utils";
31
+ import { useProviderDomains } from "../lib/use-provider-domains";
30
32
  import type { DomainRow } from "../lib/mailboxes-utils";
31
33
  import { plural } from "@/g8/lib/plural";
32
34
  import { useBrand } from "@/g8/hooks/use-brand";
@@ -66,7 +68,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
66
68
  const [note, setNote] = useState<string | null>(null);
67
69
  const idemKey = useRef<{ sig: string; key: string } | null>(null);
68
70
 
69
- const domains = useG8Query((signal) => g8Get("/provider-domains", { page: 1, limit: 50 }, { signal }), []);
71
+ const domains = useProviderDomains([]);
70
72
  const suggest = useG8Mutation(async () => {
71
73
  setNames(suggestionsOf(await g8Post("/provider-domains/generate-domains", { keyword: keyword.trim(), count: 10 })));
72
74
  setSelected([]);
@@ -106,7 +108,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
106
108
  const toggle = (d: string) => setSelected((s) => (s.includes(d) ? s.filter((x) => x !== d) : [...s, d]));
107
109
  const buyable = selected.filter((d) => avail[d]?.available);
108
110
  const contactOk = contact.first_name.trim() && contact.last_name.trim() && /\S+@\S+/.test(contact.email);
109
- const rows = listOf<DomainRow>(domains.data);
111
+ const rows = domains.items;
110
112
 
111
113
  const dialog = (() => {
112
114
  if (!pending) return null;
@@ -141,8 +143,8 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
141
143
 
142
144
  return (
143
145
  <div className={`space-y-5 ${className ?? ""}`}>
144
- <section className="space-y-2 rounded border border-gray-200 p-3">
145
- <h3 className="text-sm font-semibold">Find a domain</h3>
146
+ <section className="space-y-2 rounded border border-border p-3">
147
+ <h3 className="text-sm font-medium">Find a domain</h3>
146
148
  <form
147
149
  className="flex gap-2"
148
150
  onSubmit={(e) => {
@@ -165,7 +167,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
165
167
  {d}
166
168
  {avail[d] ? (
167
169
  <span
168
- className={`rounded px-1.5 text-xs ${avail[d].available ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`}
170
+ className={`rounded px-1.5 text-xs ${avail[d].available ? "bg-positive text-positive-foreground" : "bg-error text-error-foreground"}`}
169
171
  >
170
172
  {avail[d].available
171
173
  ? `available${avail[d].price != null ? ` ${avail[d].price}` : ""}`
@@ -189,8 +191,8 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
189
191
  </Button>
190
192
  </section>
191
193
 
192
- <section className="space-y-2 rounded border border-gray-200 p-3">
193
- <h3 className="text-sm font-semibold">Registrant</h3>
194
+ <section className="space-y-2 rounded border border-border p-3">
195
+ <h3 className="text-sm font-medium">Registrant</h3>
194
196
  <div className="grid gap-2 sm:grid-cols-3">
195
197
  <Input
196
198
  aria-label="First name"
@@ -217,7 +219,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
217
219
  </section>
218
220
 
219
221
  {note ? (
220
- <p role="status" className="text-sm text-gray-700">
222
+ <p role="status" className="text-sm text-foreground">
221
223
  {note}
222
224
  </p>
223
225
  ) : null}
@@ -226,18 +228,18 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
226
228
  <section aria-label="Your domains">
227
229
  {domains.error ? (
228
230
  <ApiError error={domains.error} onRetry={() => void domains.refetch()} />
229
- ) : domains.loading && !domains.data ? (
231
+ ) : domains.loading && !domains.loaded ? (
230
232
  <Skeleton className="h-16 w-full" />
231
233
  ) : rows.length === 0 ? (
232
234
  <EmptyState title="No sending domains yet" description={`Buy one above, or add a domain you already own in the ${brand.linkLabel} app.`} />
233
235
  ) : (
234
- <ul className="divide-y rounded border border-gray-200">
236
+ <ul className="divide-y rounded border border-border">
235
237
  {rows.map((d) => (
236
238
  <li key={d.id} className="flex flex-wrap items-center justify-between gap-2 px-3 py-2 text-sm">
237
239
  <span>
238
240
  <span className="font-medium">{d.domain}</span>{" "}
239
241
  <span className={`rounded px-1.5 text-xs ${statusClass(d.status)}`}>{d.status ?? "unknown"}</span>{" "}
240
- <span className="text-xs text-gray-500">{d.mailbox_count ?? 0} mailboxes</span>
242
+ <span className="text-xs text-muted-foreground">{d.mailbox_count ?? 0} mailboxes</span>
241
243
  </span>
242
244
  <span className="flex gap-1">
243
245
  {!d.provider_connected && !d.is_external ? (
@@ -254,6 +256,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
254
256
  ))}
255
257
  </ul>
256
258
  )}
259
+ <FirstNotice shown={rows.length} limit={rows.length} force={domains.truncated} noun="domains" />
257
260
  </section>
258
261
 
259
262
  {pending && dialog ? (
@@ -29,6 +29,7 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
29
29
  import { useG8Query } from "@/g8/hooks/use-g8-query";
30
30
  import { g8Delete, g8Get, g8Post } from "@/g8/lib/g8-client";
31
31
  import { fieldItems, listOf, objectOf } from "../lib/mailboxes-utils";
32
+ import { useProviderDomains } from "../lib/use-provider-domains";
32
33
  import type { DomainRow, MailboxRow } from "../lib/mailboxes-utils";
33
34
  import { WarmupHealth } from "./warmup-health";
34
35
  import { useBrand } from "@/g8/hooks/use-brand";
@@ -59,7 +60,7 @@ export function MailboxDetail({ mailboxId, tab, onTabChange, onClose, onExpand,
59
60
  const [pending, setPending] = useState<Pending | null>(null);
60
61
  const [testNote, setTestNote] = useState<string | null>(null);
61
62
  const q = useG8Query((signal) => g8Get(`/mailboxes/${mailboxId}`, undefined, { signal }), [mailboxId]);
62
- const domains = useG8Query((signal) => g8Get("/provider-domains", { page: 1, limit: 100 }, { signal }), []);
63
+ const domains = useProviderDomains([]);
63
64
 
64
65
  const test = useG8Mutation(async () => {
65
66
  const r = objectOf(await g8Post(`/mailboxes/${mailboxId}/test-connection`, {})) as TestResult;
@@ -84,7 +85,7 @@ export function MailboxDetail({ mailboxId, tab, onTabChange, onClose, onExpand,
84
85
  const raw = objectOf(q.data);
85
86
  const m = { id: String(mailboxId), email: null, ...raw } as MailboxRow;
86
87
  const domain = (m.email ?? "").split("@")[1]?.toLowerCase();
87
- const domainRow = listOf<DomainRow>(domains.data).find((d) => d.domain?.toLowerCase() === domain);
88
+ const domainRow = domains.items.find((d) => d.domain?.toLowerCase() === domain);
88
89
  const status = m.connection_status ?? "unknown";
89
90
  const name = m.email ?? `Mailbox ${mailboxId}`;
90
91
 
@@ -22,9 +22,13 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
22
22
  import { ApiError } from "@/g8/components/api-error";
23
23
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
24
24
  import { EmptyState } from "@/g8/components/empty-state";
25
+ import { FirstNotice } from "@/g8/components/paging-notices";
25
26
  import { listOf, objectOf, statusClass } from "../lib/mailboxes-utils";
26
27
  import type { MailboxRow } from "../lib/mailboxes-utils";
27
28
 
29
+ /** The read cap. The mailboxes API has no paging, so a full read is announced ("Showing the first 200"). */
30
+ const MAILBOX_LIMIT = 200;
31
+
28
32
  export interface MailboxListProps {
29
33
  includeArchived?: boolean;
30
34
  /** Called when a row is opened (for example to show WarmupHealth for it). */
@@ -47,7 +51,7 @@ export function MailboxList({ includeArchived = false, onSelect, emptyAction, cl
47
51
  const [tests, setTests] = useState<Record<string, string>>({});
48
52
  const [testing, setTesting] = useState<ReadonlySet<string>>(new Set());
49
53
  const list = useG8Query(
50
- (signal) => g8Get("/mailboxes", { limit: 200, include_archived: includeArchived }, { signal }),
54
+ (signal) => g8Get("/mailboxes", { limit: MAILBOX_LIMIT, include_archived: includeArchived }, { signal }),
51
55
  [includeArchived],
52
56
  );
53
57
 
@@ -114,13 +118,13 @@ export function MailboxList({ includeArchived = false, onSelect, emptyAction, cl
114
118
  <button type="button" className="text-left font-medium hover:underline" onClick={() => onSelect?.(m)}>
115
119
  {m.email ?? `Mailbox ${m.id}`}
116
120
  </button>
117
- <div className="text-xs text-gray-500">
121
+ <div className="text-xs text-muted-foreground">
118
122
  {m.display_name ?? ""}
119
123
  {m.provider ? ` - ${m.provider}` : ""}
120
124
  {m.is_archived ? " - archived" : ""}
121
125
  </div>
122
126
  {tests[m.id] ? (
123
- <div role="status" className="text-xs text-gray-700">
127
+ <div role="status" className="text-xs text-foreground">
124
128
  {tests[m.id]}
125
129
  </div>
126
130
  ) : null}
@@ -163,6 +167,7 @@ export function MailboxList({ includeArchived = false, onSelect, emptyAction, cl
163
167
  ))}
164
168
  </TableBody>
165
169
  </Table>
170
+ <FirstNotice shown={rows.length} limit={MAILBOX_LIMIT} noun="mailboxes" />
166
171
  {pending ? (
167
172
  <ConfirmSpendDialog
168
173
  open
@@ -38,7 +38,9 @@ import { useUrlState } from "@/g8/hooks/use-url-state";
38
38
  import { useG8Query } from "@/g8/hooks/use-g8-query";
39
39
  import { g8Get } from "@/g8/lib/g8-client";
40
40
  import { listOf, objectOf } from "../lib/mailboxes-utils";
41
+ import { useProviderDomains } from "../lib/use-provider-domains";
41
42
  import type { DomainRow, MailboxRow } from "../lib/mailboxes-utils";
43
+ import { FirstNotice } from "@/g8/components/paging-notices";
42
44
  import { ConnectMailbox } from "./connect-mailbox";
43
45
  import { DomainPurchase } from "./domain-purchase";
44
46
  import { DomainDetail } from "./domain-detail";
@@ -83,11 +85,12 @@ export function fleetStats(summary: Record<string, unknown>): StatItem[] {
83
85
  }
84
86
 
85
87
  function DomainList({ version, selectedId, onOpen }: { version: number; selectedId: string; onOpen: (d: DomainRow) => void }) {
86
- const q = useG8Query((signal) => g8Get("/provider-domains", { page: 1, limit: 100 }, { signal }), [version]);
87
- const rows = listOf<DomainRow>(q.data);
88
- if (q.loading && !q.data) return <Skeleton className="h-24 w-full" />;
88
+ const q = useProviderDomains([version]);
89
+ const rows = q.items;
90
+ if (q.loading && !q.loaded) return <Skeleton className="h-24 w-full" />;
89
91
  if (rows.length === 0) return null;
90
92
  return (
93
+ <>
91
94
  <ul aria-label="Your domains" className="divide-y rounded-md border">
92
95
  {rows.map((d) => (
93
96
  <li key={d.id} className={`flex items-center justify-between px-3 py-2 text-sm ${String(d.id) === selectedId ? "bg-muted" : ""}`}>
@@ -99,6 +102,8 @@ function DomainList({ version, selectedId, onOpen }: { version: number; selected
99
102
  </li>
100
103
  ))}
101
104
  </ul>
105
+ <FirstNotice shown={rows.length} limit={rows.length} force={q.truncated} noun="domains" />
106
+ </>
102
107
  );
103
108
  }
104
109
 
@@ -81,29 +81,29 @@ function Fleet() {
81
81
  {safe === false ? "Not safe to send right now" : safe === true ? "Safe to send" : "Send status unknown"}
82
82
  </p>
83
83
  {verdict.error ? (
84
- <p className="text-sm text-amber-900">
84
+ <p className="text-sm text-warning-foreground">
85
85
  The deliverability check failed, so sending is not confirmed safe. Retry before you send.
86
86
  </p>
87
87
  ) : null}
88
- {typeof v.reason === "string" ? <p className="text-sm text-gray-700">{v.reason}</p> : null}
88
+ {typeof v.reason === "string" ? <p className="text-sm text-foreground">{v.reason}</p> : null}
89
89
  </div>
90
90
  <dl className="grid grid-cols-2 gap-2 sm:grid-cols-4">
91
91
  {scalars(objectOf(summary.data))
92
92
  .filter(([k]) => k !== "safe to send")
93
93
  .map(([k, val]) => (
94
- <div key={k} className="rounded border border-gray-200 p-2">
95
- <dt className="text-xs capitalize text-gray-500">{k}</dt>
96
- <dd className="text-lg font-semibold">{val}</dd>
94
+ <div key={k} className="rounded border border-border p-2">
95
+ <dt className="text-xs capitalize text-muted-foreground">{k}</dt>
96
+ <dd className="text-lg font-medium">{val}</dd>
97
97
  </div>
98
98
  ))}
99
99
  </dl>
100
100
  {rows.length > 0 ? (
101
- <ul className="divide-y rounded border border-gray-200" aria-label="Degraded mailboxes">
101
+ <ul className="divide-y rounded border border-border" aria-label="Degraded mailboxes">
102
102
  {rows.map((r, i) => (
103
103
  <li key={String(r.id ?? i)} className="flex items-center justify-between px-3 py-2 text-sm">
104
104
  <span>
105
105
  {r.email ?? r.mailbox_email ?? `Mailbox ${r.id}`}
106
- <span className="ml-2 text-xs text-gray-500">{r.reason ?? r.reasons?.join(", ")}</span>
106
+ <span className="ml-2 text-xs text-muted-foreground">{r.reason ?? r.reasons?.join(", ")}</span>
107
107
  </span>
108
108
  <span className={`rounded px-2 py-0.5 text-xs ${statusClass(r.severity ?? r.status)}`}>
109
109
  {r.severity ?? r.status ?? "watch"}
@@ -134,7 +134,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
134
134
  const w = warm.data?.data;
135
135
  const a = w?.analytics;
136
136
  return (
137
- <section className="space-y-3 rounded border border-gray-200 p-3" aria-label="Warmup">
137
+ <section className="space-y-3 rounded border border-border p-3" aria-label="Warmup">
138
138
  <div className="flex items-center justify-between">
139
139
  <h3 className="font-medium">
140
140
  Warmup {w?.warmup_enabled ? "is on" : "is off"}
@@ -155,7 +155,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
155
155
  </div>
156
156
  </div>
157
157
  {w?.error ? (
158
- <p role="alert" className="text-sm text-amber-800">
158
+ <p role="alert" className="text-sm text-warning-foreground">
159
159
  The warmup provider did not answer: {w.error}
160
160
  </p>
161
161
  ) : null}
@@ -163,22 +163,22 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
163
163
  <>
164
164
  <dl className="grid grid-cols-2 gap-2 sm:grid-cols-4 text-sm">
165
165
  <div>
166
- <dt className="text-xs text-gray-500">Sent</dt>
167
- <dd className="font-semibold">{a.total_sent ?? 0}</dd>
166
+ <dt className="text-xs text-muted-foreground">Sent</dt>
167
+ <dd className="font-medium">{a.total_sent ?? 0}</dd>
168
168
  </div>
169
169
  <div>
170
- <dt className="text-xs text-gray-500">Inbox rate</dt>
171
- <dd className="font-semibold">
170
+ <dt className="text-xs text-muted-foreground">Inbox rate</dt>
171
+ <dd className="font-medium">
172
172
  {a.inbox_rate != null ? `${Math.round(a.inbox_rate <= 1 ? a.inbox_rate * 100 : a.inbox_rate)}%` : "-"}
173
173
  </dd>
174
174
  </div>
175
175
  <div>
176
- <dt className="text-xs text-gray-500">Spam</dt>
177
- <dd className="font-semibold">{a.total_spam ?? 0}</dd>
176
+ <dt className="text-xs text-muted-foreground">Spam</dt>
177
+ <dd className="font-medium">{a.total_spam ?? 0}</dd>
178
178
  </div>
179
179
  <div>
180
- <dt className="text-xs text-gray-500">Reputation</dt>
181
- <dd className="font-semibold">{a.reputation ?? "-"}</dd>
180
+ <dt className="text-xs text-muted-foreground">Reputation</dt>
181
+ <dd className="font-medium">{a.reputation ?? "-"}</dd>
182
182
  </div>
183
183
  </dl>
184
184
  {(a.daily_stats ?? []).length > 0 ? (
@@ -187,7 +187,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
187
187
  <span
188
188
  key={d.date}
189
189
  title={`${d.date}: ${d.sent} sent, ${d.inbox} inbox`}
190
- className="w-2 rounded-t bg-blue-500"
190
+ className="w-2 rounded-t bg-foreground/70"
191
191
  style={{ height: `${Math.max(4, Math.min(100, d.sent * 4))}%` }}
192
192
  />
193
193
  ))}
@@ -195,7 +195,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
195
195
  ) : null}
196
196
  </>
197
197
  ) : (
198
- <p className="text-sm text-gray-500">No warmup analytics yet.</p>
198
+ <p className="text-sm text-muted-foreground">No warmup analytics yet.</p>
199
199
  )}
200
200
  <Button size="sm" variant="outline" onClick={() => setConfirm(true)}>
201
201
  Run placement test
@@ -203,7 +203,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
203
203
  {toggle.error ? <ApiError error={toggle.error} onRetry={toggle.reset} /> : null}
204
204
  {placement.error ? <ApiError error={placement.error} onRetry={placement.reset} /> : null}
205
205
  {note ? (
206
- <p role="status" className="text-sm text-green-700">
206
+ <p role="status" className="text-sm text-positive-foreground">
207
207
  {note}
208
208
  </p>
209
209
  ) : null}
@@ -53,10 +53,10 @@ export function objectOf(envelope: unknown): Record<string, unknown> {
53
53
  /** Tailwind classes for a connection or health state. */
54
54
  export function statusClass(status: string | null | undefined): string {
55
55
  const s = (status ?? "").toLowerCase();
56
- if (["active", "connected", "healthy", "completed", "receiving"].includes(s)) return "bg-green-100 text-green-800";
57
- if (["paused", "watch", "warming", "pending", "connecting", "provisioning"].includes(s)) return "bg-amber-100 text-amber-800";
58
- if (s) return "bg-red-100 text-red-800";
59
- return "bg-gray-100 text-gray-700";
56
+ if (["active", "connected", "healthy", "completed", "receiving"].includes(s)) return "bg-positive text-positive-foreground";
57
+ if (["paused", "watch", "warming", "pending", "connecting", "provisioning"].includes(s)) return "bg-warning text-warning-foreground";
58
+ if (s) return "bg-error text-error-foreground";
59
+ return "bg-muted text-foreground";
60
60
  }
61
61
 
62
62
  /** Scalar fields of an open-shaped object, for a summary grid. */
@@ -0,0 +1,23 @@
1
+ "use client";
2
+
3
+ /**
4
+ * useProviderDomains: every sending domain of the workspace. The domains API pages (`page`, `limit`), so this reads page
5
+ * after page (100 per page, up to 20 pages) instead of one capped call; `truncated` says the page cap was reached.
6
+ *
7
+ * SDK operations: GET /provider-domains?page&limit (providerDomains.listProviderDomains) read
8
+ */
9
+ import { g8Get } from "@/g8/lib/g8-client";
10
+ import { useAllPages } from "@/g8/hooks/use-all-pages";
11
+ import { hasNextOf } from "@/g8/lib/paging";
12
+ import { listOf } from "./mailboxes-utils";
13
+ import type { DomainRow } from "./mailboxes-utils";
14
+
15
+ export const DOMAIN_PAGE = 100;
16
+
17
+ export function useProviderDomains(deps: readonly unknown[] = [], enabled = true) {
18
+ return useAllPages<DomainRow>(async (page, signal) => {
19
+ const env = await g8Get("/provider-domains", { page, limit: DOMAIN_PAGE }, { signal });
20
+ const rows = listOf<DomainRow>(env);
21
+ return { rows, hasNext: hasNextOf(env, DOMAIN_PAGE, rows.length) };
22
+ }, deps, { enabled });
23
+ }
@@ -133,3 +133,15 @@ Tiers: `read` and `write` need no confirmation; `billable`, `external` and `dest
133
133
  | `newsletter.unsubscribeSubscriber` | `PATCH /api/v1/newsletters/{newsletter_id}/subscribers/{subscriber_id}/unsubscribe` | external | `newsletter:run` | SubscriberTable |
134
134
  | `newsletter.deleteSubscriber` | `DELETE /api/v1/newsletters/{newsletter_id}/subscribers/{subscriber_id}` | destructive | `newsletter:delete` | SubscriberTable |
135
135
  | `newsletter.getIssueAnalyticsKpis` | `GET /api/v1/newsletters/{newsletter_id}/issues/{issue_id}/analytics/kpis` | read | `newsletter:read` | NewsletterStats |
136
+
137
+ ## Depth added (C7c)
138
+
139
+ New tabs on `NewsletterScreen`: **Templates**, **Lists**, **Settings**; issues gain a **History** tab; a subscriber shows **Activity on an issue**.
140
+
141
+ - `NewsletterSettings`: rename and goal, sender and opt-in policy (only changed fields are sent), sending domain (start verification is an external confirm, DNS records are listed, status check), resume a paused newsletter (external confirm), mark completed.
142
+ - `NewsletterListSyncs`: turn a list sync on (the consent statement is shown and sent), run it now, stop it (destructive). Lists page with Load more.
143
+ - `NewsletterTemplates`: use a template for a draft issue, approve or archive, rename, delete (destructive), write one with AI (billable confirm).
144
+ - `IssueVersions`: version history with restore (drafts only, second click) and Duplicate (external confirm).
145
+ - `SubscriberTimeline`: delivered, opened and clicked for one subscriber on one sent issue.
146
+
147
+ Not covered (and why): `rewriteIssueText`, `setIssueText`, `setIssueMediaEdit`, `setIssueButtonAction` and the template equivalents need element ids the HTML editor does not expose; `linkNewsletterCampaign` needs a campaign picker (owned by the pickers PR); `cloneTemplate`, `createTemplateFromLayout` and `batchTemplateGenerationStatus` are template-gallery flows; `getNewsletterSubscriberVerification` status is not shown (verify still runs and reports through the existing flow); `getNewsletterIssueConversation` is the hosted editor's chat. The subscriber and list pickers keep their existing paging.