@graph8/sdk 0.362.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 (615) hide show
  1. package/README.md +33 -10
  2. package/blocks/_foundation/README.md +78 -1
  3. package/blocks/_foundation/block.json +123 -6
  4. package/blocks/_foundation/components/api-error.tsx +9 -7
  5. package/blocks/_foundation/components/brand-provider.tsx +23 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  7. package/blocks/_foundation/components/confirmed-action.tsx +90 -0
  8. package/blocks/_foundation/components/connect-graph8.tsx +280 -0
  9. package/blocks/_foundation/components/empty-state.tsx +5 -2
  10. package/blocks/_foundation/components/list-select.tsx +112 -0
  11. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  12. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  13. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  14. package/blocks/_foundation/components/screen/index.ts +2 -0
  15. package/blocks/_foundation/components/screen/record-link.tsx +76 -0
  16. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  17. package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
  18. package/blocks/_foundation/components/screen/time-series-chart.tsx +15 -97
  19. package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
  20. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  21. package/blocks/_foundation/hooks/use-brand.ts +52 -0
  22. package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
  23. package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
  24. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  25. package/blocks/_foundation/hooks/use-paginated.ts +120 -0
  26. package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
  27. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  28. package/blocks/_foundation/lib/g8-cache.ts +454 -0
  29. package/blocks/_foundation/lib/g8-client.ts +1 -1
  30. package/blocks/_foundation/lib/paging.ts +57 -0
  31. package/blocks/_foundation/lib/plural.ts +23 -0
  32. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  33. package/blocks/_foundation/server/connect-route.ts +15 -0
  34. package/blocks/_foundation/server/connect.ts +127 -0
  35. package/blocks/_foundation/server/local-key.ts +70 -0
  36. package/blocks/_foundation/server/proxy.ts +103 -3
  37. package/blocks/_foundation/server/route.ts +16 -4
  38. package/blocks/agents/README.md +93 -2
  39. package/blocks/agents/block.json +343 -2
  40. package/blocks/agents/components/agent-activity.tsx +112 -0
  41. package/blocks/agents/components/agent-approvals.tsx +135 -0
  42. package/blocks/agents/components/agent-builder.tsx +29 -10
  43. package/blocks/agents/components/agent-conversations.tsx +143 -0
  44. package/blocks/agents/components/agent-knowledge-panel.tsx +4 -3
  45. package/blocks/agents/components/agent-operators.tsx +164 -0
  46. package/blocks/agents/components/agent-run-detail.tsx +119 -0
  47. package/blocks/agents/components/agent-runs.tsx +19 -10
  48. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  49. package/blocks/agents/components/agent-test-history.tsx +121 -0
  50. package/blocks/agents/components/agents-screen.tsx +226 -0
  51. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  52. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  53. package/blocks/agents/components/from-template-button.tsx +65 -0
  54. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  55. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  56. package/blocks/agents/components/knowledge-form.tsx +69 -0
  57. package/blocks/agents/components/name-dialog.tsx +32 -0
  58. package/blocks/agents/components/receptionist-button.tsx +46 -0
  59. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  60. package/blocks/agents/components/skill-editor.tsx +147 -0
  61. package/blocks/agents/components/skill-library.tsx +111 -0
  62. package/blocks/agents/lib/agent-types.ts +7 -1
  63. package/blocks/agents/lib/facts.tsx +13 -0
  64. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  65. package/blocks/agents/lib/load-more.tsx +141 -0
  66. package/blocks/agents/lib/skill-utils.ts +91 -0
  67. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  68. package/blocks/analytics/README.md +13 -0
  69. package/blocks/analytics/block.json +45 -1
  70. package/blocks/analytics/components/analytics-screen.tsx +222 -0
  71. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  72. package/blocks/analytics/components/leaderboard.tsx +14 -7
  73. package/blocks/analytics/components/report-builder.tsx +38 -11
  74. package/blocks/analytics/components/report-detail.tsx +84 -0
  75. package/blocks/analytics/components/trend-chart.tsx +62 -0
  76. package/blocks/appointments/README.md +6 -0
  77. package/blocks/appointments/block.json +33 -2
  78. package/blocks/appointments/components/availability-editor.tsx +17 -5
  79. package/blocks/appointments/components/booking-page.tsx +2 -2
  80. package/blocks/appointments/components/event-type-editor.tsx +2 -1
  81. package/blocks/appointments/components/event-type-related.tsx +4 -2
  82. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  83. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  84. package/blocks/appointments/components/meeting-list.tsx +38 -18
  85. package/blocks/appointments/components/meetings-screen.tsx +7 -4
  86. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  87. package/blocks/appointments/components/scheduling-screen.tsx +2 -1
  88. package/blocks/block.schema.json +18 -0
  89. package/blocks/block.types.ts +17 -0
  90. package/blocks/campaigns/README.md +41 -11
  91. package/blocks/campaigns/block.json +105 -42
  92. package/blocks/campaigns/components/campaign-assets.tsx +17 -6
  93. package/blocks/campaigns/components/campaign-launch-step.tsx +114 -26
  94. package/blocks/campaigns/components/campaign-list.tsx +7 -3
  95. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  96. package/blocks/campaigns/components/campaign-wizard.tsx +52 -14
  97. package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
  98. package/blocks/campaigns/components/icp-detail.tsx +163 -0
  99. package/blocks/campaigns/components/icp-editor.tsx +22 -17
  100. package/blocks/campaigns/components/persona-detail.tsx +181 -0
  101. package/blocks/campaigns/components/persona-editor.tsx +19 -11
  102. package/blocks/campaigns/lib/campaigns-utils.ts +138 -5
  103. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
  104. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
  105. package/blocks/coach/README.md +10 -0
  106. package/blocks/coach/block.json +16 -1
  107. package/blocks/coach/components/call-scorecard.tsx +7 -7
  108. package/blocks/coach/components/coach-screen.tsx +222 -0
  109. package/blocks/company-intelligence/README.md +132 -0
  110. package/blocks/company-intelligence/block.json +318 -0
  111. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  112. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  113. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  114. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  115. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  116. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  117. package/blocks/company-intelligence/components/research-run.tsx +161 -0
  118. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  119. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  120. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  121. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  122. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  123. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  124. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  125. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  126. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  127. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  128. package/blocks/copilot/README.md +13 -0
  129. package/blocks/copilot/block.json +14 -1
  130. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  131. package/blocks/copilot/components/copilot-panel.tsx +14 -5
  132. package/blocks/copilot/components/copilot-screen.tsx +148 -0
  133. package/blocks/copilot/components/copilot-team-chat.tsx +13 -6
  134. package/blocks/crm/README.md +34 -0
  135. package/blocks/crm/block.json +107 -2
  136. package/blocks/crm/components/companies-screen.tsx +359 -0
  137. package/blocks/crm/components/company-detail.tsx +10 -8
  138. package/blocks/crm/components/company-table.tsx +11 -4
  139. package/blocks/crm/components/contact-detail.tsx +10 -7
  140. package/blocks/crm/components/contact-lookup-link.tsx +44 -0
  141. package/blocks/crm/components/contact-table.tsx +10 -3
  142. package/blocks/crm/components/contacts-screen.tsx +377 -0
  143. package/blocks/crm/components/dedupe-review.tsx +13 -6
  144. package/blocks/crm/components/import-wizard.tsx +7 -9
  145. package/blocks/crm/components/list-select.tsx +20 -0
  146. package/blocks/crm/components/notes-panel.tsx +4 -4
  147. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  148. package/blocks/crm/components/record-form.tsx +6 -4
  149. package/blocks/crm/components/task-list.tsx +4 -4
  150. package/blocks/crm/components/tasks-screen.tsx +399 -0
  151. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  152. package/blocks/crm/hooks/use-custom-fields.ts +6 -1
  153. package/blocks/crm/hooks/use-page-query.ts +9 -1
  154. package/blocks/crm/lib/crm-types.ts +26 -2
  155. package/blocks/crm/lib/read-lists.ts +16 -0
  156. package/blocks/crm/lib/screen-parts.tsx +105 -0
  157. package/blocks/deals/README.md +24 -6
  158. package/blocks/deals/block.json +160 -5
  159. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  160. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  161. package/blocks/deals/components/deal-detail.tsx +25 -13
  162. package/blocks/deals/components/deal-filters.tsx +65 -0
  163. package/blocks/deals/components/deal-line-items.tsx +120 -0
  164. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  165. package/blocks/deals/components/deal-related.tsx +31 -10
  166. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  167. package/blocks/deals/components/deals-screen.tsx +66 -26
  168. package/blocks/deals/components/forecast-view.tsx +4 -4
  169. package/blocks/deals/components/pipeline-board.tsx +8 -6
  170. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  171. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  172. package/blocks/deals/components/stage-editor.tsx +5 -5
  173. package/blocks/deals/hooks/use-pipeline-deals.ts +21 -4
  174. package/blocks/deals/hooks/use-pipelines.ts +1 -1
  175. package/blocks/deals/lib/deal-extras.ts +156 -0
  176. package/blocks/dialer/README.md +45 -4
  177. package/blocks/dialer/block.json +84 -3
  178. package/blocks/dialer/components/call-contact-button.tsx +269 -0
  179. package/blocks/dialer/components/call-log-screen.tsx +208 -0
  180. package/blocks/dialer/components/call-log.tsx +23 -7
  181. package/blocks/dialer/components/call-queue.tsx +4 -4
  182. package/blocks/dialer/components/dialer-screen.tsx +361 -0
  183. package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
  184. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  185. package/blocks/dialer/components/live-calls-table.tsx +7 -3
  186. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  187. package/blocks/dialer/components/session-start-form.tsx +61 -12
  188. package/blocks/dialer/components/softphone.tsx +4 -2
  189. package/blocks/enrichment/README.md +29 -7
  190. package/blocks/enrichment/block.json +49 -9
  191. package/blocks/enrichment/components/bulk-enrich-panel.tsx +83 -29
  192. package/blocks/enrichment/components/email-verify-badge.tsx +31 -11
  193. package/blocks/enrichment/components/enrich-button.tsx +32 -9
  194. package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
  195. package/blocks/enrichment/components/enrichment-screen.tsx +341 -0
  196. package/blocks/enrichment/components/waterfall-config.tsx +66 -17
  197. package/blocks/enrichment/lib/enrichment-types.ts +39 -0
  198. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  199. package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
  200. package/blocks/forms/README.md +8 -0
  201. package/blocks/forms/block.json +139 -18
  202. package/blocks/forms/components/form-activity.tsx +71 -0
  203. package/blocks/forms/components/form-backfill.tsx +108 -0
  204. package/blocks/forms/components/form-builder.tsx +4 -2
  205. package/blocks/forms/components/form-embed.tsx +4 -2
  206. package/blocks/forms/components/form-groups.tsx +151 -0
  207. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  208. package/blocks/forms/components/form-route-history.tsx +72 -0
  209. package/blocks/forms/components/form-saved-views.tsx +81 -0
  210. package/blocks/forms/components/forms-screen.tsx +56 -19
  211. package/blocks/forms/components/more-rows.tsx +30 -0
  212. package/blocks/forms/components/submissions-export.tsx +67 -0
  213. package/blocks/forms/components/submissions-table.tsx +17 -6
  214. package/blocks/forms/lib/forms-extra.ts +67 -0
  215. package/blocks/inbox/README.md +32 -3
  216. package/blocks/inbox/block.json +29 -2
  217. package/blocks/inbox/components/inbox-list.tsx +7 -3
  218. package/blocks/inbox/components/inbox-screen.tsx +294 -27
  219. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  220. package/blocks/inbox/components/reply-composer.tsx +65 -9
  221. package/blocks/inbox/components/tag-and-assign.tsx +4 -2
  222. package/blocks/inbox/components/thread-badge.tsx +2 -2
  223. package/blocks/inbox/components/thread-view.tsx +1 -1
  224. package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
  225. package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
  226. package/blocks/inbox/lib/inbox-types.ts +47 -1
  227. package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
  228. package/blocks/integrations/README.md +63 -1
  229. package/blocks/integrations/block.json +234 -15
  230. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  231. package/blocks/integrations/components/connection-health.tsx +59 -0
  232. package/blocks/integrations/components/crm-detail.tsx +119 -0
  233. package/blocks/integrations/components/crm-records.tsx +103 -0
  234. package/blocks/integrations/components/field-mappings.tsx +61 -0
  235. package/blocks/integrations/components/integration-list.tsx +13 -8
  236. package/blocks/integrations/components/integrations-screen.tsx +152 -0
  237. package/blocks/integrations/components/migration-panel.tsx +133 -0
  238. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  239. package/blocks/integrations/components/sync-status.tsx +11 -9
  240. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  241. package/blocks/integrations/components/webhook-detail.tsx +179 -0
  242. package/blocks/integrations/components/webhook-manager.tsx +43 -25
  243. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  244. package/blocks/integrations/lib/crm-utils.ts +39 -0
  245. package/blocks/integrations/lib/facts.tsx +13 -0
  246. package/blocks/integrations/lib/integrations-utils.ts +11 -1
  247. package/blocks/integrations/lib/load-more.tsx +141 -0
  248. package/blocks/intent/README.md +14 -0
  249. package/blocks/intent/block.json +27 -1
  250. package/blocks/intent/components/account-intent-score.tsx +5 -5
  251. package/blocks/intent/components/intent-feed.tsx +13 -10
  252. package/blocks/intent/components/intent-keyword-manager.tsx +16 -12
  253. package/blocks/intent/components/intent-screen.tsx +265 -0
  254. package/blocks/intent/components/keyword-detail.tsx +124 -0
  255. package/blocks/intent/lib/intent-utils.ts +4 -4
  256. package/blocks/lifecycle/block.json +13 -8
  257. package/blocks/lifecycle/components/account-related.tsx +8 -1
  258. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  259. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  260. package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
  261. package/blocks/lifecycle/components/lifecycle-screen.tsx +3 -1
  262. package/blocks/lifecycle/lib/lifecycle.ts +1 -1
  263. package/blocks/lists/README.md +20 -1
  264. package/blocks/lists/block.json +69 -3
  265. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  266. package/blocks/lists/components/add-to-list-button.tsx +20 -9
  267. package/blocks/lists/components/list-picker.tsx +17 -3
  268. package/blocks/lists/components/list-table.tsx +12 -6
  269. package/blocks/lists/components/lists-screen.tsx +346 -0
  270. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  271. package/blocks/lists/components/suppression-manager.tsx +33 -8
  272. package/blocks/lists/components/suppressions-screen.tsx +264 -0
  273. package/blocks/mailboxes/README.md +17 -0
  274. package/blocks/mailboxes/block.json +69 -17
  275. package/blocks/mailboxes/components/connect-mailbox.tsx +32 -21
  276. package/blocks/mailboxes/components/domain-detail.tsx +148 -0
  277. package/blocks/mailboxes/components/domain-purchase.tsx +20 -14
  278. package/blocks/mailboxes/components/mailbox-detail.tsx +157 -0
  279. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  280. package/blocks/mailboxes/components/mailboxes-screen.tsx +228 -0
  281. package/blocks/mailboxes/components/warmup-health.tsx +24 -22
  282. package/blocks/mailboxes/lib/mailboxes-utils.ts +26 -5
  283. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  284. package/blocks/newsletter/README.md +12 -0
  285. package/blocks/newsletter/block.json +157 -24
  286. package/blocks/newsletter/components/issue-scheduler.tsx +4 -3
  287. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  288. package/blocks/newsletter/components/more-rows.tsx +30 -0
  289. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  290. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  291. package/blocks/newsletter/components/newsletter-screen.tsx +39 -14
  292. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  293. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  294. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  295. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  296. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  297. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  298. package/blocks/nurture/block.json +28 -14
  299. package/blocks/nurture/components/nurture-builder.tsx +13 -3
  300. package/blocks/nurture/components/nurture-enroll.tsx +12 -10
  301. package/blocks/nurture/components/nurture-form.tsx +27 -10
  302. package/blocks/nurture/components/nurture-screen.tsx +26 -11
  303. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  304. package/blocks/nurture/lib/nurture-types.ts +1 -1
  305. package/blocks/objects/README.md +18 -1
  306. package/blocks/objects/block.json +42 -21
  307. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  308. package/blocks/objects/components/object-not-found.tsx +3 -1
  309. package/blocks/objects/components/object-record-detail.tsx +10 -2
  310. package/blocks/objects/components/object-schema-editor.tsx +4 -2
  311. package/blocks/objects/components/object-table.tsx +13 -3
  312. package/blocks/objects/components/objects-screen.tsx +147 -0
  313. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  314. package/blocks/objects/lib/object-types.ts +1 -1
  315. package/blocks/pages/README.md +14 -0
  316. package/blocks/pages/block.json +140 -23
  317. package/blocks/pages/components/brand-kit-editor.tsx +5 -2
  318. package/blocks/pages/components/content-library.tsx +9 -4
  319. package/blocks/pages/components/more-rows.tsx +30 -0
  320. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  321. package/blocks/pages/components/page-analytics.tsx +176 -0
  322. package/blocks/pages/components/page-clone.tsx +99 -0
  323. package/blocks/pages/components/page-domains.tsx +167 -0
  324. package/blocks/pages/components/page-editor.tsx +1 -1
  325. package/blocks/pages/components/page-generation-status.tsx +48 -0
  326. package/blocks/pages/components/page-list.tsx +2 -1
  327. package/blocks/pages/components/page-tracking.tsx +137 -0
  328. package/blocks/pages/components/pages-screen.tsx +57 -16
  329. package/blocks/pages/components/seo-report.tsx +6 -1
  330. package/blocks/pages/lib/studio.ts +7 -0
  331. package/blocks/phone-numbers/README.md +74 -0
  332. package/blocks/phone-numbers/block.json +262 -0
  333. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  334. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  335. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  336. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  337. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  338. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  339. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  340. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  341. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  342. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  343. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  344. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  345. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  346. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  347. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  348. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  349. package/blocks/pickers/README.md +23 -0
  350. package/blocks/pickers/block.json +103 -0
  351. package/blocks/pickers/components/company-picker.tsx +90 -0
  352. package/blocks/pickers/components/contact-picker.tsx +131 -0
  353. package/blocks/pickers/components/member-picker.tsx +99 -0
  354. package/blocks/pickers/components/search-picker.tsx +98 -0
  355. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  356. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  357. package/blocks/pickers/lib/use-debounced.ts +13 -0
  358. package/blocks/quotes/README.md +13 -0
  359. package/blocks/quotes/block.json +65 -13
  360. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  361. package/blocks/quotes/components/product-picker.tsx +3 -1
  362. package/blocks/quotes/components/quote-builder.tsx +1 -1
  363. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  364. package/blocks/quotes/components/quote-list.tsx +5 -3
  365. package/blocks/quotes/components/quote-related.tsx +8 -4
  366. package/blocks/quotes/components/quote-viewer.tsx +7 -2
  367. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  368. package/blocks/radar/README.md +10 -0
  369. package/blocks/radar/block.json +41 -1
  370. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  371. package/blocks/radar/components/competitor-detail.tsx +117 -0
  372. package/blocks/radar/components/competitor-tracker.tsx +3 -1
  373. package/blocks/radar/components/radar-overview.tsx +4 -4
  374. package/blocks/radar/components/radar-report.tsx +4 -2
  375. package/blocks/radar/components/radar-screen.tsx +214 -0
  376. package/blocks/radar/components/report-detail.tsx +104 -0
  377. package/blocks/search/README.md +10 -0
  378. package/blocks/search/block.json +16 -1
  379. package/blocks/search/components/filter-builder.tsx +3 -3
  380. package/blocks/search/components/prospect-results.tsx +5 -3
  381. package/blocks/search/components/prospect-search-screen.tsx +235 -0
  382. package/blocks/search/components/saved-searches.tsx +4 -1
  383. package/blocks/search/hooks/use-filter-options.ts +20 -6
  384. package/blocks/search/lib/search-types.ts +2 -0
  385. package/blocks/sequencer/README.md +55 -9
  386. package/blocks/sequencer/block.json +126 -19
  387. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  388. package/blocks/sequencer/components/enroll-dialog.tsx +11 -5
  389. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  390. package/blocks/sequencer/components/sequence-builder.tsx +224 -60
  391. package/blocks/sequencer/components/sequence-detail.tsx +261 -0
  392. package/blocks/sequencer/components/sequence-enrollments.tsx +116 -0
  393. package/blocks/sequencer/components/sequence-list.tsx +34 -9
  394. package/blocks/sequencer/components/sequence-stats.tsx +11 -10
  395. package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
  396. package/blocks/sequencer/components/sequences-screen.tsx +162 -0
  397. package/blocks/sequencer/components/step-editor.tsx +14 -12
  398. package/blocks/sequencer/lib/sequencer-utils.ts +133 -10
  399. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
  400. package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
  401. package/blocks/social-ads/README.md +19 -0
  402. package/blocks/social-ads/block.json +233 -1
  403. package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
  404. package/blocks/social-ads/components/ads-screen.tsx +152 -0
  405. package/blocks/social-ads/components/audience-sync-detail.tsx +194 -0
  406. package/blocks/social-ads/components/audience-sync.tsx +17 -9
  407. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  408. package/blocks/social-ads/components/campaign-detail.tsx +138 -0
  409. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  410. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  411. package/blocks/social-ads/components/creative-detail.tsx +155 -0
  412. package/blocks/social-ads/components/creative-gallery.tsx +35 -7
  413. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  414. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  415. package/blocks/social-ads/components/post-composer.tsx +7 -3
  416. package/blocks/social-ads/components/post-detail.tsx +163 -0
  417. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  418. package/blocks/social-ads/components/social-performance.tsx +80 -0
  419. package/blocks/social-ads/components/social-post-list.tsx +11 -3
  420. package/blocks/social-ads/components/social-screen.tsx +187 -0
  421. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  422. package/blocks/surveys/README.md +91 -0
  423. package/blocks/surveys/block.json +299 -0
  424. package/blocks/surveys/components/account-nps.tsx +69 -0
  425. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  426. package/blocks/surveys/components/org-summary.tsx +82 -0
  427. package/blocks/surveys/components/sending-domains.tsx +113 -0
  428. package/blocks/surveys/components/survey-form.tsx +126 -0
  429. package/blocks/surveys/components/survey-responses.tsx +145 -0
  430. package/blocks/surveys/components/survey-results.tsx +72 -0
  431. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  432. package/blocks/surveys/components/survey-send.tsx +162 -0
  433. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  434. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  435. package/blocks/surveys/lib/surveys-types.ts +228 -0
  436. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  437. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  438. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  439. package/blocks/visitors/README.md +10 -0
  440. package/blocks/visitors/block.json +25 -1
  441. package/blocks/visitors/components/snippet-install.tsx +21 -10
  442. package/blocks/visitors/components/traffic-chart.tsx +4 -4
  443. package/blocks/visitors/components/visitor-company-card.tsx +26 -16
  444. package/blocks/visitors/components/visitor-detail.tsx +105 -0
  445. package/blocks/visitors/components/visitor-feed.tsx +16 -7
  446. package/blocks/visitors/components/visitors-screen.tsx +166 -0
  447. package/blocks/webchat/README.md +10 -0
  448. package/blocks/webchat/block.json +21 -7
  449. package/blocks/webchat/components/chat-widget-config.tsx +8 -5
  450. package/blocks/webchat/components/conversation-list.tsx +8 -4
  451. package/blocks/webchat/components/webchat-screen.tsx +245 -0
  452. package/blocks/workbench/README.md +11 -0
  453. package/blocks/workbench/block.json +48 -1
  454. package/blocks/workbench/components/column-recipe-editor.tsx +5 -3
  455. package/blocks/workbench/components/run-progress.tsx +1 -1
  456. package/blocks/workbench/components/workbench-grid.tsx +10 -4
  457. package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
  458. package/blocks/workbench/components/workbench-screen.tsx +202 -0
  459. package/blocks/workbench/components/workbench-sources.tsx +194 -0
  460. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  461. package/blocks/workflows/README.md +13 -0
  462. package/blocks/workflows/block.json +28 -9
  463. package/blocks/workflows/components/run-history.tsx +31 -9
  464. package/blocks/workflows/components/trigger-config.tsx +3 -1
  465. package/blocks/workflows/components/workflow-editor.tsx +4 -2
  466. package/blocks/workflows/components/workflow-list.tsx +13 -6
  467. package/blocks/workflows/components/workflows-screen.tsx +193 -0
  468. package/dist/cli.js +3109 -1977
  469. package/dist/errors.d.mts +34 -0
  470. package/dist/errors.d.ts +34 -0
  471. package/dist/errors.js +46 -0
  472. package/dist/errors.js.map +1 -0
  473. package/dist/errors.mjs +21 -0
  474. package/dist/errors.mjs.map +1 -0
  475. package/dist/index.browser.js +11 -5
  476. package/dist/index.browser.js.map +1 -1
  477. package/dist/index.browser.mjs +11 -5
  478. package/dist/index.browser.mjs.map +1 -1
  479. package/dist/index.d.mts +164 -25
  480. package/dist/index.d.ts +164 -25
  481. package/dist/index.js +11 -5
  482. package/dist/index.js.map +1 -1
  483. package/dist/index.mjs +11 -5
  484. package/dist/index.mjs.map +1 -1
  485. package/dist/react.browser.js +10 -4
  486. package/dist/react.browser.js.map +1 -1
  487. package/dist/react.browser.mjs +10 -4
  488. package/dist/react.browser.mjs.map +1 -1
  489. package/dist/react.d.mts +142 -5
  490. package/dist/react.d.ts +142 -5
  491. package/dist/react.js +10 -4
  492. package/dist/react.js.map +1 -1
  493. package/dist/react.mjs +10 -4
  494. package/dist/react.mjs.map +1 -1
  495. package/dist/types/blocks/add.d.ts +11 -4
  496. package/dist/types/blocks/brand.d.ts +122 -0
  497. package/dist/types/blocks/compose.d.ts +53 -0
  498. package/dist/types/blocks/create.d.ts +27 -20
  499. package/dist/types/blocks/doctor.d.ts +6 -1
  500. package/dist/types/blocks/flows.d.ts +7 -1
  501. package/dist/types/blocks/navfile.d.ts +35 -0
  502. package/dist/types/blocks/orgBrand.d.ts +34 -0
  503. package/dist/types/blocks/paths.d.ts +4 -0
  504. package/dist/types/blocks/prefixes.d.ts +37 -0
  505. package/dist/types/blocks/registry.d.ts +17 -0
  506. package/dist/types/blocks/screens.d.ts +53 -0
  507. package/dist/types/cli.d.ts +5 -0
  508. package/dist/types/errors.d.ts +33 -0
  509. package/dist/types/http.d.ts +2 -18
  510. package/dist/types/version.d.ts +2 -2
  511. package/docs/agents.md +15 -4
  512. package/docs/api/README.md +2 -2
  513. package/docs/api/by-path.md +1 -0
  514. package/docs/api/desk.d.ts +98 -2
  515. package/docs/api/desk.md +123 -1
  516. package/docs/api/inbox.d.ts +10 -2
  517. package/docs/api/inbox.md +10 -2
  518. package/docs/api/seo.d.ts +19 -1
  519. package/docs/api/seo.md +24 -1
  520. package/package.json +16 -2
  521. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  522. package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
  523. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  524. package/stacks/6sense.json +55 -17
  525. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  526. package/stacks/apollo/pages/companies.tsx.tmpl +33 -0
  527. package/stacks/apollo/pages/contacts.tsx.tmpl +51 -0
  528. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  529. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  530. package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
  531. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  532. package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
  533. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  534. package/stacks/apollo.json +147 -37
  535. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  536. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  537. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  538. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  539. package/stacks/calendly.json +64 -0
  540. package/stacks/clay/pages/companies.tsx.tmpl +31 -0
  541. package/stacks/clay/pages/contacts.tsx.tmpl +31 -0
  542. package/stacks/clay/pages/lists.tsx.tmpl +12 -0
  543. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  544. package/stacks/clay.json +73 -27
  545. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  546. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  547. package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
  548. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  549. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  550. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  551. package/stacks/gong.json +98 -35
  552. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  553. package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
  554. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  555. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  556. package/stacks/hootsuite.json +104 -0
  557. package/stacks/hubspot/pages/companies.tsx.tmpl +26 -0
  558. package/stacks/hubspot/pages/contacts.tsx.tmpl +45 -0
  559. package/stacks/hubspot/pages/deals.tsx.tmpl +20 -0
  560. package/stacks/hubspot/pages/inbox.tsx.tmpl +103 -0
  561. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  562. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  563. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  564. package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
  565. package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
  566. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  567. package/stacks/hubspot.json +181 -64
  568. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  569. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  570. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  571. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  572. package/stacks/intercom.json +52 -20
  573. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  574. package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
  575. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  576. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  577. package/stacks/mailchimp.json +68 -34
  578. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  579. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  580. package/stacks/outreach/pages/contacts.tsx.tmpl +46 -0
  581. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  582. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  583. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  584. package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
  585. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  586. package/stacks/outreach.json +106 -25
  587. package/stacks/pipedrive/pages/companies.tsx.tmpl +23 -0
  588. package/stacks/pipedrive/pages/contacts.tsx.tmpl +27 -0
  589. package/stacks/pipedrive/pages/deals.tsx.tmpl +20 -0
  590. package/stacks/pipedrive/pages/inbox.tsx.tmpl +103 -0
  591. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  592. package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
  593. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  594. package/stacks/pipedrive.json +117 -0
  595. package/stacks/salesforce/pages/companies.tsx.tmpl +26 -0
  596. package/stacks/salesforce/pages/contacts.tsx.tmpl +32 -0
  597. package/stacks/salesforce/pages/deals.tsx.tmpl +20 -0
  598. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  599. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  600. package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
  601. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  602. package/stacks/salesforce.json +85 -31
  603. package/stacks/zoominfo/pages/companies.tsx.tmpl +31 -0
  604. package/stacks/zoominfo/pages/contacts.tsx.tmpl +31 -0
  605. package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
  606. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  607. package/stacks/zoominfo.json +115 -0
  608. package/styles/styles.css +1 -1
  609. package/templates/next-app/README.md.tmpl +40 -9
  610. package/templates/next-app/app/globals.css.tmpl +349 -64
  611. package/templates/next-app/app/layout.tsx.tmpl +7 -2
  612. package/templates/next-app/components/app-shell.tsx.tmpl +104 -88
  613. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
  614. package/templates/next-app/dot-gitignore.tmpl +2 -0
  615. package/templates/next-app/package.json.tmpl +1 -1
@@ -15,40 +15,78 @@
15
15
  "lists"
16
16
  ],
17
17
  "operations": [
18
- "list_newsletters_newsletters_get",
19
- "create_newsletter_newsletters_post",
20
- "list_issues_newsletters__newsletter_id__issues_get",
21
18
  "activate_newsletter_newsletters__newsletter_id__activate_post",
22
- "pause_newsletter_newsletters__newsletter_id__pause_post",
23
- "get_issue_newsletters__newsletter_id__issues__issue_id__get",
19
+ "add_subscriber_newsletters__newsletter_id__subscribers_post",
20
+ "cancel_send_newsletters__newsletter_id__issues__issue_id__send_cancel_post",
21
+ "complete_newsletter_newsletters__newsletter_id__complete_post",
24
22
  "create_issue_newsletters__newsletter_id__issues_post",
25
- "update_issue_newsletters__newsletter_id__issues__issue_id__put",
23
+ "create_newsletter_newsletters_post",
24
+ "create_template_from_prompt_newsletters_templates_custom_post",
25
+ "delete_issue_newsletters__newsletter_id__issues__issue_id__delete",
26
+ "delete_newsletter_template_newsletters_templates__template_id__delete",
27
+ "delete_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__delete",
28
+ "disable_newsletter_list_sync_newsletters__newsletter_id__list_syncs__audience_list_id__delete",
29
+ "duplicate_issue_newsletters__newsletter_id__issues__issue_id__duplicate_post",
30
+ "enable_newsletter_list_sync_newsletters__newsletter_id__list_syncs_post",
26
31
  "generate_newsletter_issue_newsletters__newsletter_id__issues__issue_id__generate_post",
32
+ "get_issue_analytics_kpis_newsletters__newsletter_id__issues__issue_id__analytics_kpis_get",
27
33
  "get_issue_generation_status_newsletters__newsletter_id__issues__issue_id__generation_status_get",
28
- "send_test_email_newsletters__newsletter_id__issues__issue_id__send_test_post",
29
- "delete_issue_newsletters__newsletter_id__issues__issue_id__delete",
30
- "list_subscribers_newsletters__newsletter_id__subscribers_get",
31
- "send_issue_newsletters__newsletter_id__issues__issue_id__send_post",
34
+ "get_issue_newsletters__newsletter_id__issues__issue_id__get",
35
+ "get_newsletter_newsletters__newsletter_id__get",
36
+ "get_newsletter_settings_newsletters__newsletter_id__settings_get",
32
37
  "get_send_status_newsletters__newsletter_id__issues__issue_id__send_get",
33
- "pause_send_newsletters__newsletter_id__issues__issue_id__send_pause_post",
34
- "resume_send_newsletters__newsletter_id__issues__issue_id__send_resume_post",
35
- "cancel_send_newsletters__newsletter_id__issues__issue_id__send_cancel_post",
36
- "add_subscriber_newsletters__newsletter_id__subscribers_post",
37
- "list_lists_lists_get",
38
+ "get_subscriber_timeline_newsletters__newsletter_id__issues__issue_id__analytics_subscribers__subscriber_id__timeline_get",
38
39
  "import_subscribers_from_audience_newsletters__newsletter_id__subscribers_import_audience_post",
40
+ "list_issues_newsletters__newsletter_id__issues_get",
41
+ "list_lists_lists_get",
42
+ "list_newsletter_issue_versions_newsletters__newsletter_id__issues__issue_id__versions_get",
43
+ "list_newsletter_list_syncs_newsletters__newsletter_id__list_syncs_get",
44
+ "list_newsletters_newsletters_get",
45
+ "list_subscribers_newsletters__newsletter_id__subscribers_get",
46
+ "list_templates_newsletters_templates_get",
47
+ "newsletter_domain_status_newsletters__newsletter_id__settings_domain_status_get",
48
+ "pause_newsletter_newsletters__newsletter_id__pause_post",
49
+ "pause_send_newsletters__newsletter_id__issues__issue_id__send_pause_post",
39
50
  "preview_newsletter_subscriber_verification_newsletters__newsletter_id__subscribers_verify_preview_post",
40
- "verify_newsletter_subscribers_newsletters__newsletter_id__subscribers_verify_post",
51
+ "restore_newsletter_issue_version_newsletters__newsletter_id__issues__issue_id__versions__version_num__restore_post",
52
+ "resume_newsletter_newsletters__newsletter_id__resume_post",
53
+ "resume_send_newsletters__newsletter_id__issues__issue_id__send_resume_post",
54
+ "run_newsletter_list_sync_newsletters__newsletter_id__list_syncs__audience_list_id__run_post",
55
+ "send_issue_newsletters__newsletter_id__issues__issue_id__send_post",
56
+ "send_test_email_newsletters__newsletter_id__issues__issue_id__send_test_post",
57
+ "set_template_approval_newsletters_templates__template_id__approval_post",
58
+ "template_generation_status_newsletters_templates__template_id__generation_status_get",
41
59
  "unsubscribe_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__unsubscribe_patch",
42
- "delete_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__delete",
43
- "get_issue_analytics_kpis_newsletters__newsletter_id__issues__issue_id__analytics_kpis_get"
60
+ "update_issue_newsletters__newsletter_id__issues__issue_id__put",
61
+ "update_newsletter_newsletters__newsletter_id__patch",
62
+ "update_newsletter_settings_newsletters__newsletter_id__settings_put",
63
+ "update_newsletter_template_newsletters_templates__template_id__patch",
64
+ "use_template_newsletters_templates__template_id__use_post",
65
+ "verify_newsletter_domain_newsletters__newsletter_id__settings_verify_domain_post",
66
+ "verify_newsletter_subscribers_newsletters__newsletter_id__subscribers_verify_post"
44
67
  ],
45
68
  "components": [
46
69
  {
47
70
  "name": "NewsletterScreen",
48
71
  "file": "components/newsletter-screen.tsx",
49
72
  "purpose": "Finished newsletter workspace: pick or create a newsletter, issues list with detail (write, send, results), subscribers and performance tabs.",
50
- "operations": [],
51
- "tiers": []
73
+ "operations": [
74
+ "activate_newsletter_newsletters__newsletter_id__activate_post",
75
+ "create_newsletter_newsletters_post",
76
+ "delete_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__delete",
77
+ "get_issue_analytics_kpis_newsletters__newsletter_id__issues__issue_id__analytics_kpis_get",
78
+ "list_issues_newsletters__newsletter_id__issues_get",
79
+ "list_newsletters_newsletters_get",
80
+ "list_subscribers_newsletters__newsletter_id__subscribers_get",
81
+ "pause_newsletter_newsletters__newsletter_id__pause_post",
82
+ "unsubscribe_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__unsubscribe_patch"
83
+ ],
84
+ "tiers": [
85
+ "read",
86
+ "write",
87
+ "external",
88
+ "destructive"
89
+ ]
52
90
  },
53
91
  {
54
92
  "name": "NewsletterEditor",
@@ -112,14 +150,15 @@
112
150
  "file": "components/subscriber-table.tsx",
113
151
  "purpose": "Subscribers with search, status filter, add, import a list, verify addresses, unsubscribe and erase.",
114
152
  "operations": [
115
- "list_subscribers_newsletters__newsletter_id__subscribers_get",
116
153
  "add_subscriber_newsletters__newsletter_id__subscribers_post",
117
- "list_lists_lists_get",
154
+ "delete_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__delete",
118
155
  "import_subscribers_from_audience_newsletters__newsletter_id__subscribers_import_audience_post",
156
+ "list_lists_lists_get",
157
+ "list_newsletters_newsletters_get",
158
+ "list_subscribers_newsletters__newsletter_id__subscribers_get",
119
159
  "preview_newsletter_subscriber_verification_newsletters__newsletter_id__subscribers_verify_preview_post",
120
- "verify_newsletter_subscribers_newsletters__newsletter_id__subscribers_verify_post",
121
160
  "unsubscribe_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__unsubscribe_patch",
122
- "delete_subscriber_newsletters__newsletter_id__subscribers__subscriber_id__delete"
161
+ "verify_newsletter_subscribers_newsletters__newsletter_id__subscribers_verify_post"
123
162
  ],
124
163
  "tiers": [
125
164
  "read",
@@ -139,6 +178,96 @@
139
178
  "tiers": [
140
179
  "read"
141
180
  ]
181
+ },
182
+ {
183
+ "name": "IssueVersions",
184
+ "file": "components/issue-versions.tsx",
185
+ "purpose": "Version history of one issue with restore, and duplicate into a new draft.",
186
+ "operations": [
187
+ "list_newsletter_issue_versions_newsletters__newsletter_id__issues__issue_id__versions_get",
188
+ "restore_newsletter_issue_version_newsletters__newsletter_id__issues__issue_id__versions__version_num__restore_post",
189
+ "duplicate_issue_newsletters__newsletter_id__issues__issue_id__duplicate_post"
190
+ ],
191
+ "tiers": [
192
+ "read",
193
+ "write",
194
+ "external"
195
+ ]
196
+ },
197
+ {
198
+ "name": "MoreRows",
199
+ "file": "components/more-rows.tsx",
200
+ "purpose": "Footer for paged lists: how many rows are shown and a Load more button.",
201
+ "operations": [],
202
+ "tiers": []
203
+ },
204
+ {
205
+ "name": "NewsletterListSyncs",
206
+ "file": "components/newsletter-list-syncs.tsx",
207
+ "purpose": "Keep subscribers in step with lists: turn a sync on (consent confirmed), run it, turn it off.",
208
+ "operations": [
209
+ "list_newsletter_list_syncs_newsletters__newsletter_id__list_syncs_get",
210
+ "list_lists_lists_get",
211
+ "enable_newsletter_list_sync_newsletters__newsletter_id__list_syncs_post",
212
+ "run_newsletter_list_sync_newsletters__newsletter_id__list_syncs__audience_list_id__run_post",
213
+ "disable_newsletter_list_sync_newsletters__newsletter_id__list_syncs__audience_list_id__delete"
214
+ ],
215
+ "tiers": [
216
+ "read",
217
+ "write",
218
+ "destructive"
219
+ ]
220
+ },
221
+ {
222
+ "name": "NewsletterSettings",
223
+ "file": "components/newsletter-settings.tsx",
224
+ "purpose": "The newsletter itself: name and goal, sender and opt-in, sending domain verification, resume and complete.",
225
+ "operations": [
226
+ "update_newsletter_newsletters__newsletter_id__patch",
227
+ "update_newsletter_settings_newsletters__newsletter_id__settings_put",
228
+ "newsletter_domain_status_newsletters__newsletter_id__settings_domain_status_get",
229
+ "verify_newsletter_domain_newsletters__newsletter_id__settings_verify_domain_post",
230
+ "complete_newsletter_newsletters__newsletter_id__complete_post",
231
+ "resume_newsletter_newsletters__newsletter_id__resume_post",
232
+ "get_newsletter_settings_newsletters__newsletter_id__settings_get",
233
+ "get_newsletter_newsletters__newsletter_id__get"
234
+ ],
235
+ "tiers": [
236
+ "read",
237
+ "write",
238
+ "external"
239
+ ]
240
+ },
241
+ {
242
+ "name": "NewsletterTemplates",
243
+ "file": "components/newsletter-templates.tsx",
244
+ "purpose": "Saved issue templates: start a draft from one, approve, rename, delete, or write one with AI.",
245
+ "operations": [
246
+ "update_newsletter_template_newsletters_templates__template_id__patch",
247
+ "set_template_approval_newsletters_templates__template_id__approval_post",
248
+ "use_template_newsletters_templates__template_id__use_post",
249
+ "template_generation_status_newsletters_templates__template_id__generation_status_get",
250
+ "delete_newsletter_template_newsletters_templates__template_id__delete",
251
+ "list_templates_newsletters_templates_get",
252
+ "create_template_from_prompt_newsletters_templates_custom_post"
253
+ ],
254
+ "tiers": [
255
+ "read",
256
+ "write",
257
+ "billable",
258
+ "destructive"
259
+ ]
260
+ },
261
+ {
262
+ "name": "SubscriberTimeline",
263
+ "file": "components/subscriber-timeline.tsx",
264
+ "purpose": "What one subscriber did with one sent issue.",
265
+ "operations": [
266
+ "get_subscriber_timeline_newsletters__newsletter_id__issues__issue_id__analytics_subscribers__subscriber_id__timeline_get"
267
+ ],
268
+ "tiers": [
269
+ "read"
270
+ ]
142
271
  }
143
272
  ],
144
273
  "hooks": [],
@@ -146,6 +275,10 @@
146
275
  {
147
276
  "file": "lib/newsletter-types.ts",
148
277
  "purpose": "Newsletter/issue/subscriber types and helpers: draft check, percentages, scheduled-time conversion, generation-in-progress guard."
278
+ },
279
+ {
280
+ "file": "lib/newsletter-extra.ts",
281
+ "purpose": "Helpers for the newsletter management components: open-dict readers, changed-fields diff, domain and DNS helpers."
149
282
  }
150
283
  ],
151
284
  "recipes": [
@@ -25,6 +25,7 @@ import { useG8Query } from "@/g8/hooks/use-g8-query";
25
25
  import { useJobPoll } from "@/g8/hooks/use-job-poll";
26
26
  import { generationInProgress, isDraft, issueTitle, sendIsMoving, sendIsPaused, toIsoInstant } from "../lib/newsletter-types";
27
27
  import type { Issue, SendState } from "../lib/newsletter-types";
28
+ import { pluralWord } from "@/g8/lib/plural";
28
29
 
29
30
  export interface IssueSchedulerProps {
30
31
  newsletterId: string;
@@ -108,16 +109,16 @@ export function IssueScheduler({ newsletterId, issueId, onSent }: IssueScheduler
108
109
  <Card>
109
110
  <CardHeader><CardTitle>{issueTitle(issue.data)} <span className="text-muted-foreground text-sm font-normal">{issue.data.status}</span></CardTitle></CardHeader>
110
111
  <CardContent className="grid gap-4">
111
- {partial ? <p role="alert" className="text-sm text-amber-700">This issue is still being written by AI. Wait for it to finish: sending now would mail the partial version.</p> : null}
112
+ {partial ? <p role="alert" className="text-sm text-warning-foreground">This issue is still being written by AI. Wait for it to finish: sending now would mail the partial version.</p> : null}
112
113
  {draft ? (
113
114
  <div className="grid gap-3">
114
- <p className="text-sm">Audience: <strong>{audience.loading ? "..." : subscribers.toLocaleString()}</strong> subscribed {subscribers === 1 ? "person" : "people"}.</p>
115
+ <p className="text-sm">Audience: <strong>{audience.loading ? "..." : subscribers.toLocaleString()}</strong> subscribed {pluralWord(subscribers, "person", "people")}.</p>
115
116
  <div className="flex gap-2" role="group" aria-label="When to send">
116
117
  <Button size="sm" variant={when === "now" ? "secondary" : "ghost"} onClick={() => setWhen("now")}>Send now</Button>
117
118
  <Button size="sm" variant={when === "later" ? "secondary" : "ghost"} onClick={() => setWhen("later")}>Schedule</Button>
118
119
  </div>
119
120
  {when === "later" ? <div className="max-w-xs"><Label htmlFor="g8-send-at" className="mb-1">Send at</Label><Input id="g8-send-at" type="datetime-local" value={at} onChange={(e) => setAt(e.target.value)} /></div> : null}
120
- {inPast ? <p role="alert" className="text-sm text-red-700">Pick a time in the future.</p> : null}
121
+ {inPast ? <p role="alert" className="text-sm text-error-foreground">Pick a time in the future.</p> : null}
121
122
  <div><Button disabled={blocked} onClick={() => void openSend()}>{when === "later" ? "Schedule issue" : "Send issue"}</Button></div>
122
123
  {subscribers === 0 && !audience.loading ? <p className="text-muted-foreground text-sm">Add subscribers first (SubscriberTable).</p> : null}
123
124
  </div>
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ /**
4
+ * IssueVersions: the saved versions of one issue, newest first, with restore, and a Duplicate action that copies the
5
+ * issue into a new draft.
6
+ *
7
+ * SDK operations, in the order the screen uses them:
8
+ * 1. listNewsletterIssueVersions GET /newsletters/{id}/issues/{issue}/versions (read)
9
+ * 2. restoreNewsletterIssueVersion POST /newsletters/{id}/issues/{issue}/versions/{n}/restore (write; replaces the draft's content)
10
+ * 3. duplicateIssue POST /newsletters/{id}/issues/{issue}/duplicate (EXTERNAL, confirmed; creates a draft, sends nothing)
11
+ *
12
+ * Only a draft can be restored (a sent issue is a record of what people received). Restoring replaces the draft's
13
+ * current content, so the button says so and asks for a second click before it writes.
14
+ */
15
+ import { useState } from "react";
16
+ import { Copy } from "lucide-react";
17
+ import { Button, Skeleton } from "@graph8/sdk/ui";
18
+ import { ApiError } from "@/g8/components/api-error";
19
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
20
+ import { useConfirmAction } from "@/g8/components/confirmed-action";
21
+ import { EmptyState } from "@/g8/components/empty-state";
22
+ import { SectionCard } from "@/g8/components/screen/section-card";
23
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
24
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
25
+ import { g8Get, g8Post } from "@/g8/lib/g8-client";
26
+ import { isDraft } from "../lib/newsletter-types";
27
+ import type { Issue } from "../lib/newsletter-types";
28
+ import { asRec, num, rowsOf, str, versionNumber } from "../lib/newsletter-extra";
29
+ import type { Rec } from "../lib/newsletter-extra";
30
+
31
+ export interface IssueVersionsProps {
32
+ newsletterId: string;
33
+ issue: Pick<Issue, "id" | "status" | "subject">;
34
+ /** Called after a version was restored, so the editor reloads the draft. */
35
+ onRestored?: () => void;
36
+ /** Called with the new draft's id after Duplicate. */
37
+ onDuplicated?: (issueId: number) => void;
38
+ className?: string;
39
+ }
40
+
41
+ const when = (v: unknown): string => {
42
+ const d = new Date(str(v));
43
+ return Number.isNaN(d.getTime()) ? "" : d.toLocaleString();
44
+ };
45
+
46
+ export function IssueVersions({ newsletterId, issue, onRestored, onDuplicated, className }: IssueVersionsProps) {
47
+ const base = `/newsletters/${encodeURIComponent(newsletterId)}/issues/${issue.id}`;
48
+ const { ask, dialog, failure } = useConfirmAction();
49
+ const versions = useG8Query((signal) => g8Get(`${base}/versions`, undefined, { signal }), [base]);
50
+ const [sure, setSure] = useState<number | null>(null);
51
+ const restore = useG8Mutation((n: number) => g8Post(`${base}/versions/${n}/restore`), { onSuccess: () => { setSure(null); void versions.refetch(); onRestored?.(); } });
52
+ const rows = rowsOf<Rec>(versions.data?.data, "versions");
53
+ const draft = isDraft(issue);
54
+
55
+ return (
56
+ <div className={className}>
57
+ <div className="grid gap-4">
58
+ <SectionCard title="Duplicate" description="Copy this issue into a new draft you can change and send separately."
59
+ actions={<Button size="sm" variant="outline" onClick={() => ask({
60
+ tier: "external", title: `Duplicate "${issue.subject || `issue ${issue.id}`}"`, confirmLabel: "Duplicate",
61
+ description: "Creates a new draft with the same content. Nothing is sent to anyone.",
62
+ run: async () => { const id = num(asRec((await g8Post(`${base}/duplicate`)).data).id); if (id !== undefined) onDuplicated?.(id); },
63
+ })}><Copy className="size-4" />Duplicate issue</Button>}>
64
+ <p className="text-muted-foreground text-sm">The copy starts as a draft in this newsletter.</p>
65
+ </SectionCard>
66
+ <SectionCard title="Version history" description={draft ? "Restoring replaces the draft's current content." : "A sent issue cannot be changed; its versions are shown for reference."}>
67
+ {versions.error ? <ApiError error={versions.error} title="Could not load the versions" onRetry={() => void versions.refetch()} /> : versions.loading && !versions.data ? <Skeleton className="h-20 w-full" aria-busy="true" /> : rows.length === 0 ? (
68
+ <EmptyState compact title="No saved versions yet" description="A version is saved each time the issue is saved or rewritten." />
69
+ ) : (
70
+ <ul className="grid gap-2">
71
+ {rows.map((v, i) => {
72
+ const n = versionNumber(v);
73
+ return (
74
+ <li key={n ?? i} className="flex items-center justify-between gap-3 rounded-md border p-3">
75
+ <div className="min-w-0 text-sm"><span className="font-medium">Version {n ?? i + 1}</span>{str(v.source) ? <span className="text-muted-foreground"> from {str(v.source).replace(/_/g, " ")}</span> : null}<span className="text-muted-foreground block text-xs">{when(v.created_at)}{str(v.subject) ? ` · ${str(v.subject)}` : ""}</span></div>
76
+ {draft && n !== undefined ? (
77
+ sure === n ? (
78
+ <div className="flex gap-1"><Button size="sm" loading={restore.loading} onClick={() => void restore.mutate(n)}>Replace the draft</Button><Button size="sm" variant="ghost" onClick={() => setSure(null)}>Cancel</Button></div>
79
+ ) : <Button size="sm" variant="outline" aria-label={`Restore version ${n}`} onClick={() => setSure(n)}>Restore</Button>
80
+ ) : null}
81
+ </li>
82
+ );
83
+ })}
84
+ </ul>
85
+ )}
86
+ {restore.error ? <ApiError error={restore.error} title="Could not restore that version" /> : null}
87
+ </SectionCard>
88
+ {failure ? <ApiError error={failure} title="That did not work" /> : null}
89
+ </div>
90
+ <ConfirmSpendDialog {...dialog} />
91
+ </div>
92
+ );
93
+ }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ /**
4
+ * MoreRows: the honest end of a list that the API pages. Shows how many rows are on screen and, while the API has
5
+ * another page, a "Load more" button. A list never ends silently: either every row is reachable or this says there are more.
6
+ * (Same contract as the foundation's LoadMore; kept local so the newsletter block does not wait on it.)
7
+ *
8
+ * SDK operations: none.
9
+ */
10
+ import { Button } from "@graph8/sdk/ui";
11
+
12
+ export interface MoreRowsProps {
13
+ /** Rows on screen. */
14
+ shown: number;
15
+ /** What the rows are, plural: "people". */
16
+ noun: string;
17
+ hasMore: boolean;
18
+ loading?: boolean;
19
+ onMore: () => void;
20
+ }
21
+
22
+ export function MoreRows({ shown, noun, hasMore, loading, onMore }: MoreRowsProps) {
23
+ if (!hasMore) return null;
24
+ return (
25
+ <div className="text-muted-foreground flex items-center justify-between gap-3 py-2 text-sm">
26
+ <span role="status">Showing {shown.toLocaleString()} {noun}</span>
27
+ <Button type="button" variant="outline" size="sm" loading={loading} onClick={onMore}>Load more</Button>
28
+ </div>
29
+ );
30
+ }
@@ -81,7 +81,7 @@ export function NewsletterEditor({ initialNewsletterId, onSelectIssue }: Newslet
81
81
  {!current ? <EmptyState title="Pick a newsletter" description="Choose a newsletter to see its issues, or create one." /> : (
82
82
  <>
83
83
  <div className="flex items-center justify-between">
84
- <h2 className="text-lg font-semibold">{current.name} <span className="text-muted-foreground text-sm font-normal">{current.status}</span></h2>
84
+ <h2 className="text-lg font-medium">{current.name} <span className="text-muted-foreground text-sm font-normal">{current.status}</span></h2>
85
85
  {live ? <Button variant="outline" size="sm" loading={pause.loading} onClick={() => void pause.mutate(undefined)}>Pause newsletter</Button> : <Button size="sm" onClick={() => setPending({ kind: "activate" })}>Activate newsletter</Button>}
86
86
  </div>
87
87
  {pause.error ? <ApiError error={pause.error} title="Could not pause" /> : null}
@@ -0,0 +1,121 @@
1
+ "use client";
2
+
3
+ /**
4
+ * NewsletterListSyncs: keep subscribers in step with your lists. A synced list adds its contacts as subscribers; run a
5
+ * sync now to pull in the latest, or turn it off.
6
+ *
7
+ * SDK operations, in the order the screen uses them:
8
+ * 1. listNewsletterListSyncs GET /newsletters/{id}/list-syncs (read)
9
+ * 2. listLists GET /lists?page&limit&search (paged, "Load more") (read; the lists you can turn on)
10
+ * 3. enableNewsletterListSync POST /newsletters/{id}/list-syncs { audience_list_id, attested_text } (write, confirmed: people are subscribed to a newsletter)
11
+ * 4. runNewsletterListSync POST /newsletters/{id}/list-syncs/{list}/run (write, confirmed: adds the list's people now)
12
+ * 5. disableNewsletterListSync DELETE /newsletters/{id}/list-syncs/{list} (destructive, confirmed: stops syncing; subscribers stay)
13
+ *
14
+ * Turning a sync on subscribes real people, so the person confirms the consent statement first: the exact text is shown
15
+ * and sent as `attested_text`. The consent claim is theirs; the dialog does not pretend otherwise.
16
+ */
17
+ import { useState } from "react";
18
+ import { Button, Input } from "@graph8/sdk/ui";
19
+ import { ApiError } from "@/g8/components/api-error";
20
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
21
+ import { useConfirmAction } from "@/g8/components/confirmed-action";
22
+ import { EmptyState } from "@/g8/components/empty-state";
23
+ import { SectionCard } from "@/g8/components/screen/section-card";
24
+ import { StatusPill } from "@/g8/components/screen/status-pill";
25
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
26
+ import { usePaginated } from "@/g8/hooks/use-paginated";
27
+ import { g8Delete, g8Get, g8Post } from "@/g8/lib/g8-client";
28
+ import { asRec, num, pageOf, rowsOf, str } from "../lib/newsletter-extra";
29
+ import type { Rec } from "../lib/newsletter-extra";
30
+ import { MoreRows } from "./more-rows";
31
+
32
+ export interface NewsletterListSyncsProps {
33
+ newsletterId: string;
34
+ /** Called after a sync changed who is subscribed. */
35
+ onChanged?: () => void;
36
+ className?: string;
37
+ }
38
+
39
+ export const CONSENT_TEXT = "I confirm the people in this list agreed to receive this newsletter.";
40
+ const PAGE = 50;
41
+
42
+ export function NewsletterListSyncs({ newsletterId, onChanged, className }: NewsletterListSyncsProps) {
43
+ const base = `/newsletters/${encodeURIComponent(newsletterId)}/list-syncs`;
44
+ const { ask, dialog, failure } = useConfirmAction();
45
+ const [search, setSearch] = useState("");
46
+ const [note, setNote] = useState("");
47
+ const syncs = useG8Query((signal) => g8Get(base, undefined, { signal }), [newsletterId]);
48
+ const rows = rowsOf<Rec>(syncs.data?.data, "list_syncs");
49
+ // Disabled rows stay in the API list (so they can be turned back on): only enabled ones count as synced.
50
+ const synced = new Set(rows.filter((r) => r.enabled !== false).map((r) => String(r.audience_list_id)));
51
+ const lists = usePaginated<Rec>(async (cursor, signal) => {
52
+ const page = cursor ? Number(cursor) : 1;
53
+ const res = await g8Get("/lists", { limit: PAGE, page, search: search.trim() || undefined }, { signal });
54
+ const got = rowsOf<Rec>(res.data, "lists");
55
+ const more = (res.pagination as { has_next?: boolean } | undefined)?.has_next ?? got.length >= PAGE;
56
+ return pageOf(got, more && got.length > 0 ? page + 1 : null);
57
+ }, [search]);
58
+ const changed = (): void => { void syncs.refetch(); onChanged?.(); };
59
+
60
+ const enable = (l: { id?: unknown; title?: unknown; name?: unknown }): void => ask({
61
+ tier: "external", title: `Subscribe "${str(l.title) || str(l.name)}" to this newsletter`, confirmLabel: "Turn on sync",
62
+ description: <><p>People in this list become subscribers and will receive issues. You are confirming:</p><p className="mt-2 font-medium">{CONSENT_TEXT}</p></>,
63
+ run: () => g8Post(base, { audience_list_id: Number(l.id), attested_text: CONSENT_TEXT }), onDone: changed,
64
+ });
65
+ const run = (s: Rec): void => ask({
66
+ tier: "external", title: `Sync "${str(s.name)}" now`, confirmLabel: "Sync now",
67
+ recipients: num(s.contact_count),
68
+ description: "Adds the list's people who are not subscribed yet. People who unsubscribed stay unsubscribed.",
69
+ run: async () => { await g8Post(`${base}/${encodeURIComponent(String(s.audience_list_id))}/run`); setNote(`Synced "${str(s.name)}".`); }, onDone: changed,
70
+ });
71
+ const disable = (s: Rec): void => ask({
72
+ tier: "destructive", title: `Stop syncing "${str(s.name)}"`, confirmLabel: "Stop syncing",
73
+ description: "New contacts added to this list are no longer subscribed. People already subscribed stay subscribed.",
74
+ tolerateStatus: [404], run: () => g8Delete(`${base}/${encodeURIComponent(String(s.audience_list_id))}`), onDone: changed,
75
+ });
76
+
77
+ return (
78
+ <div className={className}>
79
+ <div className="grid gap-4">
80
+ <SectionCard title="Synced lists" description="Subscribers are kept in step with these lists.">
81
+ {syncs.error ? <ApiError error={syncs.error} title="Could not load the synced lists" onRetry={() => void syncs.refetch()} /> : rows.length === 0 ? (
82
+ <EmptyState compact title="No lists are synced" description="Choose a list below to subscribe its people." />
83
+ ) : (
84
+ <ul className="grid gap-2">
85
+ {rows.map((s) => (
86
+ <li key={String(s.audience_list_id)} className="flex items-center justify-between gap-3 rounded-md border p-3">
87
+ <div className="min-w-0"><span className="block truncate text-sm font-medium">{str(s.name) || `List ${String(s.audience_list_id)}`}</span><span className="text-muted-foreground text-xs">{num(s.contact_count) == null ? "" : `${(s.contact_count as number).toLocaleString()} contacts`}</span></div>
88
+ {s.enabled === false ? <StatusPill tone="neutral">Off</StatusPill> : <StatusPill tone="positive">On</StatusPill>}
89
+ {s.enabled === false ? (
90
+ <Button size="sm" variant="outline" aria-label={`Turn ${str(s.name)} back on`} onClick={() => enable({ id: s.audience_list_id, name: s.name })}>Turn on sync</Button>
91
+ ) : <div className="flex gap-1"><Button size="sm" variant="outline" aria-label={`Sync ${str(s.name)} now`} onClick={() => run(s)}>Sync now</Button><Button size="sm" variant="ghost" aria-label={`Stop syncing ${str(s.name)}`} onClick={() => disable(s)}>Stop</Button></div>}
92
+ </li>
93
+ ))}
94
+ </ul>
95
+ )}
96
+ {note ? <p role="status" className="mt-2 text-sm">{note}</p> : null}
97
+ </SectionCard>
98
+ <SectionCard title="Add a list" description="Find the list to subscribe.">
99
+ <Input aria-label="Search lists" placeholder="Search lists" value={search} onChange={(e) => setSearch(e.target.value)} className="mb-3 max-w-sm" />
100
+ {lists.error ? <ApiError error={lists.error} title="Could not load your lists" onRetry={() => void lists.reload()} /> : lists.loading ? <p className="text-muted-foreground text-sm" aria-busy="true">Loading lists...</p> : lists.items.length === 0 ? (
101
+ <EmptyState compact title={search ? "No list matches" : "You have no lists"} description={search ? "Try another name." : "Create a list in Lists first."} />
102
+ ) : (
103
+ <>
104
+ <ul className="grid gap-2">
105
+ {lists.items.map((l) => (
106
+ <li key={String(l.id)} className="flex items-center justify-between gap-3 rounded-md border p-3">
107
+ <span className="truncate text-sm">{str(l.title)}</span>
108
+ {synced.has(String(l.id)) ? <StatusPill tone="positive">Synced</StatusPill> : <Button size="sm" variant="outline" aria-label={`Sync ${str(l.title)}`} onClick={() => enable(l)}>Turn on sync</Button>}
109
+ </li>
110
+ ))}
111
+ </ul>
112
+ <MoreRows shown={lists.items.length} noun="lists" hasMore={lists.hasMore} loading={lists.loadingMore} onMore={() => void lists.loadMore()} />
113
+ </>
114
+ )}
115
+ </SectionCard>
116
+ {failure ? <ApiError error={failure} title="That did not work" /> : null}
117
+ </div>
118
+ <ConfirmSpendDialog {...dialog} />
119
+ </div>
120
+ );
121
+ }