@graph8/sdk 0.370.0 → 0.374.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (646) hide show
  1. package/README.md +22 -3
  2. package/blocks/_foundation/README.md +61 -0
  3. package/blocks/_foundation/block.json +57 -0
  4. package/blocks/_foundation/components/auth-gate.tsx +363 -0
  5. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  6. package/blocks/_foundation/components/empty-state.tsx +5 -2
  7. package/blocks/_foundation/components/list-select.tsx +112 -0
  8. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  9. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  10. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  11. package/blocks/_foundation/components/screen/index.ts +1 -0
  12. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  13. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  14. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  15. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  16. package/blocks/_foundation/lib/paging.ts +57 -0
  17. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  18. package/blocks/_foundation/server/auth.ts +815 -0
  19. package/blocks/agents/README.md +81 -4
  20. package/blocks/agents/block.json +311 -2
  21. package/blocks/agents/components/agent-activity.tsx +112 -0
  22. package/blocks/agents/components/agent-approvals.tsx +135 -0
  23. package/blocks/agents/components/agent-builder.tsx +17 -3
  24. package/blocks/agents/components/agent-conversations.tsx +143 -0
  25. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
  26. package/blocks/agents/components/agent-operators.tsx +164 -0
  27. package/blocks/agents/components/agent-run-detail.tsx +20 -3
  28. package/blocks/agents/components/agent-runs.tsx +3 -3
  29. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  30. package/blocks/agents/components/agent-test-history.tsx +121 -0
  31. package/blocks/agents/components/agents-screen.tsx +53 -11
  32. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  33. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  34. package/blocks/agents/components/from-template-button.tsx +65 -0
  35. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  36. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  37. package/blocks/agents/components/knowledge-form.tsx +69 -0
  38. package/blocks/agents/components/name-dialog.tsx +32 -0
  39. package/blocks/agents/components/receptionist-button.tsx +46 -0
  40. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  41. package/blocks/agents/components/skill-editor.tsx +147 -0
  42. package/blocks/agents/components/skill-library.tsx +111 -0
  43. package/blocks/agents/lib/agent-types.ts +7 -1
  44. package/blocks/agents/lib/facts.tsx +13 -0
  45. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  46. package/blocks/agents/lib/load-more.tsx +141 -0
  47. package/blocks/agents/lib/skill-utils.ts +91 -0
  48. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  49. package/blocks/analytics/components/analytics-screen.tsx +16 -3
  50. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  51. package/blocks/analytics/components/report-builder.tsx +15 -2
  52. package/blocks/appointments/README.md +6 -0
  53. package/blocks/appointments/block.json +27 -0
  54. package/blocks/appointments/components/booking-page.tsx +2 -2
  55. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  56. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  57. package/blocks/appointments/components/meeting-list.tsx +29 -13
  58. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  59. package/blocks/campaigns/README.md +1 -1
  60. package/blocks/campaigns/block.json +34 -1
  61. package/blocks/campaigns/components/campaign-assets.tsx +1 -1
  62. package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
  63. package/blocks/campaigns/components/campaign-links.tsx +180 -0
  64. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  65. package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
  66. package/blocks/campaigns/components/icp-detail.tsx +11 -2
  67. package/blocks/campaigns/components/icp-editor.tsx +8 -3
  68. package/blocks/campaigns/components/persona-detail.tsx +11 -2
  69. package/blocks/campaigns/components/persona-editor.tsx +6 -1
  70. package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
  71. package/blocks/coach/components/call-scorecard.tsx +7 -7
  72. package/blocks/company-intelligence/README.md +132 -0
  73. package/blocks/company-intelligence/block.json +318 -0
  74. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  75. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  76. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  77. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  78. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  79. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  80. package/blocks/company-intelligence/components/research-run.tsx +165 -0
  81. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  82. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  83. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  84. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  85. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  86. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  87. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  88. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  89. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  90. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  91. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  92. package/blocks/copilot/components/copilot-panel.tsx +1 -1
  93. package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
  94. package/blocks/crm/README.md +25 -0
  95. package/blocks/crm/block.json +257 -9
  96. package/blocks/crm/components/bulk-update.tsx +119 -0
  97. package/blocks/crm/components/companies-screen.tsx +23 -9
  98. package/blocks/crm/components/company-detail.tsx +2 -2
  99. package/blocks/crm/components/contact-detail.tsx +4 -4
  100. package/blocks/crm/components/contact-documents-tab.tsx +144 -0
  101. package/blocks/crm/components/contact-insights-tab.tsx +118 -0
  102. package/blocks/crm/components/contact-lookup-link.tsx +85 -0
  103. package/blocks/crm/components/contact-row-actions.tsx +43 -0
  104. package/blocks/crm/components/contact-tag-chips.tsx +113 -0
  105. package/blocks/crm/components/contacts-screen.tsx +77 -18
  106. package/blocks/crm/components/dedupe-review.tsx +6 -1
  107. package/blocks/crm/components/import-wizard.tsx +3 -7
  108. package/blocks/crm/components/list-select.tsx +20 -0
  109. package/blocks/crm/components/owner-assign.tsx +132 -0
  110. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  111. package/blocks/crm/components/record-form.tsx +1 -1
  112. package/blocks/crm/components/record-open-links.tsx +79 -0
  113. package/blocks/crm/components/tag-assign.tsx +112 -0
  114. package/blocks/crm/components/tag-manager.tsx +178 -0
  115. package/blocks/crm/components/tasks-screen.tsx +35 -15
  116. package/blocks/crm/components/unlock-info-button.tsx +82 -0
  117. package/blocks/crm/components/verify-email-button.tsx +93 -0
  118. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  119. package/blocks/crm/hooks/use-contact-tags.ts +29 -0
  120. package/blocks/crm/hooks/use-page-query.ts +3 -1
  121. package/blocks/crm/lib/crm-types.ts +14 -0
  122. package/blocks/crm/lib/read-lists.ts +16 -0
  123. package/blocks/crm/lib/record-actions.ts +102 -0
  124. package/blocks/deals/README.md +25 -6
  125. package/blocks/deals/block.json +168 -2
  126. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  127. package/blocks/deals/components/create-deal-button.tsx +109 -0
  128. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  129. package/blocks/deals/components/deal-detail.tsx +7 -5
  130. package/blocks/deals/components/deal-filters.tsx +65 -0
  131. package/blocks/deals/components/deal-line-items.tsx +120 -0
  132. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  133. package/blocks/deals/components/deal-related.tsx +30 -9
  134. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  135. package/blocks/deals/components/deals-screen.tsx +57 -22
  136. package/blocks/deals/components/forecast-view.tsx +4 -4
  137. package/blocks/deals/components/pipeline-board.tsx +8 -6
  138. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  139. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  140. package/blocks/deals/components/stage-editor.tsx +1 -1
  141. package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
  142. package/blocks/deals/lib/deal-extras.ts +156 -0
  143. package/blocks/dialer/README.md +14 -5
  144. package/blocks/dialer/block.json +17 -2
  145. package/blocks/dialer/components/call-contact-button.tsx +26 -9
  146. package/blocks/dialer/components/call-log-screen.tsx +10 -2
  147. package/blocks/dialer/components/call-log.tsx +16 -4
  148. package/blocks/dialer/components/call-queue.tsx +3 -3
  149. package/blocks/dialer/components/dialer-screen.tsx +69 -27
  150. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  151. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  152. package/blocks/dialer/components/session-start-form.tsx +58 -11
  153. package/blocks/dialer/components/softphone.tsx +1 -1
  154. package/blocks/documents/README.md +86 -0
  155. package/blocks/documents/block.json +378 -0
  156. package/blocks/documents/components/account-bar.tsx +48 -0
  157. package/blocks/documents/components/correspondence-form.tsx +95 -0
  158. package/blocks/documents/components/correspondence-import.tsx +106 -0
  159. package/blocks/documents/components/correspondence-panel.tsx +191 -0
  160. package/blocks/documents/components/documents-screen.tsx +76 -0
  161. package/blocks/documents/components/library-document-detail.tsx +175 -0
  162. package/blocks/documents/components/library-document-form.tsx +105 -0
  163. package/blocks/documents/components/library-folder-form.tsx +74 -0
  164. package/blocks/documents/components/library-panel.tsx +235 -0
  165. package/blocks/documents/components/library-upload.tsx +160 -0
  166. package/blocks/documents/components/question-row.tsx +130 -0
  167. package/blocks/documents/components/questionnaire-detail.tsx +197 -0
  168. package/blocks/documents/components/questionnaires-panel.tsx +173 -0
  169. package/blocks/documents/components/record-documents-tab.tsx +109 -0
  170. package/blocks/documents/lib/documents-types.ts +252 -0
  171. package/blocks/documents/lib/download.ts +64 -0
  172. package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
  173. package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
  174. package/blocks/documents/recipes/organize-the-library.ts +25 -0
  175. package/blocks/email-signatures/README.md +123 -0
  176. package/blocks/email-signatures/block.json +341 -0
  177. package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
  178. package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
  179. package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
  180. package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
  181. package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
  182. package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
  183. package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
  184. package/blocks/email-signatures/components/signature-policy.tsx +130 -0
  185. package/blocks/email-signatures/components/signature-preview.tsx +119 -0
  186. package/blocks/email-signatures/components/signature-report.tsx +77 -0
  187. package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
  188. package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
  189. package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
  190. package/blocks/email-signatures/lib/signatures-types.ts +392 -0
  191. package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
  192. package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
  193. package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
  194. package/blocks/enrichment/README.md +38 -10
  195. package/blocks/enrichment/block.json +164 -12
  196. package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
  197. package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
  198. package/blocks/enrichment/components/bulk-enrich-panel.tsx +79 -30
  199. package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
  200. package/blocks/enrichment/components/enrich-button.tsx +1 -1
  201. package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
  202. package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
  203. package/blocks/enrichment/components/enrichment-screen.tsx +103 -19
  204. package/blocks/enrichment/components/list-pipelines.tsx +97 -0
  205. package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
  206. package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
  207. package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
  208. package/blocks/enrichment/components/waterfall-config.tsx +27 -7
  209. package/blocks/enrichment/lib/enrichment-types.ts +51 -0
  210. package/blocks/enrichment/lib/pipeline-types.ts +234 -0
  211. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  212. package/blocks/forms/README.md +10 -0
  213. package/blocks/forms/block.json +107 -0
  214. package/blocks/forms/components/form-activity.tsx +71 -0
  215. package/blocks/forms/components/form-backfill.tsx +108 -0
  216. package/blocks/forms/components/form-builder.tsx +1 -1
  217. package/blocks/forms/components/form-embed.tsx +1 -1
  218. package/blocks/forms/components/form-groups.tsx +151 -0
  219. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  220. package/blocks/forms/components/form-route-history.tsx +72 -0
  221. package/blocks/forms/components/form-saved-views.tsx +81 -0
  222. package/blocks/forms/components/forms-screen.tsx +57 -20
  223. package/blocks/forms/components/more-rows.tsx +30 -0
  224. package/blocks/forms/components/submissions-export.tsx +67 -0
  225. package/blocks/forms/components/submissions-table.tsx +15 -6
  226. package/blocks/forms/lib/forms-extra.ts +67 -0
  227. package/blocks/inbox/README.md +45 -4
  228. package/blocks/inbox/block.json +145 -8
  229. package/blocks/inbox/components/blocked-senders.tsx +103 -0
  230. package/blocks/inbox/components/compose-message.tsx +193 -0
  231. package/blocks/inbox/components/inbox-screen.tsx +54 -6
  232. package/blocks/inbox/components/inbox-search.tsx +106 -0
  233. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  234. package/blocks/inbox/components/tag-manager.tsx +136 -0
  235. package/blocks/inbox/components/thread-actions.tsx +133 -0
  236. package/blocks/inbox/components/thread-badge.tsx +2 -2
  237. package/blocks/inbox/components/thread-view.tsx +3 -1
  238. package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
  239. package/blocks/inbox/lib/inbox-manage.ts +227 -0
  240. package/blocks/integrations/README.md +54 -1
  241. package/blocks/integrations/block.json +189 -15
  242. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  243. package/blocks/integrations/components/connection-health.tsx +59 -0
  244. package/blocks/integrations/components/crm-detail.tsx +20 -1
  245. package/blocks/integrations/components/crm-records.tsx +103 -0
  246. package/blocks/integrations/components/field-mappings.tsx +61 -0
  247. package/blocks/integrations/components/integration-list.tsx +4 -4
  248. package/blocks/integrations/components/integrations-screen.tsx +20 -3
  249. package/blocks/integrations/components/migration-panel.tsx +133 -0
  250. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  251. package/blocks/integrations/components/sync-status.tsx +6 -6
  252. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  253. package/blocks/integrations/components/webhook-detail.tsx +26 -7
  254. package/blocks/integrations/components/webhook-manager.tsx +30 -17
  255. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  256. package/blocks/integrations/lib/crm-utils.ts +39 -0
  257. package/blocks/integrations/lib/facts.tsx +13 -0
  258. package/blocks/integrations/lib/integrations-utils.ts +5 -0
  259. package/blocks/integrations/lib/load-more.tsx +141 -0
  260. package/blocks/intent/components/account-intent-score.tsx +5 -5
  261. package/blocks/intent/components/intent-feed.tsx +8 -8
  262. package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
  263. package/blocks/intent/lib/intent-utils.ts +4 -4
  264. package/blocks/lifecycle/components/account-related.tsx +8 -1
  265. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  266. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  267. package/blocks/lists/README.md +7 -1
  268. package/blocks/lists/block.json +31 -3
  269. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  270. package/blocks/lists/components/add-to-list-button.tsx +17 -7
  271. package/blocks/lists/components/list-picker.tsx +13 -3
  272. package/blocks/lists/components/list-table.tsx +1 -1
  273. package/blocks/lists/components/lists-screen.tsx +4 -2
  274. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  275. package/blocks/lists/components/suppression-manager.tsx +27 -6
  276. package/blocks/lists/components/suppressions-screen.tsx +21 -12
  277. package/blocks/mailboxes/block.json +4 -0
  278. package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
  279. package/blocks/mailboxes/components/domain-detail.tsx +10 -4
  280. package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
  281. package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
  282. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  283. package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
  284. package/blocks/mailboxes/components/warmup-health.tsx +20 -20
  285. package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
  286. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  287. package/blocks/newsletter/README.md +12 -0
  288. package/blocks/newsletter/block.json +117 -0
  289. package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
  290. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  291. package/blocks/newsletter/components/more-rows.tsx +30 -0
  292. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  293. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  294. package/blocks/newsletter/components/newsletter-screen.tsx +38 -13
  295. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  296. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  297. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  298. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  299. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  300. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  301. package/blocks/nurture/components/nurture-builder.tsx +10 -2
  302. package/blocks/nurture/components/nurture-enroll.tsx +6 -7
  303. package/blocks/nurture/components/nurture-form.tsx +5 -8
  304. package/blocks/nurture/components/nurture-screen.tsx +16 -5
  305. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  306. package/blocks/objects/README.md +1 -1
  307. package/blocks/objects/block.json +13 -5
  308. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  309. package/blocks/objects/components/object-record-detail.tsx +8 -1
  310. package/blocks/objects/components/object-schema-editor.tsx +1 -1
  311. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  312. package/blocks/pages/README.md +14 -0
  313. package/blocks/pages/block.json +123 -2
  314. package/blocks/pages/components/more-rows.tsx +30 -0
  315. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  316. package/blocks/pages/components/page-analytics.tsx +176 -0
  317. package/blocks/pages/components/page-clone.tsx +99 -0
  318. package/blocks/pages/components/page-domains.tsx +167 -0
  319. package/blocks/pages/components/page-editor.tsx +1 -1
  320. package/blocks/pages/components/page-generation-status.tsx +48 -0
  321. package/blocks/pages/components/page-tracking.tsx +137 -0
  322. package/blocks/pages/components/pages-screen.tsx +57 -16
  323. package/blocks/pages/components/seo-report.tsx +6 -1
  324. package/blocks/pages/hooks/use-landing-page-paths.ts +60 -0
  325. package/blocks/pages/lib/studio.ts +7 -0
  326. package/blocks/phone-numbers/README.md +74 -0
  327. package/blocks/phone-numbers/block.json +262 -0
  328. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  329. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  330. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  331. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  332. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  333. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  334. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  335. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  336. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  337. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  338. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  339. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  340. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  341. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  342. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  343. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  344. package/blocks/pickers/README.md +23 -0
  345. package/blocks/pickers/block.json +103 -0
  346. package/blocks/pickers/components/company-picker.tsx +90 -0
  347. package/blocks/pickers/components/contact-picker.tsx +131 -0
  348. package/blocks/pickers/components/member-picker.tsx +99 -0
  349. package/blocks/pickers/components/search-picker.tsx +98 -0
  350. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  351. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  352. package/blocks/pickers/lib/use-debounced.ts +13 -0
  353. package/blocks/programmatic-pages/README.md +82 -0
  354. package/blocks/programmatic-pages/block.json +292 -0
  355. package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
  356. package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
  357. package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
  358. package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
  359. package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
  360. package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
  361. package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
  362. package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
  363. package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
  364. package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
  365. package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
  366. package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
  367. package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
  368. package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
  369. package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
  370. package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
  371. package/blocks/quotes/README.md +38 -1
  372. package/blocks/quotes/block.json +170 -6
  373. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  374. package/blocks/quotes/components/manual-products.tsx +144 -0
  375. package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
  376. package/blocks/quotes/components/quote-builder.tsx +1 -1
  377. package/blocks/quotes/components/quote-documents.tsx +94 -0
  378. package/blocks/quotes/components/quote-evidence.tsx +95 -0
  379. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  380. package/blocks/quotes/components/quote-list.tsx +28 -8
  381. package/blocks/quotes/components/quote-related.tsx +3 -2
  382. package/blocks/quotes/components/quote-settings.tsx +184 -0
  383. package/blocks/quotes/components/quote-templates.tsx +189 -0
  384. package/blocks/quotes/components/quote-viewer.tsx +23 -5
  385. package/blocks/quotes/components/quotes-screen.tsx +15 -5
  386. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  387. package/blocks/quotes/lib/quote-admin.ts +264 -0
  388. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  389. package/blocks/radar/components/radar-overview.tsx +4 -4
  390. package/blocks/radar/components/radar-screen.tsx +2 -2
  391. package/blocks/sequencer/README.md +61 -0
  392. package/blocks/sequencer/block.json +200 -6
  393. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  394. package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
  395. package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
  396. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  397. package/blocks/sequencer/components/sequence-builder.tsx +32 -23
  398. package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
  399. package/blocks/sequencer/components/sequence-detail.tsx +10 -2
  400. package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
  401. package/blocks/sequencer/components/sequence-enrollments.tsx +5 -2
  402. package/blocks/sequencer/components/sequence-list.tsx +1 -1
  403. package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
  404. package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
  405. package/blocks/sequencer/components/sequence-stats.tsx +8 -8
  406. package/blocks/sequencer/components/sequence-templates.tsx +160 -0
  407. package/blocks/sequencer/components/sequences-screen.tsx +39 -6
  408. package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
  409. package/blocks/sequencer/components/step-editor.tsx +21 -12
  410. package/blocks/sequencer/components/step-templates.tsx +224 -0
  411. package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
  412. package/blocks/sequencer/lib/sequencer-utils.ts +9 -5
  413. package/blocks/signals/README.md +39 -0
  414. package/blocks/signals/block.json +287 -0
  415. package/blocks/signals/components/auto-seed-panel.tsx +111 -0
  416. package/blocks/signals/components/listener-form.tsx +184 -0
  417. package/blocks/signals/components/matched-posts.tsx +195 -0
  418. package/blocks/signals/components/post-engagements.tsx +114 -0
  419. package/blocks/signals/components/record-social-tab.tsx +49 -0
  420. package/blocks/signals/components/social-listeners-screen.tsx +250 -0
  421. package/blocks/signals/components/social-settings.tsx +128 -0
  422. package/blocks/signals/components/source-lists.tsx +83 -0
  423. package/blocks/signals/components/top-engagers.tsx +68 -0
  424. package/blocks/signals/components/x-integration.tsx +129 -0
  425. package/blocks/signals/lib/signals-types.ts +228 -0
  426. package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
  427. package/blocks/signals/recipes/spend-against-caps.ts +14 -0
  428. package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
  429. package/blocks/social-ads/README.md +8 -0
  430. package/blocks/social-ads/block.json +137 -4
  431. package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
  432. package/blocks/social-ads/components/audience-sync.tsx +6 -2
  433. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  434. package/blocks/social-ads/components/campaign-detail.tsx +6 -1
  435. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  436. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  437. package/blocks/social-ads/components/creative-detail.tsx +6 -1
  438. package/blocks/social-ads/components/creative-gallery.tsx +20 -2
  439. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  440. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  441. package/blocks/social-ads/components/post-composer.tsx +4 -2
  442. package/blocks/social-ads/components/post-detail.tsx +3 -0
  443. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  444. package/blocks/social-ads/components/social-performance.tsx +80 -0
  445. package/blocks/social-ads/components/social-screen.tsx +33 -6
  446. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  447. package/blocks/surveys/README.md +91 -0
  448. package/blocks/surveys/block.json +299 -0
  449. package/blocks/surveys/components/account-nps.tsx +69 -0
  450. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  451. package/blocks/surveys/components/org-summary.tsx +82 -0
  452. package/blocks/surveys/components/sending-domains.tsx +113 -0
  453. package/blocks/surveys/components/survey-form.tsx +126 -0
  454. package/blocks/surveys/components/survey-responses.tsx +145 -0
  455. package/blocks/surveys/components/survey-results.tsx +72 -0
  456. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  457. package/blocks/surveys/components/survey-send.tsx +162 -0
  458. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  459. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  460. package/blocks/surveys/lib/surveys-types.ts +228 -0
  461. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  462. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  463. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  464. package/blocks/visitors/components/snippet-install.tsx +18 -9
  465. package/blocks/visitors/components/traffic-chart.tsx +7 -6
  466. package/blocks/visitors/components/visitor-company-card.tsx +24 -14
  467. package/blocks/visitors/components/visitor-feed.tsx +5 -5
  468. package/blocks/web-analytics/README.md +102 -0
  469. package/blocks/web-analytics/block.json +292 -0
  470. package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
  471. package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
  472. package/blocks/web-analytics/components/audience-tab.tsx +135 -0
  473. package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
  474. package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
  475. package/blocks/web-analytics/components/overview-tab.tsx +102 -0
  476. package/blocks/web-analytics/components/pages-tab.tsx +148 -0
  477. package/blocks/web-analytics/components/ranked-table.tsx +103 -0
  478. package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
  479. package/blocks/web-analytics/components/report-card.tsx +71 -0
  480. package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
  481. package/blocks/web-analytics/hooks/use-report.ts +48 -0
  482. package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
  483. package/blocks/web-analytics/lib/page-path.ts +14 -0
  484. package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
  485. package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
  486. package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
  487. package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
  488. package/blocks/webchat/components/chat-widget-config.tsx +1 -1
  489. package/blocks/webchat/components/conversation-list.tsx +1 -1
  490. package/blocks/workbench/README.md +54 -2
  491. package/blocks/workbench/block.json +234 -5
  492. package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
  493. package/blocks/workbench/components/run-progress.tsx +1 -1
  494. package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
  495. package/blocks/workbench/components/workbench-automation.tsx +36 -0
  496. package/blocks/workbench/components/workbench-connections.tsx +136 -0
  497. package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
  498. package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
  499. package/blocks/workbench/components/workbench-grid.tsx +71 -8
  500. package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
  501. package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
  502. package/blocks/workbench/components/workbench-schedule.tsx +191 -0
  503. package/blocks/workbench/components/workbench-screen.tsx +65 -22
  504. package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
  505. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  506. package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
  507. package/blocks/workbench/components/workbench-triggers.tsx +178 -0
  508. package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
  509. package/blocks/workbench/lib/api-status.ts +8 -0
  510. package/blocks/workbench/lib/automation-types.ts +257 -0
  511. package/blocks/workflows/components/workflow-editor.tsx +1 -1
  512. package/dist/cli.js +744 -394
  513. package/dist/index.browser.js +1 -1
  514. package/dist/index.browser.js.map +1 -1
  515. package/dist/index.browser.mjs +1 -1
  516. package/dist/index.browser.mjs.map +1 -1
  517. package/dist/index.d.mts +2 -2
  518. package/dist/index.d.ts +2 -2
  519. package/dist/index.js +1 -1
  520. package/dist/index.js.map +1 -1
  521. package/dist/index.mjs +1 -1
  522. package/dist/index.mjs.map +1 -1
  523. package/dist/react.browser.js +1 -1
  524. package/dist/react.browser.js.map +1 -1
  525. package/dist/react.browser.mjs +1 -1
  526. package/dist/react.browser.mjs.map +1 -1
  527. package/dist/react.d.mts +2 -2
  528. package/dist/react.d.ts +2 -2
  529. package/dist/react.js +1 -1
  530. package/dist/react.js.map +1 -1
  531. package/dist/react.mjs +1 -1
  532. package/dist/react.mjs.map +1 -1
  533. package/dist/types/blocks/auth.d.ts +45 -0
  534. package/dist/types/blocks/brand.d.ts +11 -2
  535. package/dist/types/blocks/create.d.ts +11 -4
  536. package/dist/types/blocks/doctor.d.ts +1 -0
  537. package/dist/types/blocks/flows.d.ts +7 -1
  538. package/dist/types/blocks/orgBrand.d.ts +34 -0
  539. package/dist/types/cli.d.ts +3 -0
  540. package/dist/types/version.d.ts +2 -2
  541. package/docs/api/desk.d.ts +2 -2
  542. package/docs/api/desk.md +3 -3
  543. package/docs/api/forms.md +1 -1
  544. package/package.json +1 -1
  545. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  546. package/stacks/6sense/pages/contacts.tsx.tmpl +21 -0
  547. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  548. package/stacks/6sense.json +37 -4
  549. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  550. package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
  551. package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
  552. package/stacks/apollo/pages/contacts.tsx.tmpl +35 -95
  553. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  554. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  555. package/stacks/apollo/pages/lists.tsx.tmpl +24 -0
  556. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  557. package/stacks/apollo/pages/sequences.tsx.tmpl +18 -0
  558. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  559. package/stacks/apollo.json +68 -9
  560. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  561. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  562. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  563. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  564. package/stacks/calendly.json +8 -4
  565. package/stacks/clay/pages/companies.tsx.tmpl +17 -1
  566. package/stacks/clay/pages/contacts.tsx.tmpl +14 -0
  567. package/stacks/clay/pages/lists.tsx.tmpl +24 -0
  568. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  569. package/stacks/clay.json +16 -3
  570. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  571. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  572. package/stacks/gong/pages/contacts.tsx.tmpl +34 -0
  573. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  574. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  575. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  576. package/stacks/gong.json +23 -6
  577. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  578. package/stacks/hootsuite/pages/contacts.tsx.tmpl +30 -0
  579. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  580. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  581. package/stacks/hootsuite.json +19 -4
  582. package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
  583. package/stacks/hubspot/pages/companies.tsx.tmpl +16 -35
  584. package/stacks/hubspot/pages/contacts.tsx.tmpl +35 -95
  585. package/stacks/hubspot/pages/deals.tsx.tmpl +14 -1
  586. package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
  587. package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
  588. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  589. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  590. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  591. package/stacks/hubspot/pages/sequences.tsx.tmpl +18 -0
  592. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  593. package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
  594. package/stacks/hubspot.json +66 -7
  595. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  596. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  597. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  598. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  599. package/stacks/intercom.json +18 -4
  600. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  601. package/stacks/mailchimp/pages/contacts.tsx.tmpl +30 -0
  602. package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
  603. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  604. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  605. package/stacks/mailchimp.json +22 -6
  606. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  607. package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
  608. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  609. package/stacks/outreach/pages/contacts.tsx.tmpl +29 -66
  610. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  611. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  612. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  613. package/stacks/outreach/pages/sequences.tsx.tmpl +18 -0
  614. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  615. package/stacks/outreach.json +38 -9
  616. package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
  617. package/stacks/pipedrive/pages/contacts.tsx.tmpl +17 -35
  618. package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
  619. package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
  620. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  621. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  622. package/stacks/pipedrive.json +14 -2
  623. package/stacks/salesforce/pages/companies.tsx.tmpl +16 -35
  624. package/stacks/salesforce/pages/contacts.tsx.tmpl +26 -35
  625. package/stacks/salesforce/pages/deals.tsx.tmpl +14 -1
  626. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  627. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  628. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  629. package/stacks/salesforce.json +17 -3
  630. package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
  631. package/stacks/zoominfo/pages/contacts.tsx.tmpl +16 -0
  632. package/stacks/zoominfo/pages/lists.tsx.tmpl +24 -0
  633. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  634. package/stacks/zoominfo.json +36 -3
  635. package/styles/styles.css +1 -1
  636. package/templates/auth/auth-route.ts.tmpl +16 -0
  637. package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
  638. package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
  639. package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
  640. package/templates/auth/g8.auth.password.ts.tmpl +18 -0
  641. package/templates/auth/g8.auth.session.ts.tmpl +23 -0
  642. package/templates/auth/middleware.ts.tmpl +14 -0
  643. package/templates/next-app/README.md.tmpl +5 -0
  644. package/templates/next-app/app/globals.css.tmpl +349 -64
  645. package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
  646. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
@@ -0,0 +1,228 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Types, vocabularies and pure helpers for the signals block's social listening part.
5
+ *
6
+ * Every response of the social-listeners routes is an OPEN object in the contract, so the row types below follow the
7
+ * app's own models (social_listener/interfaces/fastapi/listeners_router.py), which the dev API forwards unchanged.
8
+ * Enums are copied from the request schemas (generated/schemas.ts), not from memory: the API refuses anything else.
9
+ * A count the channel did not report is null and shown as a dash, never as 0. Calls nothing.
10
+ */
11
+
12
+ export type Platform = "x" | "linkedin" | "reddit" | "instagram" | "youtube";
13
+ export type ListenerType = "brand" | "competitor" | "keyword" | "engagement" | "user";
14
+ export type PostStatus = "new" | "reviewed" | "dismissed";
15
+ export type EngagementType = "comment" | "reaction";
16
+
17
+ export const PLATFORMS: { value: Platform; label: string }[] = [
18
+ { value: "x", label: "X" },
19
+ { value: "linkedin", label: "LinkedIn" },
20
+ { value: "reddit", label: "Reddit" },
21
+ { value: "instagram", label: "Instagram" },
22
+ { value: "youtube", label: "YouTube" },
23
+ ];
24
+ export const LISTENER_TYPES: { value: ListenerType; label: string; hint: string }[] = [
25
+ { value: "brand", label: "Brand", hint: "Posts that mention your brand or products." },
26
+ { value: "competitor", label: "Competitor", hint: "Posts that mention a competitor." },
27
+ { value: "keyword", label: "Keyword", hint: "Posts that match a search query." },
28
+ { value: "engagement", label: "Engagement", hint: "Posts from people who look like your buyers." },
29
+ { value: "user", label: "Account", hint: "Everything one account posts." },
30
+ ];
31
+ export const POST_STATUSES: { value: PostStatus; label: string }[] = [
32
+ { value: "new", label: "New" },
33
+ { value: "reviewed", label: "Reviewed" },
34
+ { value: "dismissed", label: "Dismissed" },
35
+ ];
36
+
37
+ /** Minimum the API accepts for a poll interval (seconds). */
38
+ export const MIN_POLL_SEC = 60;
39
+ /** The API's page-size cap for the post and engagement reads. */
40
+ export const PAGE_SIZE = 100;
41
+
42
+ /**
43
+ * Credits per language-model pass of an auto-seed or a generated query. The API publishes no price; this is the backend
44
+ * constant (credit_server `social_listener_brand_query_build`, mirrored in frontend/features/social-listener/social-listener.costs.ts).
45
+ */
46
+ export const AI_PASS_CREDITS = 5;
47
+
48
+ export interface Listener {
49
+ id: string;
50
+ platform: string;
51
+ listener_type: string;
52
+ name: string;
53
+ query?: string | null;
54
+ target_handle?: string | null;
55
+ filters?: Record<string, unknown> | null;
56
+ poll_interval_sec?: number | null;
57
+ monthly_credit_cap?: number | null;
58
+ is_active: boolean;
59
+ paused_reason?: string | null;
60
+ last_polled_at?: string | null;
61
+ source_list_id?: string | null;
62
+ created_at?: string | null;
63
+ updated_at?: string | null;
64
+ }
65
+ export interface ListenerDetailData { listener: Listener; match_count?: number | null; credit_burn?: number | null }
66
+
67
+ export interface MatchedPost {
68
+ post_external_id: string;
69
+ platform: string;
70
+ matched_listener_ids?: string[];
71
+ author_handle?: string | null;
72
+ author_name?: string | null;
73
+ author_followers?: number | null;
74
+ author_verified?: boolean | null;
75
+ author_profile_image_url?: string | null;
76
+ text?: string | null;
77
+ post_url?: string | null;
78
+ created_at?: string | null;
79
+ like_count?: number | null;
80
+ reply_count?: number | null;
81
+ repost_count?: number | null;
82
+ view_count?: number | null;
83
+ classifier_score?: number | null;
84
+ engagement_intent?: string | null;
85
+ resolved_mashup_contact_id?: string | null;
86
+ status?: string | null;
87
+ }
88
+ export interface PostEngagement {
89
+ engagement_external_id: string;
90
+ engagement_type: string;
91
+ reaction_type?: string | null;
92
+ author_handle?: string | null;
93
+ author_name?: string | null;
94
+ author_url?: string | null;
95
+ author_headline?: string | null;
96
+ text?: string | null;
97
+ created_at?: string | null;
98
+ }
99
+ export interface TopAccount {
100
+ author_external_id: string;
101
+ author_handle?: string | null;
102
+ author_name?: string | null;
103
+ author_url?: string | null;
104
+ author_headline?: string | null;
105
+ platform: string;
106
+ posts_engaged?: number | null;
107
+ comments?: number | null;
108
+ reactions?: number | null;
109
+ last_engaged_at?: string | null;
110
+ }
111
+ export interface SourceList {
112
+ audience_id: number;
113
+ list_title?: string | null;
114
+ audience_type?: string | null;
115
+ contact_count?: number | null;
116
+ is_active?: boolean | null;
117
+ is_dynamic?: boolean | null;
118
+ last_synced_at?: string | null;
119
+ total_listeners_created?: number | null;
120
+ channel_coverage?: Record<string, number> | null;
121
+ }
122
+ export interface SocialStats {
123
+ monthly_credit_burn?: number | null;
124
+ posts_matched?: number | null;
125
+ posts_reviewed?: number | null;
126
+ posts_dismissed?: number | null;
127
+ contacts_resolved?: number | null;
128
+ x_api_spend_usd?: number | null;
129
+ listeners_active?: number | null;
130
+ listeners_paused?: number | null;
131
+ }
132
+ export interface ChannelRule { enabled?: boolean; monthly_credit_cap?: number; per_poll_result_cap?: number; max_comments_per_post?: number; max_reactions_per_post?: number; fetch_engagements?: boolean }
133
+ export interface CaptureRule { enabled?: boolean; min_followers?: number; only_when_classified?: boolean }
134
+ export interface SocialSettings {
135
+ monthly_credit_cap?: number | null;
136
+ other_channels_monthly_credit_cap?: number | null;
137
+ alert_threshold_pct?: number | null;
138
+ auto_pause_on_cap?: boolean | null;
139
+ auto_archive_days?: number | null;
140
+ auto_create_contacts?: boolean | null;
141
+ classifier_model?: string | null;
142
+ channel_settings?: Record<string, ChannelRule> | null;
143
+ contact_capture?: Record<string, CaptureRule> | null;
144
+ }
145
+ export interface XStatus { connected?: boolean; mode?: string | null; x_app_username?: string | null; connected_at?: string | null; degraded_reason?: string | null }
146
+ export interface XMode { auth_mode?: string | null; byo_connected?: boolean | null; shared_available?: boolean | null; shared_cap_total?: number | null; shared_cap_remaining?: number | null }
147
+ export interface SeedJob { job_id?: string; status?: string; stage?: string; error?: string | null; result?: { brand_created?: number; competitors_created?: number; keywords_created?: number; engagement_created?: number; skipped?: string[] } | null }
148
+ export interface CostPreview { posts_per_day_estimate?: number | null; credits_per_month_estimate?: number | null; x_usd_per_month_estimate?: number | null; cached?: boolean; note?: string | null }
149
+
150
+ /** The rows of an open `{ items: [] }` payload (the dev API surfaces every list as `items`). */
151
+ export function itemsOf<T>(data: unknown): T[] {
152
+ const d = data as { items?: unknown } | T[] | null | undefined;
153
+ if (Array.isArray(d)) return d;
154
+ return Array.isArray(d?.items) ? (d.items as T[]) : [];
155
+ }
156
+ export function totalOf(data: unknown): number | undefined {
157
+ const t = (data as { total?: unknown } | null | undefined)?.total;
158
+ return typeof t === "number" ? t : undefined;
159
+ }
160
+
161
+ export const platformLabel = (p: string | null | undefined): string => PLATFORMS.find((x) => x.value === p)?.label ?? (p ? p : "");
162
+ export const typeLabel = (t: string | null | undefined): string => LISTENER_TYPES.find((x) => x.value === t)?.label ?? (t ? t : "");
163
+ /** A number, or null when the channel did not report one (unknown is not zero). */
164
+ export const num = (n: number | null | undefined): number | null => (typeof n === "number" && Number.isFinite(n) ? n : null);
165
+ export const fmt = (n: number | null | undefined): string => { const v = num(n); return v === null ? "-" : v.toLocaleString(); };
166
+ export const when = (iso: string | null | undefined): string => {
167
+ if (!iso) return "-";
168
+ const t = Date.parse(iso);
169
+ return Number.isNaN(t) ? "-" : new Date(t).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" });
170
+ };
171
+ export const cadence = (sec: number | null | undefined): string => {
172
+ const s = num(sec);
173
+ if (s === null) return "-";
174
+ if (s % 3600 === 0) return `every ${s / 3600} h`;
175
+ if (s % 60 === 0) return `every ${s / 60} min`;
176
+ return `every ${s} s`;
177
+ };
178
+ /** Credits per month a poll cadence cannot bound: only the cap does, so the cap is what a confirm states. */
179
+ export const capText = (cap: number | null | undefined): string => {
180
+ const c = num(cap);
181
+ return c === null ? "no cap reported" : c === 0 ? "no per-listener cap" : `capped at ${c.toLocaleString()} credits a month`;
182
+ };
183
+ export const statusOf = (l: Pick<Listener, "is_active">): "active" | "paused" => (l.is_active ? "active" : "paused");
184
+
185
+ /**
186
+ * Only the fields that changed. A cleared text or number is NOT sent: the dev API builds the inner body with
187
+ * `model_dump(exclude_none=True)`, so a null would be dropped and the save would report something that never happened.
188
+ */
189
+ export function listenerDiff(before: Listener, next: { name: string; query: string; target_handle: string; poll_interval_sec: number | null; monthly_credit_cap: number | null }): Record<string, unknown> {
190
+ const out: Record<string, unknown> = {};
191
+ if (next.name.trim() !== before.name) out.name = next.name.trim();
192
+ if (next.query.trim() && next.query.trim() !== (before.query ?? "")) out.query = next.query.trim();
193
+ if (next.target_handle.trim() && next.target_handle.trim() !== (before.target_handle ?? "")) out.target_handle = next.target_handle.trim();
194
+ if (next.poll_interval_sec !== null && next.poll_interval_sec !== num(before.poll_interval_sec)) out.poll_interval_sec = next.poll_interval_sec;
195
+ if (next.monthly_credit_cap !== null && next.monthly_credit_cap !== num(before.monthly_credit_cap)) out.monthly_credit_cap = next.monthly_credit_cap;
196
+ return out;
197
+ }
198
+
199
+ /** A page of a POST-search read: rows plus whether another page follows (from the reported total, else a full page). */
200
+ export function morePages(data: unknown, rows: number, page: number, size: number): boolean {
201
+ const t = totalOf(data);
202
+ return t !== undefined ? page * size < t : rows >= size;
203
+ }
204
+
205
+ /** The sequences a person can enrol into: live ones only. */
206
+ export interface SequenceRow { id: string; name?: string | null; status?: string | null }
207
+ export const isLiveSequence = (s: SequenceRow): boolean => ["live", "waiting", "running", "active"].includes(String(s.status ?? "").toLowerCase());
208
+
209
+ /** Top-level numeric settings: key, label, max (when the API bounds it). */
210
+ export const SETTING_NUMBERS: { key: "monthly_credit_cap" | "other_channels_monthly_credit_cap" | "alert_threshold_pct" | "auto_archive_days"; label: string; max?: number; hint: string }[] = [
211
+ { key: "monthly_credit_cap", label: "Monthly credit cap (X)", hint: "The org-wide ceiling on social listening credits." },
212
+ { key: "other_channels_monthly_credit_cap", label: "Monthly credit cap (other channels)", hint: "LinkedIn, Reddit, Instagram and YouTube together. 0 means no org-wide ceiling." },
213
+ { key: "alert_threshold_pct", label: "Alert at (% of cap)", max: 100, hint: "You are alerted when spend reaches this share of the cap." },
214
+ { key: "auto_archive_days", label: "Archive posts after (days)", hint: "Older matched posts are archived." },
215
+ ];
216
+ export const CHANNEL_NUMBERS: { key: "monthly_credit_cap" | "per_poll_result_cap" | "max_comments_per_post" | "max_reactions_per_post"; label: string }[] = [
217
+ { key: "monthly_credit_cap", label: "Monthly credit cap" },
218
+ { key: "per_poll_result_cap", label: "Posts per poll" },
219
+ { key: "max_comments_per_post", label: "Comments per post" },
220
+ { key: "max_reactions_per_post", label: "Reactions per post" },
221
+ ];
222
+
223
+ /** Whole non-negative integer from a text box, or null when empty / invalid. */
224
+ export function wholeNumber(text: string, max?: number): number | null {
225
+ if (text.trim() === "") return null;
226
+ const n = Number(text);
227
+ return Number.isInteger(n) && n >= 0 && (max === undefined || n <= max) ? n : null;
228
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @title Estimate a listener's cost, then create it with a monthly cap
3
+ * @when Starting to watch a keyword or competitor. Creating a listener makes it poll, and reading posts spends credits, so
4
+ * a person confirms the cap first. The estimate charges nothing.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ export async function previewThenCreate(query: string, name: string, monthlyCreditCap: number, approved: boolean): Promise<{ estimateCreditsPerMonth: unknown; listenerId?: string }> {
11
+ const est = await g8.api.signals.previewListenerCost({ body: { listener_type: "keyword", platform: "x", query } });
12
+ const estimateCreditsPerMonth = (est.data as { credits_per_month_estimate?: unknown }).credits_per_month_estimate;
13
+ if (!approved) return { estimateCreditsPerMonth };
14
+ const made = await g8.api.signals.createListener({ body: { listener_type: "keyword", platform: "x", name, query, monthly_credit_cap: monthlyCreditCap } });
15
+ return { estimateCreditsPerMonth, listenerId: (made.data as { id?: string }).id };
16
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @title Compare this month's credit spend with the org-wide cap
3
+ * @when A budget alert for social listening. A cap of 0 means no org-wide ceiling, which is not "at the limit".
4
+ */
5
+ import { g8 } from "@graph8/sdk";
6
+
7
+ g8.init({ apiKey: process.env.G8_API_KEY! });
8
+
9
+ export async function spendVsCap(): Promise<{ spent: number | null; cap: number | null; share: number | null }> {
10
+ const [stats, settings] = await Promise.all([g8.api.signals.getSocialStats({}), g8.api.signals.getSocialSettings({})]);
11
+ const spent = (stats.data as { monthly_credit_burn?: number }).monthly_credit_burn ?? null;
12
+ const cap = (settings.data as { monthly_credit_cap?: number }).monthly_credit_cap ?? null;
13
+ return { spent, cap, share: spent !== null && cap ? spent / cap : null };
14
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @title Read new matched posts and mark them reviewed
3
+ * @when A daily triage queue for social signals. The read spends nothing (it reads what the worker stored); a post is
4
+ * identified by its channel AND its id.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ export async function newPosts(limit = 50): Promise<Array<{ platform: string; id: string; text: string | null }>> {
11
+ const res = await g8.api.signals.searchMatchedPosts({ body: { status: "new", limit, page: 1 } });
12
+ const rows = ((res.data as { items?: Array<{ platform: string; post_external_id: string; text?: string | null }> }).items ?? []);
13
+ return rows.map((p) => ({ platform: p.platform, id: p.post_external_id, text: p.text ?? null }));
14
+ }
15
+
16
+ export async function markReviewed(platform: "x" | "linkedin" | "reddit" | "instagram" | "youtube", postId: string): Promise<void> {
17
+ await g8.api.signals.markPost({ path: { post_id: postId }, query: { platform }, body: { status: "reviewed" } });
18
+ }
@@ -137,3 +137,11 @@ Ask the person: campaign daily budget, dates and destination URL (always typed b
137
137
  | updateAdCreative | PATCH /ad-creatives/{creative_id} | write | CreativeGallery |
138
138
  | updateAudienceSync | PATCH /audience-syncs/{config_id} | external | AudienceSync |
139
139
  | updateSocialPost | PATCH /social/posts/{post_id} | write | PostComposer |
140
+
141
+ ## Depth added (C7c)
142
+
143
+ - `CampaignDetail` gains **Ads** (`CampaignAds`: list, edit, pause or activate, archive, add; all external or destructive confirms) and **Edit** (`CampaignEditor`: name, daily budget, end date, spend cap, pause or resume; previewed, then an external confirm; fields the platform refuses are disabled with its reason).
144
+ - `CreativeDetail` gains **AI tools** (`CreativeTools`: edit by instruction, rewrite a field with undo, review and apply, other sizes, retry, move to draft, save as template; billable passes are confirmed) and **Revisions** (`CreativeRevisions`). `CreativeGallery` loads past 50 and has a **Concept board** (`CreativeConcepts`, billable confirm).
145
+ - `SocialScreen` gains **Ideas** (`SocialIdeas`, Draft a post opens the composer with the brief), **Performance** (`SocialPerformance`), a per-post Performance tab, and Disconnect on an account (destructive confirm).
146
+
147
+ Not covered (and why): `createAdCreativeSet` and `readAdCreativeThroughPersona` need persona pickers; video clips, image-point edits, reskin, set-image and library ops need pickers or media editing; `uploadAdImage` is multipart; social autopilot (`saveSocialAutopilot`) auto-publishes and engagers (`addSocialEngagersToAudience`, `enrollSocialEngagers`) need list and sequence pickers; `getAudienceSync` is already covered by the list read.
@@ -14,39 +14,64 @@
14
14
  "lists"
15
15
  ],
16
16
  "operations": [
17
+ "apply_ad_creative_review_findings_ad_creatives__creative_id__ai_review_apply_post",
18
+ "archive_campaign_ad_ad_campaigns__campaign_id__ads__ad_id__delete",
17
19
  "cancel_social_post_social_posts__post_id__cancel_post",
20
+ "chat_edit_ad_creative_ad_creatives__creative_id__chat_edit_post",
18
21
  "create_ad_creative_ad_creatives_post",
22
+ "create_ad_creative_size_set_ad_creatives__creative_id__size_set_post",
19
23
  "create_audience_sync_audience_syncs_post",
24
+ "create_campaign_ad_ad_campaigns__campaign_id__ads_post",
20
25
  "create_social_post_social_posts_post",
21
26
  "delete_ad_campaign_ad_campaigns__campaign_id__delete",
22
27
  "delete_ad_creative_ad_creatives__creative_id__delete",
23
28
  "delete_audience_sync_audience_syncs__config_id__delete",
24
29
  "delete_social_post_social_posts__post_id__delete",
30
+ "disconnect_social_account_social_accounts__account_id__delete",
31
+ "generate_ad_concepts_ad_creatives_concepts_post",
25
32
  "generate_social_post_social_posts_generate_post",
33
+ "get_ad_campaign_ad_campaigns__campaign_id__get",
34
+ "get_ad_creative_ad_creatives__creative_id__get",
26
35
  "get_ad_creative_specs_ad_creatives_specs_get",
27
36
  "get_ad_creative_status_ad_creatives__creative_id__status_get",
37
+ "get_campaign_editable_fields_ad_campaigns__campaign_id__editable_get",
28
38
  "get_per_campaign_metrics_ad_campaigns_metrics_get",
39
+ "get_social_analytics_social_analytics_get",
40
+ "get_social_post_metrics_social_posts__post_id__metrics_get",
29
41
  "get_social_post_social_posts__post_id__get",
30
42
  "launch_ad_campaign_audience_syncs__config_id__launch_post",
31
43
  "list_ad_campaigns_ad_campaigns_get",
44
+ "list_ad_creative_revisions_ad_creatives__creative_id__revisions_get",
32
45
  "list_ad_creatives_ad_creatives_get",
33
46
  "list_audience_syncs_audience_syncs_get",
47
+ "list_campaign_ads_ad_campaigns__campaign_id__ads_get",
34
48
  "list_social_accounts_social_accounts_get",
49
+ "list_social_ideas_social_ideas_get",
35
50
  "list_social_posts_social_posts_get",
36
51
  "list_sync_errors_audience_syncs__config_id__errors_get",
37
52
  "list_sync_runs_audience_syncs__config_id__runs_get",
53
+ "move_ad_creative_to_draft_ad_creatives__creative_id__move_to_draft_post",
38
54
  "move_ad_creative_to_gallery_ad_creatives__creative_id__move_to_gallery_post",
55
+ "preview_ad_campaign_edit_ad_campaigns__campaign_id__edit_preview_post",
39
56
  "publish_social_post_social_posts__post_id__publish_post",
40
57
  "refresh_all_campaign_statuses_ad_campaigns_status_refresh_post",
41
58
  "refresh_campaign_status_ad_campaigns__campaign_id__status_refresh_post",
59
+ "regenerate_ad_creative_copy_ad_creatives__creative_id__regenerate_copy_post",
42
60
  "regenerate_ad_creative_image_ad_creatives__creative_id__regenerate_image_post",
61
+ "restore_ad_creative_revision_ad_creatives__creative_id__restore_revision_post",
62
+ "retry_ad_creative_generation_ad_creatives__creative_id__retry_post",
43
63
  "retry_campaign_launch_ad_campaigns__campaign_id__retry_post",
64
+ "review_ad_creative_ad_creatives__creative_id__ai_review_post",
65
+ "rewrite_ad_creative_field_ad_creatives__creative_id__rewrite_field_post",
66
+ "save_ad_creative_as_template_ad_creatives__creative_id__save_as_template_post",
44
67
  "schedule_social_post_social_posts__post_id__schedule_post",
68
+ "set_campaign_ad_status_ad_campaigns__campaign_id__ads__ad_id__status_post",
45
69
  "trigger_audience_sync_audience_syncs__config_id__trigger_post",
70
+ "update_ad_campaign_ad_campaigns__campaign_id__put",
46
71
  "update_ad_creative_ad_creatives__creative_id__patch",
47
72
  "update_audience_sync_audience_syncs__config_id__patch",
48
- "update_social_post_social_posts__post_id__patch",
49
- "get_ad_creative_ad_creatives__creative_id__get"
73
+ "update_campaign_ad_ad_campaigns__campaign_id__ads__ad_id__patch",
74
+ "update_social_post_social_posts__post_id__patch"
50
75
  ],
51
76
  "components": [
52
77
  {
@@ -146,10 +171,12 @@
146
171
  "file": "components/social-screen.tsx",
147
172
  "purpose": "Finished social page: posts by status with a New post composer sheet, and connected accounts.",
148
173
  "operations": [
149
- "list_social_posts_social_posts_get"
174
+ "list_social_posts_social_posts_get",
175
+ "disconnect_social_account_social_accounts__account_id__delete"
150
176
  ],
151
177
  "tiers": [
152
- "read"
178
+ "read",
179
+ "destructive"
153
180
  ]
154
181
  },
155
182
  {
@@ -237,6 +264,108 @@
237
264
  "external",
238
265
  "destructive"
239
266
  ]
267
+ },
268
+ {
269
+ "name": "CampaignAds",
270
+ "file": "components/campaign-ads.tsx",
271
+ "purpose": "The ads inside a campaign: list, edit, pause or activate, archive and add (each confirmed).",
272
+ "operations": [
273
+ "update_campaign_ad_ad_campaigns__campaign_id__ads__ad_id__patch",
274
+ "list_campaign_ads_ad_campaigns__campaign_id__ads_get",
275
+ "set_campaign_ad_status_ad_campaigns__campaign_id__ads__ad_id__status_post",
276
+ "archive_campaign_ad_ad_campaigns__campaign_id__ads__ad_id__delete",
277
+ "create_campaign_ad_ad_campaigns__campaign_id__ads_post"
278
+ ],
279
+ "tiers": [
280
+ "read",
281
+ "external",
282
+ "destructive"
283
+ ]
284
+ },
285
+ {
286
+ "name": "CampaignEditor",
287
+ "file": "components/campaign-editor.tsx",
288
+ "purpose": "Edit a live ad campaign: name, budget, end date, spend cap, pause or resume; previewed, then confirmed.",
289
+ "operations": [
290
+ "preview_ad_campaign_edit_ad_campaigns__campaign_id__edit_preview_post",
291
+ "update_ad_campaign_ad_campaigns__campaign_id__put",
292
+ "get_ad_campaign_ad_campaigns__campaign_id__get",
293
+ "get_campaign_editable_fields_ad_campaigns__campaign_id__editable_get"
294
+ ],
295
+ "tiers": [
296
+ "read",
297
+ "external"
298
+ ]
299
+ },
300
+ {
301
+ "name": "CreativeConcepts",
302
+ "file": "components/creative-concepts.tsx",
303
+ "purpose": "Concept board: creative directions to react to before generating (confirmed AI spend).",
304
+ "operations": [
305
+ "generate_ad_concepts_ad_creatives_concepts_post"
306
+ ],
307
+ "tiers": [
308
+ "billable"
309
+ ]
310
+ },
311
+ {
312
+ "name": "CreativeRevisions",
313
+ "file": "components/creative-revisions.tsx",
314
+ "purpose": "Saved versions of an ad creative with restore.",
315
+ "operations": [
316
+ "list_ad_creative_revisions_ad_creatives__creative_id__revisions_get",
317
+ "restore_ad_creative_revision_ad_creatives__creative_id__restore_revision_post"
318
+ ],
319
+ "tiers": [
320
+ "read",
321
+ "write"
322
+ ]
323
+ },
324
+ {
325
+ "name": "CreativeTools",
326
+ "file": "components/creative-tools.tsx",
327
+ "purpose": "AI tools for a creative: edit by instruction, rewrite a field, review, other sizes, retry, template.",
328
+ "operations": [
329
+ "get_ad_creative_specs_ad_creatives_specs_get",
330
+ "update_ad_creative_ad_creatives__creative_id__patch",
331
+ "apply_ad_creative_review_findings_ad_creatives__creative_id__ai_review_apply_post",
332
+ "move_ad_creative_to_draft_ad_creatives__creative_id__move_to_draft_post",
333
+ "save_ad_creative_as_template_ad_creatives__creative_id__save_as_template_post",
334
+ "chat_edit_ad_creative_ad_creatives__creative_id__chat_edit_post",
335
+ "regenerate_ad_creative_copy_ad_creatives__creative_id__regenerate_copy_post",
336
+ "rewrite_ad_creative_field_ad_creatives__creative_id__rewrite_field_post",
337
+ "review_ad_creative_ad_creatives__creative_id__ai_review_post",
338
+ "create_ad_creative_size_set_ad_creatives__creative_id__size_set_post",
339
+ "retry_ad_creative_generation_ad_creatives__creative_id__retry_post"
340
+ ],
341
+ "tiers": [
342
+ "read",
343
+ "write",
344
+ "billable"
345
+ ]
346
+ },
347
+ {
348
+ "name": "SocialIdeas",
349
+ "file": "components/social-ideas.tsx",
350
+ "purpose": "Post ideas mined from signals and content, with a Draft a post action.",
351
+ "operations": [
352
+ "list_social_ideas_social_ideas_get"
353
+ ],
354
+ "tiers": [
355
+ "read"
356
+ ]
357
+ },
358
+ {
359
+ "name": "SocialPerformance",
360
+ "file": "components/social-performance.tsx",
361
+ "purpose": "Social post performance: totals and per-day numbers, and the numbers of one post.",
362
+ "operations": [
363
+ "get_social_analytics_social_analytics_get",
364
+ "get_social_post_metrics_social_posts__post_id__metrics_get"
365
+ ],
366
+ "tiers": [
367
+ "read"
368
+ ]
240
369
  }
241
370
  ],
242
371
  "hooks": [
@@ -253,6 +382,10 @@
253
382
  {
254
383
  "file": "lib/social-ads-types.ts",
255
384
  "purpose": "Post, account, campaign, creative and sync shapes; time helpers (localInputToIso, formatInstant, formatDateOnly), metricsFor and listOf."
385
+ },
386
+ {
387
+ "file": "lib/ads-extra.ts",
388
+ "purpose": "Helpers for the ads and social management components: open-dict readers, editable-field map, campaign edit diff."
256
389
  }
257
390
  ],
258
391
  "recipes": [
@@ -17,6 +17,7 @@
17
17
  import { useState } from "react";
18
18
  import { Button, Input, Label, Skeleton } from "@graph8/sdk/ui";
19
19
  import { ApiError } from "@/g8/components/api-error";
20
+ import { FirstNotice } from "@/g8/components/paging-notices";
20
21
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
21
22
  import { EmptyState } from "@/g8/components/empty-state";
22
23
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
@@ -55,10 +56,14 @@ interface SyncError { id: number; error_message?: string; started_at?: string }
55
56
 
56
57
  const EMPTY_LAUNCH = { name: "", objective: "", daily_budget: "", currency: "USD", destination_url: "", creative_image_url: "", headline: "", primary_text: "", start_date: "", end_date: "" };
57
58
 
59
+ /** The runs and errors APIs have no paging: a full read is announced under the list. */
60
+ const RUN_LIMIT = 20;
61
+ const ERROR_LIMIT = 10;
62
+
58
63
  function Runs({ syncId }: { syncId: number }) {
59
64
  const [confirm, setConfirm] = useState(false);
60
65
  const [runs, setRuns] = useState<SyncRun[] | null>(null);
61
- const load = useG8Mutation(async () => { setRuns(listOf<SyncRun>((await g8Get(`/audience-syncs/${syncId}/runs`, { limit: 20 })).data, "runs")); });
66
+ const load = useG8Mutation(async () => { setRuns(listOf<SyncRun>((await g8Get(`/audience-syncs/${syncId}/runs`, { limit: RUN_LIMIT })).data, "runs")); });
62
67
  return (
63
68
  <SectionCard title="Runs" description="Loading past runs is a billable read, so it asks first.">
64
69
  {runs == null ? <Button size="sm" variant="outline" onClick={() => setConfirm(true)}>Load runs</Button> : runs.length === 0 ? <p className="text-sm text-muted-foreground">No runs yet.</p> : (
@@ -73,6 +78,7 @@ function Runs({ syncId }: { syncId: number }) {
73
78
  ))}
74
79
  </ul>
75
80
  )}
81
+ {runs ? <FirstNotice shown={runs.length} limit={RUN_LIMIT} noun="runs" /> : null}
76
82
  {load.error ? <ApiError error={load.error} onRetry={load.reset} /> : null}
77
83
  <ConfirmSpendDialog open={confirm} tier="billable" title="Load sync runs" description="Listing the runs of a sync is classified billable by the API." confirmLabel="Load runs" confirming={load.loading}
78
84
  onCancel={() => setConfirm(false)} onConfirm={() => void load.mutate(undefined).then(() => setConfirm(false))} />
@@ -84,7 +90,7 @@ export function AudienceSyncDetail({ syncId, onClose, onExpand, onCollapse, onCh
84
90
  const brand = useBrand();
85
91
  const { ask, dialog, failure } = useConfirmAction();
86
92
  const list = useG8Query(async (signal) => listOf<AudienceSyncConfig>((await g8Get("/audience-syncs", undefined, { signal })).data), []);
87
- const errors = useG8Query(async (signal) => listOf<SyncError>((await g8Get(`/audience-syncs/${syncId}/errors`, { limit: 10 }, { signal })).data), [syncId]);
93
+ const errors = useG8Query(async (signal) => listOf<SyncError>((await g8Get(`/audience-syncs/${syncId}/errors`, { limit: ERROR_LIMIT }, { signal })).data), [syncId]);
88
94
  const [launch, setLaunch] = useState(EMPTY_LAUNCH);
89
95
  const [confirmLaunch, setConfirmLaunch] = useState(false);
90
96
  const [notice, setNotice] = useState<string | null>(null);
@@ -154,7 +160,7 @@ export function AudienceSyncDetail({ syncId, onClose, onExpand, onCollapse, onCh
154
160
  id: "errors",
155
161
  label: "Errors",
156
162
  content: errors.loading ? <Skeleton className="h-8 w-full" /> : errors.error ? <ApiError error={errors.error} onRetry={() => void errors.refetch()} /> : (errors.data ?? []).length === 0 ? <p className="text-sm text-muted-foreground">No recent errors.</p> : (
157
- <ul className="space-y-1 text-sm">{(errors.data ?? []).map((e) => <li key={e.id}>{e.started_at ? `${formatInstant(e.started_at)}: ` : ""}{e.error_message}</li>)}</ul>
163
+ <><ul className="space-y-1 text-sm">{(errors.data ?? []).map((e) => <li key={e.id}>{e.started_at ? `${formatInstant(e.started_at)}: ` : ""}{e.error_message}</li>)}</ul><FirstNotice shown={(errors.data ?? []).length} limit={ERROR_LIMIT} noun="errors" /></>
158
164
  ),
159
165
  },
160
166
  {
@@ -23,6 +23,7 @@
23
23
  import { useState } from "react";
24
24
  import { Button, Input, Label, Skeleton } from "@graph8/sdk/ui";
25
25
  import { ApiError } from "@/g8/components/api-error";
26
+ import { FirstNotice } from "@/g8/components/paging-notices";
26
27
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
27
28
  import type { ConfirmTier } from "@/g8/components/confirm-spend-dialog";
28
29
  import { EmptyState } from "@/g8/components/empty-state";
@@ -49,6 +50,9 @@ type Pending =
49
50
 
50
51
  const listSize = (l: ListSummary) => l.total ?? l.contact_count ?? l.company_count ?? undefined;
51
52
 
53
+ /** The errors API has no paging: a full read is announced under the list. */
54
+ const ERROR_LIMIT = 5;
55
+
52
56
  export function AudienceSync({ onLaunched, onSelectSync, className }: AudienceSyncProps) {
53
57
  const brand = useBrand();
54
58
  const [list, setList] = useState<ListSummary | null>(null);
@@ -63,7 +67,7 @@ export function AudienceSync({ onLaunched, onSelectSync, className }: AudienceSy
63
67
  const [notice, setNotice] = useState<string | null>(null);
64
68
 
65
69
  const syncs = useG8Query(async (signal) => listOf<AudienceSyncConfig>((await g8Get("/audience-syncs", undefined, { signal })).data), []);
66
- const errors = useG8Query(async (signal) => listOf<{ id: number; error_message?: string; started_at?: string }>((await g8Get(`/audience-syncs/${errorsFor}/errors`, { limit: 5 }, { signal })).data), [errorsFor], { enabled: errorsFor != null });
70
+ const errors = useG8Query(async (signal) => listOf<{ id: number; error_message?: string; started_at?: string }>((await g8Get(`/audience-syncs/${errorsFor}/errors`, { limit: ERROR_LIMIT }, { signal })).data), [errorsFor], { enabled: errorsFor != null });
67
71
 
68
72
  const work = async (id: number | "new", fn: () => Promise<unknown>, tolerate404 = false) => {
69
73
  setBusy(id);
@@ -123,7 +127,7 @@ export function AudienceSync({ onLaunched, onSelectSync, className }: AudienceSy
123
127
  <Button size="sm" variant="ghost" disabled={busy != null} onClick={() => setPending({ kind: "delete", sync: s })}>Delete</Button>
124
128
  </div>
125
129
  </div>
126
- {errorsFor === s.id ? <div className="text-xs">{errors.loading ? "Loading..." : (errors.data ?? []).length === 0 ? "No recent errors." : <ul>{(errors.data ?? []).map((e) => <li key={e.id}>{formatInstant(e.started_at)}: {e.error_message}</li>)}</ul>}</div> : null}
130
+ {errorsFor === s.id ? <div className="text-xs">{errors.loading ? "Loading..." : (errors.data ?? []).length === 0 ? "No recent errors." : <><ul>{(errors.data ?? []).map((e) => <li key={e.id}>{formatInstant(e.started_at)}: {e.error_message}</li>)}</ul><FirstNotice shown={(errors.data ?? []).length} limit={ERROR_LIMIT} noun="errors" /></>}</div> : null}
127
131
  {launch.syncId === s.id ? (
128
132
  <form className="grid gap-2 sm:grid-cols-2" onSubmit={(e) => { e.preventDefault(); if (readyToLaunch) setPending({ kind: "launch", sync: s }); }}>
129
133
  {([["name", "Campaign name"], ["objective", "Objective"], ["daily_budget", "Daily budget"], ["destination_url", "Landing page URL"], ["creative_image_url", "Image URL (required on Google)"], ["headline", "Headline"], ["primary_text", "Primary text"]] as const).map(([key, label]) => (