@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,155 @@
1
+ "use client";
2
+
3
+ /**
4
+ * The scope every report shares: which sites, which window, and whether bots and internal traffic count. It lives in the
5
+ * URL (`site`, `days`, `from`, `to`, `bots`, `internal`), so one site picker and one window drive every tab and a link
6
+ * opens the same view.
7
+ *
8
+ * What the API rejects is never sent (each of these is a 400 from the backend):
9
+ * - a site key that is not letters, digits, `-` or `_` (`write_key` goes into a SQL list; the router refuses anything else);
10
+ * - `date_from` without `date_to`, an inverted range, a range wider than 365 days, a day that is not a calendar date;
11
+ * - `days` outside 1..365.
12
+ * A URL that carries such a value is read as if the value were absent, and the picker shows the window really in use.
13
+ *
14
+ * SDK operations: none.
15
+ */
16
+ import { useCallback } from "react";
17
+ import { useUrlState } from "@/g8/hooks/use-url-state";
18
+
19
+ export const MAX_WINDOW_DAYS = 365;
20
+ export const DAY_PRESETS = [7, 30, 90, 365] as const;
21
+ export const DEFAULT_DAYS = 30;
22
+
23
+ /** Largest `limit` each list report accepts (the contract's maximum); a bigger value is a 422. */
24
+ export const LIMITS = {
25
+ campaigns: 200, sources: 200, referrals: 200, geo: 200, cities: 200, languages: 100,
26
+ topPages: 200, landing: 200, exit: 200, events: 200, forms: 200, formAnalysis: 100,
27
+ byLanding: 100, byCampaign: 200, realtimePages: 50, realtimeSources: 50, realtimeLocations: 200,
28
+ } as const;
29
+
30
+ const KEY_RE = /^[A-Za-z0-9_-]+$/;
31
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
32
+
33
+ /** Site keys from a comma list: only valid tokens, deduplicated and sorted (the API sorts them too). */
34
+ export function parseSites(raw: string): string[] {
35
+ const keys = raw.split(",").map((k) => k.trim()).filter((k) => KEY_RE.test(k));
36
+ return [...new Set(keys)].sort();
37
+ }
38
+
39
+ /** A real calendar day as a UTC timestamp, or undefined. */
40
+ export function dayMs(s: string): number | undefined {
41
+ if (!DATE_RE.test(s)) return undefined;
42
+ const t = Date.parse(`${s}T00:00:00Z`);
43
+ if (Number.isNaN(t) || new Date(t).toISOString().slice(0, 10) !== s) return undefined;
44
+ return t;
45
+ }
46
+
47
+ export function todayUtc(now: number = Date.now()): string {
48
+ return new Date(now).toISOString().slice(0, 10);
49
+ }
50
+
51
+ export type RangeProblem = "incomplete" | "invalid" | "inverted" | "too_wide" | null;
52
+
53
+ /** Why a from/to pair cannot be sent, or null when it can. */
54
+ export function rangeProblem(from: string, to: string): RangeProblem {
55
+ if (!from || !to) return "incomplete";
56
+ const a = dayMs(from);
57
+ const b = dayMs(to);
58
+ if (a == null || b == null) return "invalid";
59
+ if (a > b) return "inverted";
60
+ if ((b - a) / 86400000 + 1 > MAX_WINDOW_DAYS) return "too_wide";
61
+ return null;
62
+ }
63
+
64
+ export const RANGE_PROBLEM_TEXT: Record<Exclude<RangeProblem, null>, string> = {
65
+ incomplete: "Pick both a start and an end day.",
66
+ invalid: "Use real calendar days.",
67
+ inverted: "The start day must not be after the end day.",
68
+ too_wide: `A range can span at most ${MAX_WINDOW_DAYS} days.`,
69
+ };
70
+
71
+ export interface WindowSpec {
72
+ kind: "days" | "range";
73
+ days: number;
74
+ from?: string;
75
+ to?: string;
76
+ }
77
+
78
+ /** The window the URL describes: a valid from/to pair wins, otherwise `days` (clamped to a real preset-or-number), else 30. */
79
+ export function resolveWindow(daysRaw: string, from: string, to: string): WindowSpec {
80
+ if (rangeProblem(from, to) === null) {
81
+ const span = Math.round((dayMs(to)! - dayMs(from)!) / 86400000) + 1;
82
+ return { kind: "range", days: span, from, to };
83
+ }
84
+ const n = Number(daysRaw);
85
+ const days = Number.isInteger(n) && n >= 1 && n <= MAX_WINDOW_DAYS ? n : DEFAULT_DAYS;
86
+ return { kind: "days", days };
87
+ }
88
+
89
+ export function describeWindow(w: WindowSpec): string {
90
+ if (w.kind === "range") return `${w.from} to ${w.to}`;
91
+ return `Last ${w.days} days`;
92
+ }
93
+
94
+ export interface Scope {
95
+ sites: string[];
96
+ window: WindowSpec;
97
+ bots: boolean;
98
+ internal: boolean;
99
+ }
100
+
101
+ export type ReportQuery = Record<string, string | number | boolean | undefined>;
102
+
103
+ /** Query for a windowed report. Switches are sent only when on (the API's default is off). */
104
+ export function windowedQuery(scope: Scope): ReportQuery {
105
+ const q: ReportQuery = trafficQuery(scope);
106
+ if (scope.window.kind === "range") {
107
+ q.date_from = scope.window.from;
108
+ q.date_to = scope.window.to;
109
+ } else {
110
+ q.days = scope.window.days;
111
+ }
112
+ return q;
113
+ }
114
+
115
+ /** Query for a report that has no window (realtime): sites and traffic switches only. */
116
+ export function trafficQuery(scope: Scope): ReportQuery {
117
+ const q: ReportQuery = {};
118
+ if (scope.sites.length) q.write_key = scope.sites.join(",");
119
+ if (scope.bots) q.include_bots = true;
120
+ if (scope.internal) q.include_internal = true;
121
+ return q;
122
+ }
123
+
124
+ /** Stable text for a scope, used as the dependency of every report read. */
125
+ export function scopeKey(scope: Scope): string {
126
+ return JSON.stringify([scope.sites, scope.window, scope.bots, scope.internal]);
127
+ }
128
+
129
+ export interface ScopeState extends Scope {
130
+ setSites: (sites: string[]) => void;
131
+ setDays: (days: number) => void;
132
+ setRange: (from: string, to: string) => void;
133
+ setBots: (on: boolean) => void;
134
+ setInternal: (on: boolean) => void;
135
+ key: string;
136
+ }
137
+
138
+ export function useAnalyticsScope(): ScopeState {
139
+ const [siteRaw, setSite] = useUrlState("site", "");
140
+ const [daysRaw, setDaysRaw] = useUrlState("days", "");
141
+ const [from, setFrom] = useUrlState("from", "");
142
+ const [to, setTo] = useUrlState("to", "");
143
+ const [botsRaw, setBotsRaw] = useUrlState("bots", "");
144
+ const [internalRaw, setInternalRaw] = useUrlState("internal", "");
145
+ const sites = parseSites(siteRaw);
146
+ const window = resolveWindow(daysRaw, from, to);
147
+ const scope: Scope = { sites, window, bots: botsRaw === "1", internal: internalRaw === "1" };
148
+
149
+ const setSites = useCallback((next: string[]) => setSite(next.length ? parseSites(next.join(",")).join(",") : null), [setSite]);
150
+ const setDays = useCallback((d: number) => { setFrom(null); setTo(null); setDaysRaw(d === DEFAULT_DAYS ? null : String(d)); }, [setFrom, setTo, setDaysRaw]);
151
+ const setRange = useCallback((a: string, b: string) => { if (rangeProblem(a, b) === null) { setDaysRaw(null); setFrom(a); setTo(b); } }, [setDaysRaw, setFrom, setTo]);
152
+ const setBots = useCallback((on: boolean) => setBotsRaw(on ? "1" : null), [setBotsRaw]);
153
+ const setInternal = useCallback((on: boolean) => setInternalRaw(on ? "1" : null), [setInternalRaw]);
154
+ return { ...scope, setSites, setDays, setRange, setBots, setInternal, key: scopeKey(scope) };
155
+ }
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ /**
4
+ * A page path the single-page report accepts. The backend (reports/analytics/routers/web_analytics_router.py,
5
+ * `_PAGE_PATH_RE`) answers 400 for anything else, so a path that fails this is never requested.
6
+ *
7
+ * SDK operations: none.
8
+ */
9
+ export const PAGE_PATH_RE = /^\/[A-Za-z0-9/_\-.~%]*$/;
10
+ export const PAGE_PATH_MAX = 1000;
11
+
12
+ export function isReportablePath(path: string): boolean {
13
+ return path.length > 0 && path.length <= PAGE_PATH_MAX && PAGE_PATH_RE.test(path);
14
+ }
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Types and readers for the web analytics reports.
5
+ *
6
+ * TYPED FROM THE BACKEND, NOT THE CONTRACT. The contract answers every web analytics report with
7
+ * `AnalyticsResponse { success, data: object | array, meta }`: `data` is untyped. The row shapes below are the column
8
+ * aliases of the ClickHouse queries (reports/analytics/queries/{audience,acquisition,behavior,conversions,engagement,
9
+ * realtime}.py) as the service returns them (reports/analytics/services/web_analytics_service.py). Every field is optional
10
+ * and read defensively, so a column the backend adds or renames never breaks a screen.
11
+ *
12
+ * Two facts about the envelope matter to every card:
13
+ * - `meta.stale: true` means the query FAILED (or timed out) and the service swallowed the error. With no cached copy,
14
+ * `data` is empty ([] or {}), which looks exactly like "no traffic". The cards say "unavailable", never "0".
15
+ * - single-row reports (audience overview, conversion rate, engagement overview, realtime) answer an object; the rest
16
+ * answer an array of rows.
17
+ */
18
+
19
+ export interface AnalyticsMeta {
20
+ org_id?: string;
21
+ days?: number;
22
+ cached?: boolean;
23
+ stale?: boolean;
24
+ cache_ttl?: number;
25
+ timestamp?: string;
26
+ query_time_ms?: number | null;
27
+ }
28
+
29
+ export interface AnalyticsEnvelope<T> {
30
+ success?: boolean;
31
+ data?: T;
32
+ meta?: AnalyticsMeta;
33
+ }
34
+
35
+ /** A report after reading: its data, and whether the backend flagged it stale. */
36
+ export interface ReportData<T> {
37
+ data: T;
38
+ stale: boolean;
39
+ }
40
+
41
+ export type Row = Record<string, unknown>;
42
+
43
+ /** The value as a finite number, or undefined (ClickHouse decimals may arrive as numeric strings). */
44
+ export function num(v: unknown): number | undefined {
45
+ if (typeof v === "number") return Number.isFinite(v) ? v : undefined;
46
+ if (typeof v === "string" && v.trim() !== "") {
47
+ const n = Number(v);
48
+ return Number.isFinite(n) ? n : undefined;
49
+ }
50
+ return undefined;
51
+ }
52
+
53
+ export function str(v: unknown): string {
54
+ return typeof v === "string" ? v : v == null ? "" : String(v);
55
+ }
56
+
57
+ /** The rows of a list report. A non-array `data` (an object where a list is expected) is no rows. */
58
+ export function readRows<T = Row>(env: unknown): ReportData<T[]> {
59
+ const e = env as AnalyticsEnvelope<unknown> | null | undefined;
60
+ return { data: Array.isArray(e?.data) ? (e!.data as T[]) : [], stale: e?.meta?.stale === true };
61
+ }
62
+
63
+ /** The row of a single-row report. `{}` and `[]` (a swallowed failure) are no row (null). */
64
+ export function readRow<T = Row>(env: unknown): ReportData<T | null> {
65
+ const e = env as AnalyticsEnvelope<unknown> | null | undefined;
66
+ const d = e?.data;
67
+ const row = d && typeof d === "object" && !Array.isArray(d) && Object.keys(d).length > 0 ? (d as T) : null;
68
+ return { data: row, stale: e?.meta?.stale === true };
69
+ }
70
+
71
+ /** A report whose `data` is an object of named lists (technology, demographics). Missing lists are empty. */
72
+ export function readGroups<K extends string>(env: unknown, keys: readonly K[]): ReportData<Record<K, Row[]> | null> {
73
+ const { data, stale } = readRow<Record<string, unknown>>(env);
74
+ if (!data) return { data: null, stale };
75
+ const out = {} as Record<K, Row[]>;
76
+ for (const k of keys) out[k] = Array.isArray(data[k]) ? (data[k] as Row[]) : [];
77
+ return { data: out, stale };
78
+ }
79
+
80
+ // ---------------------------------------------------------------- row shapes (from the query aliases)
81
+
82
+ export interface AudienceOverview { total_users?: number; new_users?: number; page_views?: number; sessions?: number }
83
+ export interface TrendPoint { date?: string; users?: number; new_users?: number; page_views?: number; sessions?: number }
84
+ export interface NewVsReturning { user_type?: "New" | "Returning" | string; users?: number; page_views?: number }
85
+ export interface GeoCountry { country?: string; country_code?: string; users?: number; sessions?: number }
86
+ export interface GeoCity { city?: string; region?: string; country?: string; country_code?: string; users?: number; sessions?: number }
87
+ export interface Language { language?: string; users?: number; sessions?: number }
88
+ export interface Channel { channel?: string; users?: number; sessions?: number; page_views?: number; bounce_rate?: number | null }
89
+ export interface ChannelTrendPoint { date?: string; channel?: string; users?: number; sessions?: number }
90
+ export interface SourceMedium { source?: string; medium?: string; users?: number; sessions?: number; page_views?: number }
91
+ export interface CampaignRow { campaign?: string; source?: string; medium?: string; users?: number; sessions?: number; page_views?: number }
92
+ export interface Referral { referrer?: string; users?: number; sessions?: number; page_views?: number }
93
+ export interface PageRow { page_path?: string; page_title?: string; views?: number; unique_visitors?: number; avg_time_on_page?: number | null; bounce_rate?: number | null }
94
+ export interface LandingRow { landing_page?: string; landing_title?: string; sessions?: number; users?: number }
95
+ export interface ExitRow { exit_page?: string; exit_title?: string; exits?: number; users?: number }
96
+ export interface EventRow { event_name?: string; event_category?: string; event_action?: string; event_label?: string; total_events?: number; unique_users?: number; first_seen?: string; last_seen?: string }
97
+ export interface PageMetrics { views?: number; unique_visitors?: number; form_submissions?: number; bookings?: number }
98
+ export interface GoalRow { goal_name?: string; completions?: number; unique_completions?: number; total_visitors?: number; conversion_rate?: number | null }
99
+ export interface GoalTrendPoint { date?: string; form_submissions?: number; meetings_booked?: number; total_conversions?: number; unique_converters?: number }
100
+ export interface ConversionRate { total_visitors?: number; total_converters?: number; conversion_rate?: number | null }
101
+ export interface FormRow { form_id?: string; form_name?: string; submissions?: number; unique_submitters?: number; first_submission?: string; latest_submission?: string }
102
+ export interface FormTrendPoint { date?: string; submissions?: number; unique_submitters?: number; unique_forms?: number }
103
+ export interface FormAnalysisRow { form_id?: string; form_name?: string; viewers?: number; submissions?: number; unique_submitters?: number; conversion_rate?: number | null }
104
+ export interface FunnelStep { step?: string; step_order?: number; users?: number; percentage?: number | null }
105
+ export interface EngagementOverview {
106
+ total_sessions?: number; avg_session_depth?: number; avg_time_minutes?: number; bounce_rate?: number; engaged_sessions?: number; engaged_pct?: number;
107
+ depth_trend_pct?: number | null; time_trend_pct?: number | null; bounce_trend_pct?: number | null; engaged_trend_pct?: number | null;
108
+ }
109
+ export interface EngagementTrendPoint { date?: string; sessions?: number; avg_session_depth?: number; avg_time_minutes?: number; bounce_rate?: number; engaged_sessions?: number }
110
+ export interface DepthBucket { depth_bucket?: string; sessions?: number; percentage?: number }
111
+ export interface TimeBucket { duration_bucket?: string; sessions?: number; percentage?: number }
112
+ export interface EngagementByChannel { channel?: string; sessions?: number; avg_session_depth?: number; avg_time_minutes?: number; bounce_rate?: number; engaged_pct?: number }
113
+ export interface EngagementByCampaign { campaign?: string; source?: string; medium?: string; sessions?: number; users?: number; avg_session_depth?: number; avg_time_minutes?: number; bounce_rate?: number; engaged_pct?: number; engaged_sessions?: number }
114
+ export interface EngagementByLanding { landing_page?: string; sessions?: number; avg_session_depth?: number; avg_time_minutes?: number; bounce_rate?: number; engaged_pct?: number }
115
+ export interface RealtimeOverview { active_5m?: number; active_30m?: number; pageviews_5m?: number; pageviews_30m?: number; data_as_of?: string | null; data_lag_seconds?: number | null }
116
+ export interface RealtimePage { page_path?: string; page_title?: string; views?: number; visitors?: number }
117
+ export interface RealtimeLocation { country?: string; country_code?: string; region?: string; region_code?: string; visitors?: number; page_views?: number }
118
+ export interface RealtimeSource { source?: string; visitors?: number; page_views?: number }
119
+ export interface Technology { browsers?: Row[]; operating_systems?: Row[]; devices?: Row[] }
120
+
121
+ // ---------------------------------------------------------------- formatting (an unknown value is a dash, never 0)
122
+
123
+ export const DASH = "-";
124
+
125
+ export function fmtInt(v: unknown): string {
126
+ const n = num(v);
127
+ return n == null ? DASH : Math.round(n).toLocaleString();
128
+ }
129
+
130
+ export function fmtDecimal(v: unknown, digits = 1): string {
131
+ const n = num(v);
132
+ return n == null ? DASH : n.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: digits });
133
+ }
134
+
135
+ /** A value that is already a percentage (12.5 means 12.5%). */
136
+ export function fmtPct(v: unknown, digits = 1): string {
137
+ const n = num(v);
138
+ return n == null ? DASH : `${n.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: digits })}%`;
139
+ }
140
+
141
+ /** n as a percentage of d; a zero or missing denominator is a dash, never 0%. */
142
+ export function pctOf(n: unknown, d: unknown): number | undefined {
143
+ const a = num(n);
144
+ const b = num(d);
145
+ return a == null || b == null || b <= 0 ? undefined : (a / b) * 100;
146
+ }
147
+
148
+ /** Minutes as "2m 14s" (under a minute: "42s"). */
149
+ export function fmtMinutes(v: unknown): string {
150
+ const n = num(v);
151
+ if (n == null) return DASH;
152
+ const total = Math.round(n * 60);
153
+ if (total < 60) return `${total}s`;
154
+ const m = Math.floor(total / 60);
155
+ const s = total % 60;
156
+ return s === 0 ? `${m}m` : `${m}m ${s}s`;
157
+ }
158
+
159
+ export function fmtSeconds(v: unknown): string {
160
+ const n = num(v);
161
+ return n == null ? DASH : fmtMinutes(n / 60);
162
+ }
163
+
164
+ /** A date (YYYY-MM-DD or ISO) as "Oct 4", UTC so it never shifts a day. */
165
+ export function fmtDay(v: unknown): string {
166
+ const s = str(v);
167
+ if (!s) return DASH;
168
+ const d = new Date(s.length === 10 ? `${s}T00:00:00Z` : s);
169
+ return Number.isNaN(d.getTime()) ? s : d.toLocaleDateString(undefined, { month: "short", day: "numeric", timeZone: "UTC" });
170
+ }
171
+
172
+ export function fmtDateTime(v: unknown): string {
173
+ const s = str(v);
174
+ if (!s) return DASH;
175
+ const d = new Date(s);
176
+ return Number.isNaN(d.getTime()) ? s : d.toLocaleString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", timeZone: "UTC" }) + " UTC";
177
+ }
178
+
179
+ /** Sum of a numeric field over rows; undefined when no row carries it. */
180
+ export function sumOf<T>(rows: T[], pick: (r: T) => unknown): number | undefined {
181
+ let total: number | undefined;
182
+ for (const r of rows) {
183
+ const n = num(pick(r));
184
+ if (n != null) total = (total ?? 0) + n;
185
+ }
186
+ return total;
187
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @title Rank landing pages by sessions and engagement
3
+ * @when Deciding which landing pages deserve more traffic or a rewrite. `limit` is capped per report (landing pages 200,
4
+ * engagement by landing page 100); a bigger value is a 422. The landing page path is the address the visitor first saw.
5
+ * Rows are read from the backend query aliases (reports/analytics/queries/behavior.py and engagement.py). Read tier, free.
6
+ */
7
+ import { g8 } from "@graph8/sdk";
8
+
9
+ g8.init({ apiKey: process.env.G8_API_KEY! });
10
+
11
+ interface Landing { landing_page?: string; sessions?: number; users?: number }
12
+ interface Engaged { landing_page?: string; bounce_rate?: number; engaged_pct?: number }
13
+ interface Envelope<T> { data?: T }
14
+
15
+ export async function bestLandingPages(days = 30, top = 10) {
16
+ const query = { days, limit: 100 };
17
+ const [landing, engagement] = await Promise.all([
18
+ g8.api.webAnalytics.getLandingPages({ query }),
19
+ g8.api.webAnalytics.getEngagementByLandingPage({ query }),
20
+ ]);
21
+ const byPage = new Map((((engagement as Envelope<Engaged[]>).data) ?? []).map((e) => [e.landing_page, e]));
22
+ return (((landing as Envelope<Landing[]>).data) ?? [])
23
+ .sort((a, b) => (b.sessions ?? 0) - (a.sessions ?? 0))
24
+ .slice(0, top)
25
+ .map((l) => ({ page: l.landing_page, sessions: l.sessions, bounceRate: byPage.get(l.landing_page)?.bounce_rate ?? null, engagedPct: byPage.get(l.landing_page)?.engaged_pct ?? null }));
26
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @title Find forms that get views but few submissions
3
+ * @when A conversion review. `conversion_rate` is null when a form has no viewers (for example appointment-only forms):
4
+ * skip those, do not read them as 0% or 100%. `limit` for form analysis is capped at 100. Read tier, free.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ interface FormAnalysis { form_id?: string; form_name?: string; viewers?: number; unique_submitters?: number; conversion_rate?: number | null }
11
+ interface Envelope<T> { data?: T }
12
+
13
+ export async function weakForms(days = 30, minViewers = 50) {
14
+ const res = await g8.api.webAnalytics.getFormAnalysis({ query: { days, limit: 100 } });
15
+ return (((res as Envelope<FormAnalysis[]>).data) ?? [])
16
+ .filter((f) => (f.viewers ?? 0) >= minViewers && f.conversion_rate != null)
17
+ .sort((a, b) => (a.conversion_rate as number) - (b.conversion_rate as number))
18
+ .map((f) => ({ id: f.form_id, name: f.form_name, viewers: f.viewers, rate: f.conversion_rate }));
19
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @title Summarise a site's traffic, sources and conversion for a window
3
+ * @when A weekly email, a Slack digest or a dashboard header. The contract types these reports as open objects
4
+ * (`AnalyticsResponse.data` is `object | array`), so the row shapes below come from the backend queries
5
+ * (reports/analytics/queries/*.py). `meta.stale: true` means the query failed and the answer is empty: treat it as
6
+ * "unavailable", never as zero traffic. Pass `writeKey` (a site's public key, letters, digits, - and _) to scope to one site.
7
+ * All read tier, free.
8
+ */
9
+ import { g8 } from "@graph8/sdk";
10
+
11
+ g8.init({ apiKey: process.env.G8_API_KEY! });
12
+
13
+ interface Meta { stale?: boolean }
14
+ interface Envelope<T> { data?: T; meta?: Meta }
15
+
16
+ export async function weeklyTraffic(days = 7, writeKey?: string) {
17
+ const query = { days, write_key: writeKey };
18
+ const [overview, channels, rate] = await Promise.all([
19
+ g8.api.webAnalytics.getAudienceOverview({ query }),
20
+ g8.api.webAnalytics.getAcquisitionChannels({ query }),
21
+ g8.api.webAnalytics.getConversionRate({ query }),
22
+ ]);
23
+ const unavailable = [overview, channels, rate].some((r) => (r as Envelope<unknown>).meta?.stale === true);
24
+ if (unavailable) return { unavailable: true as const };
25
+ const o = (overview as Envelope<{ total_users?: number; new_users?: number; sessions?: number; page_views?: number }>).data ?? {};
26
+ const c = (channels as Envelope<Array<{ channel?: string; sessions?: number }>>).data ?? [];
27
+ const r = (rate as Envelope<{ conversion_rate?: number | null }>).data ?? {};
28
+ return {
29
+ unavailable: false as const,
30
+ visitors: o.total_users,
31
+ sessions: o.sessions,
32
+ pageViews: o.page_views,
33
+ // null means there were no visitors: report "n/a", not 0%.
34
+ conversionRate: r.conversion_rate ?? null,
35
+ topChannels: [...c].sort((a, b) => (b.sessions ?? 0) - (a.sessions ?? 0)).slice(0, 3).map((x) => x.channel),
36
+ };
37
+ }
@@ -121,7 +121,7 @@ export function ChatWidgetConfig({ initialBotId, surface = "external" }: ChatWid
121
121
  <div><Label htmlFor="g8-wc-text" className="mb-1">Reply text</Label><Input id="g8-wc-text" placeholder="#111111" value={draft.assistantMessageTextColor ?? ""} onChange={(e) => set({ assistantMessageTextColor: e.target.value })} /></div>
122
122
  <label className="flex items-center gap-2 self-end text-sm"><input type="checkbox" checked={Boolean(draft.darkMode)} onChange={(e) => set({ darkMode: e.target.checked })} />Dark mode</label>
123
123
  </div>
124
- {problem ? <p role="alert" className="text-sm text-red-700">{problem}</p> : null}
124
+ {problem ? <p role="alert" className="text-sm text-error-foreground">{problem}</p> : null}
125
125
  {save.error ? <ApiError error={save.error} title="Could not save the widget settings" /> : null}
126
126
  <div><Button loading={save.loading} disabled={Boolean(problem) || settings.loading || Boolean(settings.error)} onClick={() => void save.mutate(undefined)}>{exists ? "Save changes" : "Create widget settings"}</Button></div>
127
127
  </CardContent>
@@ -47,7 +47,7 @@ export function ConversationList({ linkParam, botId, pageSize = 25, onSelectVisi
47
47
  <div className="grid gap-3">
48
48
  {counts.length > 0 ? (
49
49
  <dl className="grid grid-cols-2 gap-2 sm:grid-cols-4">
50
- {counts.map(([k, v]) => <div key={k} className="rounded-md border p-3"><dt className="text-muted-foreground text-xs">{k.replace(/_/g, " ")}</dt><dd className="text-lg font-semibold">{v.toLocaleString()}</dd></div>)}
50
+ {counts.map(([k, v]) => <div key={k} className="rounded-md border p-3"><dt className="text-muted-foreground text-xs">{k.replace(/_/g, " ")}</dt><dd className="text-lg font-medium">{v.toLocaleString()}</dd></div>)}
51
51
  </dl>
52
52
  ) : null}
53
53
  <form className="flex flex-wrap items-center gap-2" onSubmit={(e) => { e.preventDefault(); setPage(1); setSearch(text.trim()); }}>
@@ -16,7 +16,27 @@ waterfall and formula columns, quote then run, progress, unlock locked rows, sen
16
16
  **Import people** (free preview and match count, then a confirmed import; rows land locked) and **Scrape the web** (credit estimate and balance, confirmed run, polled progress) are in the table header (`SearchImportPanel`, `ScraperPanel`). Open any row (`?row=`) for `WorkbenchRowDetail`: every cell, per-cell edit, unlock, send to a list, delete. `?view=full` opens a row full-page.
17
17
  Slots: `toolbarActions`, `renderRowActions(table)`, `renderDetailActions(table)`.
18
18
 
19
- Prerequisites: `enrichment` and `lists` installed, proxy allowlist `workbench`, `enrichment`, `lists`; provider keys and credits as below.
19
+ An opened table has four tabs (`?tab=`, Data is the default):
20
+ - **Data**: the grid. Filter by one column (a "contains" text filter) and sort by one column, delete a column (confirmed), see the
21
+ live **Locked rows** count in the header, and from the selection panel **Send to sequence** (below). **Edit details** renames the
22
+ workbench or changes its description (`WorkbenchTableSettings`).
23
+ - **Automation** (`WorkbenchAutomation`): **Auto-enrich** (the passes it would run, then a confirm with the exact pass count),
24
+ **Schedule** (hourly, daily, weekly or a cron whose minute is a plain number, in UTC, saved stopped until started; an AI schedule is priced from the API's run-all quote, which the server requires back as the cap; a CRM schedule collects the CRM and a limit of 1 to 1000; editing keeps the schedule's action_config), **Triggers** (when a row arrives,
25
+ an AI column finishes or a status changes: run the AI columns, recompute formulas, send the row to a list, call a webhook, with an
26
+ optional condition) and **Pipelines** (an ordered run of the recipe columns, created switched off).
27
+ - **Import**: `WorkbenchDatafileImport` (the id of an uploaded file, optionally an AI-suggested header-to-column mapping you edit before
28
+ importing) and `WorkbenchCrmImport` (records from a connected CRM, 1 to 10,000).
29
+ - **Connections** (`WorkbenchConnections`): copy new rows into another workbench by name, optionally some columns only; sync an outgoing
30
+ connection on demand (it stops at the first locked row); delete.
31
+
32
+ **Send to sequence** (`WorkbenchSendToSequence`): pick a running sequence by name and the list the people are saved to; the confirm counts
33
+ exactly who will be emailed. Locked rows are never sent, and an empty selection cannot be sent (an omitted row list means every row).
34
+
35
+ Spend is never guessed: a started AI schedule shows the API's own per-run price (its quote); arming an AI trigger, enabling a pipeline with AI or provider steps, switching on
36
+ auto-enrich, importing into an auto-enriching workbench and the mapping suggestion (one AI call) are billable-tier operations whose total graph8 does not publish for rows that have not arrived yet, so each confirm
37
+ says so and shows no number. Everything that leaves the workspace (enrolling people, reading the CRM) is an external-tier confirm.
38
+
39
+ Prerequisites: `enrichment` and `lists` installed, proxy allowlist `workbench`, `enrichment`, `lists`, `sequences` and `crm-syncs` (the sequence picker and the CRM list); provider keys and credits as below.
20
40
 
21
41
  ## What you get
22
42
 
@@ -115,25 +135,49 @@ Ask the person: who approves a run's price (the proxy's `authorize` decides who
115
135
  | addWorkbenchColumn | POST /workbench/tables/{table_id}/columns | write | ColumnRecipeEditor |
116
136
  | addWorkbenchRow | POST /workbench/tables/{table_id}/rows | write | WorkbenchAddRows |
117
137
  | bulkDeleteWorkbenchRows | POST /workbench/tables/{table_id}/rows/bulk-delete | destructive | WorkbenchRowActions |
138
+ | createList | POST /lists | write | WorkbenchTriggers, WorkbenchSendToSequence |
139
+ | createWorkbenchConnection | POST /workbench/tables/{table_id}/connections | write | WorkbenchConnections |
118
140
  | createWorkbenchFormulaColumns | POST /workbench/tables/{table_id}/formula-columns | write | ColumnRecipeEditor |
141
+ | createWorkbenchPipeline | POST /workbench/tables/{table_id}/pipelines | billable | WorkbenchPipelines |
119
142
  | createWorkbenchTable | POST /workbench/tables | write | WorkbenchTableList |
143
+ | createWorkbenchTrigger | POST /workbench/tables/{table_id}/triggers | billable | WorkbenchTriggers |
144
+ | deleteWorkbenchColumn | DELETE /workbench/tables/{table_id}/columns/{column_id} | destructive | WorkbenchGrid |
145
+ | deleteWorkbenchConnection | DELETE /workbench/tables/{table_id}/connections/{connection_id} | destructive | WorkbenchConnections |
146
+ | deleteWorkbenchPipeline | DELETE /workbench/tables/{table_id}/pipelines/{pipeline_id} | destructive | WorkbenchPipelines |
147
+ | deleteWorkbenchSchedule | DELETE /workbench/tables/{table_id}/schedule | destructive | WorkbenchSchedule |
120
148
  | deleteWorkbenchTable | DELETE /workbench/tables/{table_id} | destructive | WorkbenchTableList |
149
+ | deleteWorkbenchTrigger | DELETE /workbench/tables/{table_id}/triggers/{trigger_id} | destructive | WorkbenchTriggers |
121
150
  | estimateWorkbenchScraperCredits | GET /workbench/scraper/credit-estimate | read | recipes |
122
151
  | exportWorkbenchTable | POST /workbench/tables/{table_id}/export | write | WorkbenchRowActions |
123
152
  | getWorkbenchAnalytics | GET /workbench/tables/{table_id}/analytics | read | RunProgress |
124
153
  | getWorkbenchHistory | GET /workbench/tables/{table_id}/history | read | RunProgress |
154
+ | getWorkbenchLockedRowCount | GET /workbench/tables/{table_id}/locked-row-count | read | WorkbenchScreen |
155
+ | getWorkbenchMappingSuggestion | GET /workbench/tables/{table_id}/suggest-mappings/{job_id} | read | WorkbenchDatafileImport |
156
+ | getWorkbenchPipeline | GET /workbench/tables/{table_id}/pipelines/{pipeline_id} | read | WorkbenchPipelines |
157
+ | getWorkbenchRow | GET /workbench/tables/{table_id}/rows/{row_id} | read | WorkbenchRowDetail |
158
+ | getWorkbenchSchedule | GET /workbench/tables/{table_id}/schedule | read | WorkbenchSchedule |
125
159
  | getWorkbenchScraperRun | GET /workbench/tables/{table_id}/scraper/runs/{run_id} | read | recipes |
126
160
  | getWorkbenchTable | GET /workbench/tables/{table_id} | read | WorkbenchGrid, useWorkbenchTable |
161
+ | importWorkbenchDatafile | POST /workbench/tables/{table_id}/import/datafile | write | WorkbenchDatafileImport |
162
+ | importWorkbenchFromCrm | POST /workbench/tables/{table_id}/import/crm | external | WorkbenchCrmImport |
127
163
  | importWorkbenchFromSearch | POST /workbench/tables/{table_id}/import/search | billable | recipes |
164
+ | listCrmSyncs | GET /crm-syncs | read | WorkbenchCrmImport, WorkbenchSchedule |
128
165
  | listEnrichmentProviders | GET /enrichment/providers | read | ColumnRecipeEditor |
166
+ | listLists | GET /lists | read | WorkbenchTriggers, WorkbenchSendToSequence |
167
+ | listSequences | GET /sequences | read | WorkbenchSendToSequence |
129
168
  | listWorkbenchColumns | GET /workbench/tables/{table_id}/columns | read | WorkbenchGrid, useWorkbenchTable |
169
+ | listWorkbenchConnections | GET /workbench/tables/{table_id}/connections | read | WorkbenchConnections |
170
+ | listWorkbenchEnrichmentConfigs | GET /workbench/tables/{table_id}/enrichment-configs | read | WorkbenchAutoEnrich |
171
+ | listWorkbenchPipelines | GET /workbench/tables/{table_id}/pipelines | read | WorkbenchPipelines |
130
172
  | listWorkbenchRows | GET /workbench/tables/{table_id}/rows | read | WorkbenchGrid |
131
173
  | listWorkbenchTables | GET /workbench/tables | read | WorkbenchTableList |
132
174
  | listWorkbenchTargetFields | GET /workbench/target-fields | read | ColumnRecipeEditor |
175
+ | listWorkbenchTriggers | GET /workbench/tables/{table_id}/triggers | read | WorkbenchTriggers |
133
176
  | mapWorkbenchColumn | PUT /workbench/tables/{table_id}/columns/{column_id}/mapping | write | ColumnRecipeEditor |
134
177
  | pasteImportWorkbenchRows | POST /workbench/tables/{table_id}/import/paste | write | WorkbenchAddRows |
135
178
  | previewWorkbenchSearchImport | POST /workbench/tables/{table_id}/import/search/preview | read | recipes |
136
- | quoteWorkbenchAiRun | POST /workbench/tables/{table_id}/ai/validate-credits | read | RunProgress |
179
+ | queryWorkbenchRows | POST /workbench/tables/{table_id}/rows/query | read | WorkbenchGrid |
180
+ | quoteWorkbenchAiRun | POST /workbench/tables/{table_id}/ai/validate-credits | read | RunProgress, WorkbenchSchedule |
137
181
  | quoteWorkbenchUnlock | POST /workbench/tables/{table_id}/unlock-quote | read | WorkbenchRowActions |
138
182
  | quoteWorkbenchWaterfallRun | POST /workbench/tables/{table_id}/waterfall/configs/{column_id}/validate-credits | read | RunProgress |
139
183
  | runWorkbenchColumnAi | POST /workbench/tables/{table_id}/columns/{col_id}/ai/run | billable | RunProgress |
@@ -142,5 +186,13 @@ Ask the person: who approves a run's price (the proxy's `authorize` decides who
142
186
  | runWorkbenchWaterfall | POST /workbench/tables/{table_id}/waterfall/configs/{column_id}/run | billable | RunProgress |
143
187
  | saveWorkbenchColumnAiConfig | PUT /workbench/tables/{table_id}/columns/{col_id}/ai-config | write | ColumnRecipeEditor |
144
188
  | saveWorkbenchWaterfall | POST /workbench/tables/{table_id}/waterfall/configs | write | ColumnRecipeEditor |
189
+ | sendWorkbenchRowsToSequence | POST /workbench/tables/{table_id}/send-to-sequence | external | WorkbenchSendToSequence |
190
+ | setWorkbenchAutoEnrich | PUT /workbench/tables/{table_id}/auto-enrich | billable | WorkbenchAutoEnrich |
191
+ | setWorkbenchSchedule | PUT /workbench/tables/{table_id}/schedule | billable | WorkbenchSchedule |
192
+ | suggestWorkbenchMappings | POST /workbench/tables/{table_id}/suggest-mappings | billable | WorkbenchDatafileImport |
193
+ | syncWorkbenchConnection | POST /workbench/tables/{table_id}/connections/{connection_id}/sync | write | WorkbenchConnections |
145
194
  | unlockWorkbenchRows | POST /workbench/tables/{table_id}/unlock-rows | billable | WorkbenchRowActions |
195
+ | updateWorkbenchPipeline | PUT /workbench/tables/{table_id}/pipelines/{pipeline_id} | billable | WorkbenchPipelines |
146
196
  | updateWorkbenchRow | PATCH /workbench/tables/{table_id}/rows/{row_id} | write | WorkbenchGrid |
197
+ | updateWorkbenchTable | PUT /workbench/tables/{table_id} | write | WorkbenchTableSettings |
198
+ | updateWorkbenchTrigger | PUT /workbench/tables/{table_id}/triggers/{trigger_id} | billable | WorkbenchTriggers |