@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
@@ -14,27 +14,45 @@
14
14
  "foundation"
15
15
  ],
16
16
  "operations": [
17
+ "apply_landing_page_review_findings_landing_pages__landing_page_id__ai_review_apply_post",
18
+ "clear_landing_page_consent_override_landing_pages__landing_page_id__tracking_consent_delete",
19
+ "clone_html_to_landing_page_landing_pages_clone_html_post",
20
+ "clone_url_to_landing_page_landing_pages_clone_url_post",
17
21
  "create_content_page_content_pages_post",
18
22
  "create_landing_page_landing_pages_post",
19
23
  "create_landing_page_preview_link_landing_pages__landing_page_id__preview_get",
20
24
  "delete_content_page_content_pages__page_id__delete",
21
25
  "delete_landing_page_landing_pages__landing_page_id__delete",
26
+ "edit_landing_page_with_chat_landing_pages__landing_page_id__edit_post",
22
27
  "enrich_content_page_content_pages__page_id__enrich_seo_post",
28
+ "export_landing_page_landing_pages__landing_page_id__export_get",
29
+ "fix_landing_page_finding_with_ai_landing_pages__landing_page_id__ai_review_fix_with_ai_post",
30
+ "generate_landing_page_seo_metadata_landing_pages__landing_page_id__generate_seo_metadata_post",
23
31
  "get_brand_kit_overview_brand_kit_get",
24
32
  "get_content_page_content_pages__page_id__get",
25
33
  "get_landing_page_landing_pages__landing_page_id__get",
34
+ "get_landing_page_reporting_overview_landing_pages_reporting_overview_get",
35
+ "get_landing_page_status_landing_pages__landing_page_id__status_get",
36
+ "get_landing_page_tracking_landing_pages__landing_page_id__tracking_get",
26
37
  "get_layout_selections_brand_kit_layout_selections_get",
27
38
  "get_seo_audit_issues_seo_audits__audit_id__issues_get",
28
39
  "get_seo_audit_seo_audits__audit_id__get",
29
40
  "list_archetypes_brand_kit_archetypes_get",
30
41
  "list_content_pages_content_pages_get",
42
+ "list_landing_page_bindings_landing_pages__landing_page_id__bindings_get",
43
+ "list_landing_page_domains_landing_pages_domains_get",
44
+ "list_landing_page_reporting_companies_landing_pages_reporting_companies_get",
45
+ "list_landing_page_reporting_people_landing_pages_reporting_people_get",
31
46
  "list_landing_page_versions_landing_pages__landing_page_id__versions_get",
32
47
  "list_landing_pages_landing_pages_get",
33
48
  "list_seo_audits_seo_audits_get",
34
49
  "list_seo_tracked_keywords_seo_rank_tracking_get",
50
+ "provision_landing_page_tracking_landing_pages__landing_page_id__tracking_provision_post",
35
51
  "publish_landing_page_landing_pages__landing_page_id__publish_post",
52
+ "remove_landing_page_binding_landing_pages__landing_page_id__bindings_delete",
36
53
  "remove_seo_tracked_keywords_seo_rank_tracking_keywords_remove_post",
37
54
  "restore_landing_page_version_landing_pages__landing_page_id__versions__version_num__restore_post",
55
+ "review_landing_page_landing_pages__landing_page_id__ai_review_post",
38
56
  "save_layout_selections_brand_kit_layout_selections_put",
39
57
  "save_seo_tracked_keywords_seo_rank_tracking_keywords_post",
40
58
  "start_seo_audit_seo_audits_post",
@@ -42,7 +60,8 @@
42
60
  "unpublish_landing_page_landing_pages__landing_page_id__unpublish_post",
43
61
  "update_content_page_content_pages__page_id__patch",
44
62
  "update_landing_page_landing_pages__landing_page_id__patch",
45
- "update_landing_page_section_landing_pages__landing_page_id__sections__section_idx__patch"
63
+ "update_landing_page_section_landing_pages__landing_page_id__sections__section_idx__patch",
64
+ "update_landing_page_tracking_landing_pages__landing_page_id__tracking_patch"
46
65
  ],
47
66
  "components": [
48
67
  {
@@ -148,9 +167,111 @@
148
167
  "write",
149
168
  "billable"
150
169
  ]
170
+ },
171
+ {
172
+ "name": "MoreRows",
173
+ "file": "components/more-rows.tsx",
174
+ "purpose": "Footer for paged lists: how many rows are shown and a Load more button.",
175
+ "operations": [],
176
+ "tiers": []
177
+ },
178
+ {
179
+ "name": "PageAiTools",
180
+ "file": "components/page-ai-tools.tsx",
181
+ "purpose": "AI help for a page: edit by instruction, review and apply fixes, write search metadata (each AI pass confirmed).",
182
+ "operations": [
183
+ "edit_landing_page_with_chat_landing_pages__landing_page_id__edit_post",
184
+ "apply_landing_page_review_findings_landing_pages__landing_page_id__ai_review_apply_post",
185
+ "review_landing_page_landing_pages__landing_page_id__ai_review_post",
186
+ "fix_landing_page_finding_with_ai_landing_pages__landing_page_id__ai_review_fix_with_ai_post",
187
+ "update_landing_page_landing_pages__landing_page_id__patch",
188
+ "generate_landing_page_seo_metadata_landing_pages__landing_page_id__generate_seo_metadata_post"
189
+ ],
190
+ "tiers": [
191
+ "write",
192
+ "billable"
193
+ ]
194
+ },
195
+ {
196
+ "name": "PageAnalytics",
197
+ "file": "components/page-analytics.tsx",
198
+ "purpose": "Landing page performance: visits, identified people and companies, form submissions and sources, for all pages or one.",
199
+ "operations": [
200
+ "list_landing_page_reporting_people_landing_pages_reporting_people_get",
201
+ "list_landing_page_reporting_companies_landing_pages_reporting_companies_get",
202
+ "get_landing_page_reporting_overview_landing_pages_reporting_overview_get"
203
+ ],
204
+ "tiers": [
205
+ "read"
206
+ ]
207
+ },
208
+ {
209
+ "name": "PageClone",
210
+ "file": "components/page-clone.tsx",
211
+ "purpose": "Rebuild a URL or pasted HTML as an editable landing page (confirmed AI spend).",
212
+ "operations": [
213
+ "clone_url_to_landing_page_landing_pages_clone_url_post",
214
+ "clone_html_to_landing_page_landing_pages_clone_html_post"
215
+ ],
216
+ "tiers": [
217
+ "billable"
218
+ ]
219
+ },
220
+ {
221
+ "name": "PageDomains",
222
+ "file": "components/page-domains.tsx",
223
+ "purpose": "Where one page is live: its URLs, publish to a connected domain, take a URL down, download the HTML.",
224
+ "operations": [
225
+ "list_landing_page_bindings_landing_pages__landing_page_id__bindings_get",
226
+ "list_landing_page_domains_landing_pages_domains_get",
227
+ "export_landing_page_landing_pages__landing_page_id__export_get",
228
+ "publish_landing_page_landing_pages__landing_page_id__publish_post",
229
+ "remove_landing_page_binding_landing_pages__landing_page_id__bindings_delete"
230
+ ],
231
+ "tiers": [
232
+ "read",
233
+ "external",
234
+ "destructive"
235
+ ]
236
+ },
237
+ {
238
+ "name": "PageGenerationStatus",
239
+ "file": "components/page-generation-status.tsx",
240
+ "purpose": "Progress of an AI-built page while it is generating.",
241
+ "operations": [
242
+ "get_landing_page_status_landing_pages__landing_page_id__status_get"
243
+ ],
244
+ "tiers": [
245
+ "read"
246
+ ]
247
+ },
248
+ {
249
+ "name": "PageTracking",
250
+ "file": "components/page-tracking.tsx",
251
+ "purpose": "How one page is measured: tracking mode, form label, chat widget and conversion goal; own stream and consent reset.",
252
+ "operations": [
253
+ "update_landing_page_tracking_landing_pages__landing_page_id__tracking_patch",
254
+ "provision_landing_page_tracking_landing_pages__landing_page_id__tracking_provision_post",
255
+ "clear_landing_page_consent_override_landing_pages__landing_page_id__tracking_consent_delete",
256
+ "get_landing_page_tracking_landing_pages__landing_page_id__tracking_get"
257
+ ],
258
+ "tiers": [
259
+ "read",
260
+ "write",
261
+ "destructive"
262
+ ]
263
+ }
264
+ ],
265
+ "hooks": [
266
+ {
267
+ "name": "useLandingPagePaths",
268
+ "file": "hooks/use-landing-page-paths.ts",
269
+ "purpose": "Resolve a website path to the one published landing page at that address (by id, never by name), for glue such as web analytics' Open landing page.",
270
+ "operations": [
271
+ "list_landing_pages_landing_pages_get"
272
+ ]
151
273
  }
152
274
  ],
153
- "hooks": [],
154
275
  "lib": [
155
276
  {
156
277
  "file": "lib/studio.ts",
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ /**
4
+ * MoreRows: the honest end of a list that the API pages. Shows how many rows are on screen and, while the API has
5
+ * another page, a "Load more" button. A list never ends silently: either every row is reachable or this says there are more.
6
+ * (Same contract as the foundation's LoadMore; kept local so the pages block does not wait on it.)
7
+ *
8
+ * SDK operations: none.
9
+ */
10
+ import { Button } from "@graph8/sdk/ui";
11
+
12
+ export interface MoreRowsProps {
13
+ /** Rows on screen. */
14
+ shown: number;
15
+ /** What the rows are, plural: "people". */
16
+ noun: string;
17
+ hasMore: boolean;
18
+ loading?: boolean;
19
+ onMore: () => void;
20
+ }
21
+
22
+ export function MoreRows({ shown, noun, hasMore, loading, onMore }: MoreRowsProps) {
23
+ if (!hasMore) return null;
24
+ return (
25
+ <div className="text-muted-foreground flex items-center justify-between gap-3 py-2 text-sm">
26
+ <span role="status">Showing {shown.toLocaleString()} {noun}</span>
27
+ <Button type="button" variant="outline" size="sm" loading={loading} onClick={onMore}>Load more</Button>
28
+ </div>
29
+ );
30
+ }
@@ -0,0 +1,196 @@
1
+ "use client";
2
+
3
+ /**
4
+ * PageAiTools: AI help for one landing page. Describe a change in plain words and the page is edited; run a review that
5
+ * lists what could be better and apply the exact fixes for free; write search title, description and keywords.
6
+ *
7
+ * SDK operations, in the order the screen uses them:
8
+ * 1. editLandingPageWithChat POST /landing-pages/{id}/edit { instruction, section_idx? } (BILLABLE, confirmed; changes the draft only)
9
+ * 2. reviewLandingPage POST /landing-pages/{id}/ai-review { deep } (BILLABLE, confirmed; changes nothing)
10
+ * 3. applyLandingPageReviewFindings POST /landing-pages/{id}/ai-review/apply { finding_ids } (write, free, no model runs)
11
+ * 4. fixLandingPageFindingWithAi POST /landing-pages/{id}/ai-review/fix-with-ai { finding } (BILLABLE per finding, confirmed)
12
+ * 5. generateLandingPageSeoMetadata POST /landing-pages/{id}/generate-seo-metadata (BILLABLE, confirmed)
13
+ * 6. updateLandingPage PATCH /landing-pages/{id} { description } (write; "Use as description")
14
+ *
15
+ * The API quotes no price in advance and the balance is not checked here, so every dialog says what runs and how many
16
+ * AI passes it is (a "quick check" runs no model: 0 credits). Edits change the draft; the live page changes only when
17
+ * it is published again. Nothing here publishes.
18
+ */
19
+ import { useState } from "react";
20
+ import { Button, Label, Textarea } from "@graph8/sdk/ui";
21
+ import { ApiError } from "@/g8/components/api-error";
22
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
23
+ import { useConfirmAction } from "@/g8/components/confirmed-action";
24
+ import { EmptyState } from "@/g8/components/empty-state";
25
+ import { KeyValueList } from "@/g8/components/screen/key-value-list";
26
+ import { SectionCard } from "@/g8/components/screen/section-card";
27
+ import { StatusPill } from "@/g8/components/screen/status-pill";
28
+ import type { Tone } from "@/g8/components/screen/status-pill";
29
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
30
+ import { g8Patch, g8Post } from "@/g8/lib/g8-client";
31
+ import { asRec, num, rowsOf, text } from "../lib/studio";
32
+ import type { Rec } from "../lib/studio";
33
+
34
+ export interface PageAiToolsProps {
35
+ pageId: string;
36
+ /** Called after the page draft changed (an edit, applied fixes, a new description). */
37
+ onChanged?: () => void;
38
+ className?: string;
39
+ }
40
+
41
+ const SEVERITY: Record<string, Tone> = { error: "negative", critical: "negative", high: "negative", warning: "warning", medium: "warning", info: "neutral", low: "neutral" };
42
+
43
+ /** Findings the review returned. The id is required: a finding without one cannot be applied. */
44
+ export function findingsOf(data: unknown): Rec[] {
45
+ return rowsOf<Rec>(data, "findings").filter((f) => text(f.id) !== "");
46
+ }
47
+
48
+ function ChatEdit({ pageId, onChanged }: PageAiToolsProps) {
49
+ const { ask, dialog, failure } = useConfirmAction();
50
+ const [instruction, setInstruction] = useState("");
51
+ const [section, setSection] = useState("");
52
+ const [done, setDone] = useState("");
53
+ const sectionIdx = section.trim() === "" ? undefined : Number(section) - 1;
54
+ const sectionOk = sectionIdx === undefined || (Number.isInteger(sectionIdx) && sectionIdx >= 0);
55
+ const go = (): void => {
56
+ const body = { instruction: instruction.trim(), ...(sectionIdx === undefined ? {} : { section_idx: sectionIdx }) };
57
+ ask({
58
+ tier: "billable",
59
+ title: sectionIdx === undefined ? "Edit the whole page with AI" : `Edit section ${sectionIdx + 1} with AI`,
60
+ confirmLabel: "Edit with AI",
61
+ description: `One AI pass applies your instruction to the draft. Uses credits; the API gives no price in advance and your balance is not checked here. The live page does not change until you publish.`,
62
+ run: async () => { await g8Post(`/landing-pages/${encodeURIComponent(pageId)}/edit`, body); setDone("Edit applied to the draft."); setInstruction(""); },
63
+ onDone: () => onChanged?.(),
64
+ });
65
+ };
66
+ return (
67
+ <SectionCard title="Edit with AI" description="Say what to change, for example: make the headline shorter and add a second call to action.">
68
+ <form className="grid gap-3" onSubmit={(e) => { e.preventDefault(); if (instruction.trim() && sectionOk) go(); }}>
69
+ <div>
70
+ <Label htmlFor="ai-instruction" className="mb-1">What should change</Label>
71
+ <Textarea id="ai-instruction" rows={3} value={instruction} onChange={(e) => { setInstruction(e.target.value); setDone(""); }} />
72
+ </div>
73
+ <div className="max-w-40">
74
+ <Label htmlFor="ai-section" className="mb-1">Section number (optional)</Label>
75
+ <input id="ai-section" inputMode="numeric" className="border-input bg-background h-9 w-full rounded-md border px-2 text-sm" value={section} onChange={(e) => setSection(e.target.value.replace(/\D/g, ""))} />
76
+ </div>
77
+ <div><Button type="submit" disabled={!instruction.trim() || !sectionOk}>Edit with AI</Button></div>
78
+ </form>
79
+ {done ? <p role="status" className="mt-2 text-sm">{done}</p> : null}
80
+ {failure ? <ApiError error={failure} title="The edit did not work" /> : null}
81
+ <ConfirmSpendDialog {...dialog} />
82
+ </SectionCard>
83
+ );
84
+ }
85
+
86
+ function Review({ pageId, onChanged }: PageAiToolsProps) {
87
+ const { ask, dialog, failure } = useConfirmAction();
88
+ const base = `/landing-pages/${encodeURIComponent(pageId)}`;
89
+ const [findings, setFindings] = useState<Rec[] | null>(null);
90
+ const [picked, setPicked] = useState<string[]>([]);
91
+ const [note, setNote] = useState("");
92
+ const apply = useG8Mutation(() => g8Post(`${base}/ai-review/apply`, { finding_ids: picked }), {
93
+ onSuccess: (res) => {
94
+ // The API answers with the findings that remain (id, title, severity only): keep our full copies of those.
95
+ const d = asRec(res.data);
96
+ const applied = num(d.applied) ?? 0;
97
+ // remaining_findings comes from a rule-only re-review, so it never lists AI findings: drop only what could be applied (had an exact fix).
98
+ const done = new Set((findings ?? []).filter((f) => picked.includes(text(f.id)) && f.suggested_fix).map((f) => text(f.id)));
99
+ setFindings((cur) => (cur ?? []).filter((f) => !done.has(text(f.id))));
100
+ setNote(applied === picked.length ? `Applied ${applied} fix${applied === 1 ? "" : "es"}.` : `Applied ${applied} of ${picked.length} selected. The rest have no exact fix: use Fix with AI.`);
101
+ setPicked([]);
102
+ if (applied > 0) onChanged?.();
103
+ },
104
+ });
105
+ const review = (deep: boolean): void => ask({
106
+ tier: "billable",
107
+ title: deep ? "Review this page with AI" : "Quick check of this page",
108
+ confirmLabel: deep ? "Review with AI" : "Run quick check",
109
+ estimatedCredits: deep ? undefined : 0,
110
+ description: deep ? "One AI pass reads the page and lists what to improve. Uses credits; the API gives no price in advance. Nothing on the page changes." : "Runs the built-in checks only, no AI model: 0 credits. Nothing on the page changes.",
111
+ run: async () => { const res = await g8Post(`${base}/ai-review`, { deep }); setFindings(findingsOf(res.data)); setPicked([]); setNote(""); },
112
+ });
113
+ const fix = (f: Rec): void => ask({
114
+ tier: "billable",
115
+ title: `Fix "${text(f.title, "this finding")}" with AI`,
116
+ confirmLabel: "Fix with AI",
117
+ description: "One AI pass rewrites the affected part of the draft. Uses credits for this finding only.",
118
+ run: async () => { await g8Post(`${base}/ai-review/fix-with-ai`, { finding: f }); setFindings((cur) => (cur ?? []).filter((x) => text(x.id) !== text(f.id))); },
119
+ onDone: () => onChanged?.(),
120
+ });
121
+ const toggle = (id: string): void => setPicked((cur) => (cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id]));
122
+ return (
123
+ <SectionCard title="Review" description="Find wording, brand and clarity problems, then apply the exact fixes."
124
+ actions={<div className="flex gap-1"><Button size="sm" variant="outline" onClick={() => review(false)}>Quick check</Button><Button size="sm" onClick={() => review(true)}>Review with AI</Button></div>}>
125
+ {findings === null ? <EmptyState compact title="No review yet" description="Run a review to see what could be better on this page." />
126
+ : findings.length === 0 ? <EmptyState compact title="Nothing to fix" description="The review found no remaining problems." />
127
+ : (
128
+ <div className="grid gap-2">
129
+ <ul className="grid gap-2">
130
+ {findings.map((f) => {
131
+ const id = text(f.id);
132
+ return (
133
+ <li key={id} className="flex items-start gap-3 rounded-md border p-3">
134
+ <input type="checkbox" aria-label={`Select ${text(f.title, id)}`} className="mt-1" checked={picked.includes(id)} onChange={() => toggle(id)} />
135
+ <div className="min-w-0 flex-1">
136
+ <div className="flex items-center gap-2"><span className="text-sm font-medium">{text(f.title, id)}</span><StatusPill tone={SEVERITY[text(f.severity).toLowerCase()] ?? "neutral"}>{text(f.severity, "note")}</StatusPill></div>
137
+ {text(f.description) ? <p className="text-muted-foreground text-xs">{text(f.description)}</p> : null}
138
+ {text(f.suggested_fix) ? <p className="text-xs">Suggested: {typeof f.suggested_fix === "string" ? f.suggested_fix : JSON.stringify(f.suggested_fix)}</p> : null}
139
+ </div>
140
+ <Button size="sm" variant="ghost" aria-label={`Fix ${text(f.title, id)} with AI`} onClick={() => fix(f)}>Fix with AI</Button>
141
+ </li>
142
+ );
143
+ })}
144
+ </ul>
145
+ <div className="flex items-center gap-2">
146
+ <Button size="sm" loading={apply.loading} disabled={picked.length === 0} onClick={() => void apply.mutate(undefined)}>Apply {picked.length || ""} selected (free)</Button>
147
+ <Button size="sm" variant="ghost" onClick={() => setPicked(findings.map((f) => text(f.id)))}>Select all</Button>
148
+ </div>
149
+ </div>
150
+ )}
151
+ {note ? <p role="status" className="mt-2 text-sm">{note}</p> : null}
152
+ {apply.error ? <ApiError error={apply.error} title="Could not apply the fixes" /> : null}
153
+ {failure ? <ApiError error={failure} title="That did not work" /> : null}
154
+ <ConfirmSpendDialog {...dialog} />
155
+ </SectionCard>
156
+ );
157
+ }
158
+
159
+ function SeoMetadata({ pageId, onChanged }: PageAiToolsProps) {
160
+ const { ask, dialog, failure } = useConfirmAction();
161
+ const [meta, setMeta] = useState<Rec | null>(null);
162
+ const use = useG8Mutation((description: string) => g8Patch(`/landing-pages/${encodeURIComponent(pageId)}`, { description }), { onSuccess: () => onChanged?.() });
163
+ const scalars = Object.entries(meta ?? {}).filter(([, v]) => typeof v === "string" || typeof v === "number" || (Array.isArray(v) && v.every((x) => typeof x === "string")));
164
+ const description = text(meta?.description) || text(meta?.meta_description);
165
+ return (
166
+ <SectionCard title="Search title and description" description="Write the title, description and keywords search engines show."
167
+ actions={<Button size="sm" onClick={() => ask({
168
+ tier: "billable", title: "Write search metadata with AI", confirmLabel: "Write metadata",
169
+ description: "One AI pass reads the page and proposes a title, description and keywords. Uses credits; the API gives no price in advance. Nothing is saved until you choose to.",
170
+ run: async () => { setMeta(asRec((await g8Post(`/landing-pages/${encodeURIComponent(pageId)}/generate-seo-metadata`)).data)); },
171
+ })}>Write with AI</Button>}>
172
+ {!meta ? <EmptyState compact title="Not generated yet" description="Generate a proposal, read it, then keep what you like." /> : (
173
+ <div className="grid gap-3">
174
+ <KeyValueList columns={1} items={scalars.map(([k, v]) => ({ label: k.replace(/_/g, " "), value: Array.isArray(v) ? v.join(", ") : String(v), wide: true }))} />
175
+ {description ? <div><Button size="sm" variant="outline" loading={use.loading} onClick={() => void use.mutate(description)}>Use as the page description</Button></div> : null}
176
+ {use.data ? <p role="status" className="text-sm">Description saved.</p> : null}
177
+ </div>
178
+ )}
179
+ {use.error ? <ApiError error={use.error} title="Could not save the description" /> : null}
180
+ {failure ? <ApiError error={failure} title="That did not work" /> : null}
181
+ <ConfirmSpendDialog {...dialog} />
182
+ </SectionCard>
183
+ );
184
+ }
185
+
186
+ export function PageAiTools({ pageId, onChanged, className }: PageAiToolsProps) {
187
+ return (
188
+ <div className={className}>
189
+ <div className="grid gap-4">
190
+ <ChatEdit pageId={pageId} onChanged={onChanged} />
191
+ <Review pageId={pageId} onChanged={onChanged} />
192
+ <SeoMetadata pageId={pageId} onChanged={onChanged} />
193
+ </div>
194
+ </div>
195
+ );
196
+ }
@@ -0,0 +1,176 @@
1
+ "use client";
2
+
3
+ /**
4
+ * PageAnalytics: how landing pages perform. Visits, identified people and companies, form submissions and sign-ups
5
+ * for a window and a traffic source, per page and in total, plus who visited. Pass `pageId` to scope everything to
6
+ * one page (the page detail tab); omit it for the whole portfolio.
7
+ *
8
+ * SDK operations, in the order the screen uses them (all read, free):
9
+ * 1. getLandingPageReportingOverview GET /landing-pages/reporting/overview?days&source&page_id
10
+ * 2. listLandingPageReportingPeople GET /landing-pages/reporting/people?cursor&limit&days&source&page_id (paged, "Load more")
11
+ * 3. listLandingPageReportingCompanies GET /landing-pages/reporting/companies?cursor&limit&days&source&page_id (paged, "Load more")
12
+ *
13
+ * The payloads are open-shaped in the contract: only values that are really numbers are shown, a missing one is a
14
+ * dash (never a zero). When graph8 cannot identify visitors right now it says so instead of showing an empty list.
15
+ */
16
+ import { useState } from "react";
17
+ import { Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@graph8/sdk/ui";
18
+ import { ApiError } from "@/g8/components/api-error";
19
+ import { EmptyState } from "@/g8/components/empty-state";
20
+ import { SectionCard } from "@/g8/components/screen/section-card";
21
+ import { StatStrip } from "@/g8/components/screen/stat-strip";
22
+ import type { StatItem } from "@/g8/components/screen/stat-strip";
23
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
24
+ import { usePaginated } from "@/g8/hooks/use-paginated";
25
+ import { g8Get } from "@/g8/lib/g8-client";
26
+ import { asRec, num, pageOf, rowsOf, shortDate, text } from "../lib/studio";
27
+ import type { Rec } from "../lib/studio";
28
+ import { MoreRows } from "./more-rows";
29
+
30
+ export interface PageAnalyticsProps {
31
+ /** Scope every number to one page. Omit for all pages. */
32
+ pageId?: string;
33
+ className?: string;
34
+ }
35
+
36
+ const SOURCES = ["all", "paid", "email", "organic", "referral", "direct"] as const;
37
+ const SELECT = "border-input bg-background h-9 rounded-md border px-2 text-sm";
38
+ const PAGE = 50;
39
+
40
+ const count = (v: unknown): string => (num(v) == null ? "-" : (v as number).toLocaleString());
41
+ const rate = (v: unknown): string => (num(v) == null ? "-" : `${((v as number) * 100).toFixed(1)}%`);
42
+
43
+ function PeopleList({ query }: { query: Record<string, string | number | string[]> }) {
44
+ const list = usePaginated<Rec>(async (cursor, signal) => {
45
+ const res = await g8Get("/landing-pages/reporting/people", { ...query, cursor: cursor ? Number(cursor) : 0, limit: PAGE }, { signal });
46
+ const d = asRec(res.data);
47
+ return pageOf(rowsOf<Rec>(d, "people"), d.next_cursor ?? res.pagination?.next_cursor);
48
+ }, [JSON.stringify(query)]);
49
+ if (list.error) return <ApiError error={list.error} title="Could not load visitors" onRetry={() => void list.reload()} />;
50
+ if (list.loading) return <Skeleton className="h-24 w-full" aria-busy="true" />;
51
+ if (list.items.length === 0) return <EmptyState compact title="No identified people yet" description="People appear here once a visitor is matched to a contact, for example after they submit a form." />;
52
+ return (
53
+ <>
54
+ <Table>
55
+ <TableHeader><TableRow><TableHead>Person</TableHead><TableHead>Visits</TableHead><TableHead>Top source</TableHead><TableHead>Last seen</TableHead></TableRow></TableHeader>
56
+ <TableBody>
57
+ {list.items.map((p, i) => (
58
+ <TableRow key={text(p.contact_id) || i}>
59
+ <TableCell className="font-medium">{text(p.name, "Identified contact")}</TableCell>
60
+ <TableCell>{count(p.visits)}</TableCell>
61
+ <TableCell>{text(p.top_source) || "-"}</TableCell>
62
+ <TableCell>{shortDate(p.last_seen) || "-"}</TableCell>
63
+ </TableRow>
64
+ ))}
65
+ </TableBody>
66
+ </Table>
67
+ <MoreRows shown={list.items.length} noun="people" hasMore={list.hasMore} loading={list.loadingMore} onMore={() => void list.loadMore()} />
68
+ </>
69
+ );
70
+ }
71
+
72
+ function CompaniesList({ query }: { query: Record<string, string | number | string[]> }) {
73
+ const list = usePaginated<Rec>(async (cursor, signal) => {
74
+ const res = await g8Get("/landing-pages/reporting/companies", { ...query, cursor: cursor ? Number(cursor) : 0, limit: PAGE }, { signal });
75
+ const d = asRec(res.data);
76
+ return pageOf(rowsOf<Rec>(d, "companies"), d.next_cursor ?? res.pagination?.next_cursor);
77
+ }, [JSON.stringify(query)]);
78
+ if (list.error) return <ApiError error={list.error} title="Could not load companies" onRetry={() => void list.reload()} />;
79
+ if (list.loading) return <Skeleton className="h-24 w-full" aria-busy="true" />;
80
+ if (list.items.length === 0) return <EmptyState compact title="No identified companies yet" description="Companies appear when a visit can be resolved to the organization behind it." />;
81
+ return (
82
+ <>
83
+ <Table>
84
+ <TableHeader><TableRow><TableHead>Company</TableHead><TableHead>Visits</TableHead><TableHead>Top source</TableHead><TableHead>Last seen</TableHead></TableRow></TableHeader>
85
+ <TableBody>
86
+ {list.items.map((c, i) => (
87
+ <TableRow key={text(c.company_id) || i}>
88
+ <TableCell className="font-medium">{text(c.company_name) || text(c.name) || text(c.domain) || "Unknown company"}</TableCell>
89
+ <TableCell>{count(c.visits)}</TableCell>
90
+ <TableCell>{text(c.top_source) || "-"}</TableCell>
91
+ <TableCell>{shortDate(c.last_seen) || "-"}</TableCell>
92
+ </TableRow>
93
+ ))}
94
+ </TableBody>
95
+ </Table>
96
+ <MoreRows shown={list.items.length} noun="companies" hasMore={list.hasMore} loading={list.loadingMore} onMore={() => void list.loadMore()} />
97
+ </>
98
+ );
99
+ }
100
+
101
+ export function PageAnalytics({ pageId, className }: PageAnalyticsProps) {
102
+ const [days, setDays] = useState(30);
103
+ const [source, setSource] = useState<(typeof SOURCES)[number]>("all");
104
+ const query: Record<string, string | number | string[]> = { days, source, ...(pageId ? { page_id: [pageId] } : {}) };
105
+ const overview = useG8Query((signal) => g8Get("/landing-pages/reporting/overview", query, { signal }), [days, source, pageId]);
106
+
107
+ const o = asRec(overview.data?.data);
108
+ const totals = asRec(o.totals);
109
+ const pages = rowsOf<Rec>(o.pages);
110
+ const sources = rowsOf<Rec>(o.sources);
111
+ const stats: StatItem[] = [
112
+ { label: "Page views", value: count(totals.page_views) },
113
+ { label: "Unique visitors", value: count(totals.unique_visitors) },
114
+ { label: "Identified people", value: o.people_resolution_available === false ? "-" : count(totals.identified_people), hint: o.people_resolution_available === false ? "Visitor identification is not available right now." : undefined },
115
+ { label: "Identified companies", value: o.company_resolution_available === false ? "-" : count(totals.identified_companies), hint: o.company_resolution_available === false ? "Company matching is catching up. This is not zero." : undefined },
116
+ { label: "Form submissions", value: count(totals.form_submissions) },
117
+ { label: "Form conversion", value: rate(totals.form_conversion_rate) },
118
+ ];
119
+
120
+ return (
121
+ <div className={className}>
122
+ <div className="mb-4 flex flex-wrap items-center gap-2">
123
+ <select aria-label="Reporting window" className={SELECT} value={days} onChange={(e) => setDays(Number(e.target.value))}>
124
+ {[7, 30, 90].map((d) => <option key={d} value={d}>Last {d} days</option>)}
125
+ </select>
126
+ <select aria-label="Traffic source" className={SELECT} value={source} onChange={(e) => setSource(e.target.value as (typeof SOURCES)[number])}>
127
+ {SOURCES.map((s) => <option key={s} value={s}>{s === "all" ? "All sources" : s[0].toUpperCase() + s.slice(1)}</option>)}
128
+ </select>
129
+ </div>
130
+ {overview.error ? <ApiError error={overview.error} title="Could not load page analytics" onRetry={() => void overview.refetch()} /> : (
131
+ <div className="grid gap-4">
132
+ <StatStrip items={stats} loading={overview.loading && !overview.data} />
133
+ {o.stale === true ? <p role="note" className="text-muted-foreground text-xs">These numbers may be a few minutes old.</p> : null}
134
+ {!pageId ? (
135
+ <SectionCard title="By page" flush>
136
+ {pages.length === 0 ? <EmptyState compact title="No published pages in this window" description="Only published pages with a URL are measured." /> : (
137
+ <Table>
138
+ <TableHeader><TableRow><TableHead>Page</TableHead><TableHead>Views</TableHead><TableHead>Visitors</TableHead><TableHead>Forms</TableHead><TableHead>Top source</TableHead></TableRow></TableHeader>
139
+ <TableBody>
140
+ {pages.map((p, i) => (
141
+ <TableRow key={text(p.id) || i}>
142
+ <TableCell className="font-medium">{text(p.name) || text(p.title) || text(p.id)}</TableCell>
143
+ <TableCell>{count(p.page_views)}</TableCell>
144
+ <TableCell>{count(p.unique_visitors)}</TableCell>
145
+ <TableCell>{count(p.form_submissions)}</TableCell>
146
+ <TableCell>{text(p.top_source) || "-"}</TableCell>
147
+ </TableRow>
148
+ ))}
149
+ </TableBody>
150
+ </Table>
151
+ )}
152
+ </SectionCard>
153
+ ) : null}
154
+ {sources.length > 0 ? (
155
+ <SectionCard title="Traffic sources" flush>
156
+ <Table>
157
+ <TableHeader><TableRow><TableHead>Source</TableHead><TableHead>Views</TableHead><TableHead>Visitors</TableHead></TableRow></TableHeader>
158
+ <TableBody>
159
+ {sources.slice(0, 10).map((s, i) => (
160
+ <TableRow key={i}><TableCell className="font-medium">{text(s.channel) || "Unknown"}</TableCell><TableCell>{count(s.page_views)}</TableCell><TableCell>{count(s.unique_visitors)}</TableCell></TableRow>
161
+ ))}
162
+ </TableBody>
163
+ </Table>
164
+ </SectionCard>
165
+ ) : null}
166
+ <SectionCard title="Identified people" description="Visitors matched to a contact.">
167
+ <PeopleList query={query} />
168
+ </SectionCard>
169
+ <SectionCard title="Identified companies" description="Organizations behind the visits.">
170
+ <CompaniesList query={query} />
171
+ </SectionCard>
172
+ </div>
173
+ )}
174
+ </div>
175
+ );
176
+ }