@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
@@ -12,25 +12,63 @@
12
12
  "foundation"
13
13
  ],
14
14
  "operations": [
15
- "list_crm_syncs_crm_syncs_get",
16
- "list_connections_integrations_connections_get",
17
- "get_slack_status_integrations_slack_status_get",
18
- "get_google_workspace_status_integrations_google_workspace_get",
19
- "get_roam_status_integrations_roam_status_get",
15
+ "cancel_migration_integrations_migrations__migration_id__cancel_post",
16
+ "confirm_migration_integrations_migrations__migration_id__confirm_post",
17
+ "create_webhook_webhooks_post",
20
18
  "delete_connection_integrations_connections__connection_id__delete",
21
- "get_crm_status_crm_syncs__provider__status_get",
19
+ "delete_webhook_webhooks__webhook_id__delete",
20
+ "disconnect_sugarcrm_integrations_sugarcrm_delete",
21
+ "discover_crm_fields_integrations_crm_fields_get",
22
+ "get_crm_company_integrations_crm_connections__connection_id__companies__company_id__get",
23
+ "get_crm_connection_integrations_crm_connections__connection_id__get",
24
+ "get_crm_connection_metadata_integrations_crm_connections__connection_id__metadata_get",
25
+ "get_crm_connection_status_integrations_crm_connections__connection_id__status_get",
26
+ "get_crm_contact_integrations_crm_connections__connection_id__contacts__contact_id__get",
27
+ "get_crm_deal_integrations_crm_connections__connection_id__deals__deal_id__get",
22
28
  "get_crm_fields_crm_syncs__provider__fields_get",
23
- "set_push_sync_enabled_integrations_crm_sync_push_enabled_put",
24
- "set_pull_sync_enabled_integrations_crm_sync_pull_enabled_put",
29
+ "get_crm_lead_integrations_crm_connections__connection_id__leads__lead_id__get",
30
+ "get_crm_status_crm_syncs__provider__status_get",
31
+ "get_google_workspace_status_integrations_google_workspace_get",
32
+ "get_last_completed_migration_integrations_connections__connection_id__last_migration_get",
33
+ "get_migration_status_integrations_migrations__migration_id__get",
34
+ "get_roam_status_integrations_roam_status_get",
35
+ "get_slack_status_integrations_slack_status_get",
25
36
  "get_sync_job_integrations_crm_sync_jobs__job_id__get",
26
- "push_contacts_to_crm_crm_syncs__provider__contacts_push_post",
27
- "list_webhooks_webhooks_get",
37
+ "get_webhook_webhooks__webhook_id__get",
38
+ "list_ad_account_health_audience_syncs_account_health_get",
39
+ "list_ad_accounts_audience_syncs_accounts_get",
40
+ "list_ad_platforms_audience_syncs_platforms_get",
41
+ "list_connections_integrations_connections_get",
42
+ "list_crm_activities_integrations_crm_activities_get",
43
+ "list_crm_companies_integrations_crm_connections__connection_id__companies_get",
44
+ "list_crm_contact_activities_integrations_crm_connections__connection_id__contacts__contact_id__activities_get",
45
+ "list_crm_contacts_integrations_crm_connections__connection_id__contacts_get",
46
+ "list_crm_deals_integrations_crm_connections__connection_id__deals_get",
47
+ "list_crm_leads_integrations_crm_connections__connection_id__leads_get",
48
+ "list_crm_syncs_crm_syncs_get",
49
+ "list_google_drive_folder_files_integrations_google_workspace_folders__folder_id__files_get",
50
+ "list_google_drive_folders_integrations_google_workspace_folders_get",
51
+ "list_lead_statuses_integrations_crm_sync_leads_statuses_get",
52
+ "list_mappable_graph8_fields_integrations_crm_fields_mappable_get",
53
+ "list_webhook_deliveries_webhooks__webhook_id__deliveries_get",
28
54
  "list_webhook_events_webhooks_events_get",
29
- "create_webhook_webhooks_post",
30
- "update_webhook_webhooks__webhook_id__patch",
55
+ "list_webhooks_webhooks_get",
56
+ "preview_lead_push_integrations_crm_sync_leads_push_preview_get",
57
+ "pull_all_contacts_integrations_crm_sync_contacts_pull_post",
58
+ "pull_all_deals_integrations_crm_sync_deals_pull_post",
59
+ "pull_all_leads_integrations_crm_sync_leads_pull_post",
60
+ "push_all_contacts_integrations_crm_sync_contacts_push_post",
61
+ "push_all_deals_integrations_crm_sync_deals_push_post",
62
+ "push_all_leads_integrations_crm_sync_leads_push_post",
63
+ "push_contacts_to_crm_crm_syncs__provider__contacts_push_post",
31
64
  "rotate_webhook_secret_webhooks__webhook_id__rotate_secret_post",
32
- "list_webhook_deliveries_webhooks__webhook_id__deliveries_get",
33
- "delete_webhook_webhooks__webhook_id__delete"
65
+ "set_pull_sync_enabled_integrations_crm_sync_pull_enabled_put",
66
+ "set_push_sync_enabled_integrations_crm_sync_push_enabled_put",
67
+ "start_delta_sync_integrations_migrations_delta_sync_post",
68
+ "start_migration_discovery_integrations_migrations_discovery_post",
69
+ "test_google_workspace_connection_integrations_google_workspace_test_post",
70
+ "update_webhook_webhooks__webhook_id__patch",
71
+ "validate_sugarcrm_integrations_sugarcrm_validate_post"
34
72
  ],
35
73
  "components": [
36
74
  {
@@ -122,13 +160,137 @@
122
160
  "list_webhook_deliveries_webhooks__webhook_id__deliveries_get",
123
161
  "update_webhook_webhooks__webhook_id__patch",
124
162
  "rotate_webhook_secret_webhooks__webhook_id__rotate_secret_post",
125
- "delete_webhook_webhooks__webhook_id__delete"
163
+ "delete_webhook_webhooks__webhook_id__delete",
164
+ "get_webhook_webhooks__webhook_id__get"
165
+ ],
166
+ "tiers": [
167
+ "read",
168
+ "external",
169
+ "destructive"
170
+ ]
171
+ },
172
+ {
173
+ "name": "ConnectionHealth",
174
+ "file": "components/connection-health.tsx",
175
+ "purpose": "Live status, connection record, CRM account details and the last historic import for one CRM connection.",
176
+ "operations": [
177
+ "get_crm_connection_integrations_crm_connections__connection_id__get",
178
+ "get_crm_connection_status_integrations_crm_connections__connection_id__status_get",
179
+ "get_crm_connection_metadata_integrations_crm_connections__connection_id__metadata_get",
180
+ "get_last_completed_migration_integrations_connections__connection_id__last_migration_get"
181
+ ],
182
+ "tiers": [
183
+ "read"
184
+ ]
185
+ },
186
+ {
187
+ "name": "SyncTriggers",
188
+ "file": "components/sync-triggers.tsx",
189
+ "purpose": "Run a sync now: push or pull contacts, deals and leads as background jobs with a lead push preview; every sync confirmed.",
190
+ "operations": [
191
+ "get_sync_job_integrations_crm_sync_jobs__job_id__get",
192
+ "list_lead_statuses_integrations_crm_sync_leads_statuses_get",
193
+ "preview_lead_push_integrations_crm_sync_leads_push_preview_get",
194
+ "push_all_contacts_integrations_crm_sync_contacts_push_post",
195
+ "pull_all_contacts_integrations_crm_sync_contacts_pull_post",
196
+ "push_all_deals_integrations_crm_sync_deals_push_post",
197
+ "pull_all_deals_integrations_crm_sync_deals_pull_post",
198
+ "push_all_leads_integrations_crm_sync_leads_push_post",
199
+ "pull_all_leads_integrations_crm_sync_leads_pull_post"
200
+ ],
201
+ "tiers": [
202
+ "read",
203
+ "external"
204
+ ]
205
+ },
206
+ {
207
+ "name": "CrmRecords",
208
+ "file": "components/crm-records.tsx",
209
+ "purpose": "Browse the contacts, companies, deals, leads and activity log inside a connected CRM, read only.",
210
+ "operations": [
211
+ "list_crm_contact_activities_integrations_crm_connections__connection_id__contacts__contact_id__activities_get",
212
+ "list_crm_activities_integrations_crm_activities_get",
213
+ "list_crm_contacts_integrations_crm_connections__connection_id__contacts_get",
214
+ "list_crm_companies_integrations_crm_connections__connection_id__companies_get",
215
+ "list_crm_deals_integrations_crm_connections__connection_id__deals_get",
216
+ "list_crm_leads_integrations_crm_connections__connection_id__leads_get",
217
+ "get_crm_contact_integrations_crm_connections__connection_id__contacts__contact_id__get",
218
+ "get_crm_company_integrations_crm_connections__connection_id__companies__company_id__get",
219
+ "get_crm_deal_integrations_crm_connections__connection_id__deals__deal_id__get",
220
+ "get_crm_lead_integrations_crm_connections__connection_id__leads__lead_id__get"
221
+ ],
222
+ "tiers": [
223
+ "read"
224
+ ]
225
+ },
226
+ {
227
+ "name": "FieldMappings",
228
+ "file": "components/field-mappings.tsx",
229
+ "purpose": "The CRM's fields next to the graph8 fields they can map to, searchable, read only.",
230
+ "operations": [
231
+ "discover_crm_fields_integrations_crm_fields_get",
232
+ "list_mappable_graph8_fields_integrations_crm_fields_mappable_get"
233
+ ],
234
+ "tiers": [
235
+ "read"
236
+ ]
237
+ },
238
+ {
239
+ "name": "MigrationPanel",
240
+ "file": "components/migration-panel.tsx",
241
+ "purpose": "Scan a CRM, review the counts, import its history, sync later changes; every step confirmed.",
242
+ "operations": [
243
+ "get_last_completed_migration_integrations_connections__connection_id__last_migration_get",
244
+ "get_migration_status_integrations_migrations__migration_id__get",
245
+ "start_migration_discovery_integrations_migrations_discovery_post",
246
+ "start_delta_sync_integrations_migrations_delta_sync_post",
247
+ "confirm_migration_integrations_migrations__migration_id__confirm_post",
248
+ "cancel_migration_integrations_migrations__migration_id__cancel_post"
126
249
  ],
127
250
  "tiers": [
128
251
  "read",
129
252
  "external",
130
253
  "destructive"
131
254
  ]
255
+ },
256
+ {
257
+ "name": "SugarCrmConnect",
258
+ "file": "components/sugarcrm-connect.tsx",
259
+ "purpose": "Connect SugarCRM with an instance address and login (the one CRM the API can sign in to), check, update credentials, remove.",
260
+ "operations": [
261
+ "validate_sugarcrm_integrations_sugarcrm_validate_post",
262
+ "disconnect_sugarcrm_integrations_sugarcrm_delete"
263
+ ],
264
+ "tiers": [
265
+ "read",
266
+ "destructive"
267
+ ]
268
+ },
269
+ {
270
+ "name": "WorkspaceTools",
271
+ "file": "components/workspace-tools.tsx",
272
+ "purpose": "Slack, Google Workspace and Roam with live status and a connect link-out; Google connection test and Drive browser.",
273
+ "operations": [
274
+ "list_google_drive_folders_integrations_google_workspace_folders_get",
275
+ "list_google_drive_folder_files_integrations_google_workspace_folders__folder_id__files_get",
276
+ "test_google_workspace_connection_integrations_google_workspace_test_post"
277
+ ],
278
+ "tiers": [
279
+ "read"
280
+ ]
281
+ },
282
+ {
283
+ "name": "AdAccounts",
284
+ "file": "components/ad-accounts.tsx",
285
+ "purpose": "Ad platforms, their accounts and any account the platform has paused, with a connect link-out.",
286
+ "operations": [
287
+ "list_ad_platforms_audience_syncs_platforms_get",
288
+ "list_ad_accounts_audience_syncs_accounts_get",
289
+ "list_ad_account_health_audience_syncs_account_health_get"
290
+ ],
291
+ "tiers": [
292
+ "read"
293
+ ]
132
294
  }
133
295
  ],
134
296
  "hooks": [],
@@ -136,6 +298,18 @@
136
298
  {
137
299
  "file": "lib/integrations-utils.ts",
138
300
  "purpose": "CRM provider list and labels, status types and envelope helpers shared by the integrations components."
301
+ },
302
+ {
303
+ "file": "lib/load-more.tsx",
304
+ "purpose": "Paging helpers (useLoadMoreList, useGrowingList, LoadMore) so no list stops silently at a cap."
305
+ },
306
+ {
307
+ "file": "lib/crm-utils.ts",
308
+ "purpose": "Readers for records in a CRM's own shape: title, subtitle, flattened fields."
309
+ },
310
+ {
311
+ "file": "lib/facts.tsx",
312
+ "purpose": "FactList: renders an open-shaped API object as labelled facts."
139
313
  }
140
314
  ],
141
315
  "recipes": [
@@ -0,0 +1,82 @@
1
+ "use client";
2
+
3
+ /**
4
+ * AdAccounts: the ad platforms graph8 can sync audiences to (Google Ads, Meta, LinkedIn and others), whether each is connected, how many syncs
5
+ * it carries, which accounts are available, and a warning for any account the platform has paused or de-authorised (the usual reason a
6
+ * sync stopped). A platform that is not connected shows a "Connect in graph8" link: the sign-in screen only runs in the graph8 app.
7
+ * All reads; the health list is the last hourly check, not a live call. Creating and running the syncs themselves is on the Ads screen.
8
+ *
9
+ * SDK operations, in order:
10
+ * 1. listAdPlatforms GET /audience-syncs/platforms (read) platform, connected, active_syncs
11
+ * 2. listAdAccounts GET /audience-syncs/accounts (read) the accounts you can sync to
12
+ * 3. listAdAccountHealth GET /audience-syncs/account-health (read) only the accounts that are NOT healthy
13
+ */
14
+ import { Skeleton } from "@graph8/sdk/ui";
15
+ import { ApiError } from "@/g8/components/api-error";
16
+ import { EmptyState } from "@/g8/components/empty-state";
17
+ import { SectionCard } from "@/g8/components/screen/section-card";
18
+ import { StatusPill } from "@/g8/components/screen/status-pill";
19
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
20
+ import { brandUrl, useBrand } from "@/g8/hooks/use-brand";
21
+ import { g8Get } from "@/g8/lib/g8-client";
22
+ import { arrayOf, GRAPH8_APP_INTEGRATIONS_PATH } from "../lib/integrations-utils";
23
+
24
+ export interface AdAccountsProps {
25
+ /** Where "Connect" sends people. Default: graph8 Settings > Integrations. */
26
+ connectUrl?: string;
27
+ className?: string;
28
+ }
29
+
30
+ interface Platform { platform: string; connected?: boolean; account_name?: string | null; account_id?: string | null; active_syncs?: number }
31
+ // /audience-syncs/accounts answers ad_account_id / ad_account_name.
32
+ interface Account { platform?: string; ad_account_id?: string | number; ad_account_name?: string }
33
+ interface Health { platform?: string; account_id?: string | number; health?: string; reason?: string; message?: string; detail?: string }
34
+ const label = (s: string): string => s.replace(/[_-]/g, " ").replace(/^./, (c) => c.toUpperCase());
35
+
36
+ export function AdAccounts({ connectUrl: connectUrlProp, className }: AdAccountsProps) {
37
+ const brand = useBrand();
38
+ const connectUrl = connectUrlProp ?? brandUrl(brand, GRAPH8_APP_INTEGRATIONS_PATH);
39
+ const platforms = useG8Query((signal) => g8Get("/audience-syncs/platforms", undefined, { signal }), []);
40
+ const accounts = useG8Query((signal) => g8Get("/audience-syncs/accounts", undefined, { signal }), []);
41
+ const health = useG8Query((signal) => g8Get("/audience-syncs/account-health", undefined, { signal }), []);
42
+ const unhealthy = arrayOf<Health>(health.data);
43
+ const accountRows = arrayOf<Account>(accounts.data);
44
+ const platformRows = arrayOf<Platform>(platforms.data);
45
+
46
+ return (
47
+ <div className={`flex flex-col gap-4 ${className ?? ""}`}>
48
+ {unhealthy.length > 0 ? (
49
+ <div role="alert" className="rounded-md border border-error-foreground/25 bg-error p-3 text-sm text-error-foreground" data-testid="ad-health-warning">
50
+ <p className="font-medium">{unhealthy.length === 1 ? "An ad account needs attention" : `${unhealthy.length} ad accounts need attention`}</p>
51
+ <ul className="mt-1 list-disc pl-5">{unhealthy.map((h, i) => <li key={`${h.platform}-${h.account_id}-${i}`}>{label(h.platform ?? "Platform")} account {String(h.account_id ?? "")}: {label(h.health ?? "unhealthy")}{h.reason || h.message || h.detail ? ` (${h.reason ?? h.message ?? h.detail})` : ""}</li>)}</ul>
52
+ </div>
53
+ ) : null}
54
+ {health.error ? <ApiError compact error={health.error} title="Could not check account health" onRetry={() => void health.refetch()} /> : null}
55
+ <SectionCard title="Ad platforms" description={`Where ${brand.name} can sync audiences`} flush>
56
+ {platforms.error ? <div className="p-3"><ApiError error={platforms.error} title="Could not load the platforms" onRetry={() => void platforms.refetch()} /></div> : null}
57
+ {platforms.loading && !platforms.data ? <Skeleton className="m-3 h-12" /> : null}
58
+ <ul className="divide-y" aria-label="Ad platforms">
59
+ {platformRows.map((p) => (
60
+ <li key={p.platform} data-platform={p.platform} className="flex flex-wrap items-center justify-between gap-2 px-4 py-3 text-sm">
61
+ <span><span className="font-medium">{label(p.platform)}</span><span className="block text-xs text-muted-foreground">{p.connected ? `${p.active_syncs ?? 0} active ${p.active_syncs === 1 ? "sync" : "syncs"}${p.account_name ? `, ${p.account_name}` : ""}` : "Not connected"}</span></span>
62
+ {p.connected ? <StatusPill tone="positive" dot>Connected</StatusPill> : <a className="text-sm underline" href={connectUrl} target="_blank" rel="noreferrer">Connect in {brand.linkLabel}</a>}
63
+ </li>
64
+ ))}
65
+ </ul>
66
+ {platforms.data && platformRows.length === 0 ? <EmptyState compact title="No ad platforms" description="Ad platforms appear here once they are available to your workspace." /> : null}
67
+ </SectionCard>
68
+ <SectionCard title="Accounts" description="The ad accounts you can sync to" flush>
69
+ {accounts.error ? <div className="p-3"><ApiError error={accounts.error} title="Could not load the accounts" onRetry={() => void accounts.refetch()} /></div> : null}
70
+ {accounts.loading && !accounts.data ? <Skeleton className="m-3 h-12" /> : null}
71
+ <ul className="divide-y" aria-label="Ad accounts">
72
+ {accountRows.map((a, i) => {
73
+ const id = String(a.ad_account_id ?? "");
74
+ const bad = unhealthy.find((h) => String(h.account_id) === id && (!h.platform || !a.platform || h.platform === a.platform));
75
+ return <li key={`${a.platform}-${id}-${i}`} className="flex items-center justify-between gap-2 px-4 py-2 text-sm"><span>{a.ad_account_name || id}<span className="ml-2 text-xs text-muted-foreground">{a.platform ? label(a.platform) : ""} {id}</span></span><StatusPill tone={bad ? "negative" : "positive"} dot>{bad ? label(bad.health ?? "unhealthy") : "Healthy"}</StatusPill></li>;
76
+ })}
77
+ </ul>
78
+ {accounts.data && accountRows.length === 0 ? <EmptyState compact title="No ad accounts" description={`Connect an ad platform in ${brand.linkLabel} to see its accounts here.`} /> : null}
79
+ </SectionCard>
80
+ </div>
81
+ );
82
+ }
@@ -0,0 +1,59 @@
1
+ "use client";
2
+
3
+ /**
4
+ * ConnectionHealth: is this CRM connection actually working? Its connection record, its live status check ("Recheck" asks the CRM again)
5
+ * and the CRM's account metadata (which edition, which objects it exposes). Reads only; nothing here changes the connection.
6
+ * The checks are read-only but reach the CRM, so a CRM that is down answers with an error and Retry rather than a stale "connected".
7
+ *
8
+ * SDK operations, in order:
9
+ * 1. getCrmConnection GET /integrations/crm/connections/{connection_id} (read)
10
+ * 2. getCrmConnectionStatus GET /integrations/crm/connections/{connection_id}/status (read)
11
+ * 3. getCrmConnectionMetadata GET /integrations/crm/connections/{connection_id}/metadata?crm_type (read)
12
+ * 4. getLastCompletedMigration GET /integrations/connections/{connection_id}/last-migration (read) last historic import
13
+ */
14
+ import { Button, Skeleton } from "@graph8/sdk/ui";
15
+ import { ApiError } from "@/g8/components/api-error";
16
+ import { SectionCard } from "@/g8/components/screen/section-card";
17
+ import { StatusPill } from "@/g8/components/screen/status-pill";
18
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
19
+ import { g8Get } from "@/g8/lib/g8-client";
20
+ import { FactList } from "../lib/facts";
21
+ import { crmType } from "../lib/integrations-utils";
22
+
23
+ export interface ConnectionHealthProps {
24
+ connectionId: string;
25
+ /** hubspot, salesforce, ... (sent as crm_type for the metadata read). */
26
+ provider: string;
27
+ className?: string;
28
+ }
29
+
30
+ const enc = encodeURIComponent;
31
+ const data = (res: unknown): Record<string, unknown> => ((res as { data?: Record<string, unknown> } | null)?.data ?? {}) as Record<string, unknown>;
32
+
33
+ function Card({ title, query, empty }: { title: string; query: { data: unknown; error: unknown; loading: boolean; refetch: () => Promise<unknown> }; empty: string }) {
34
+ return (
35
+ <SectionCard title={title} actions={<Button size="sm" variant="ghost" onClick={() => void query.refetch()}>Recheck</Button>}>
36
+ {query.error ? <ApiError error={query.error} title={`Could not check: ${title.toLowerCase()}`} onRetry={() => void query.refetch()} /> : null}
37
+ {query.loading && !query.data ? <Skeleton className="h-12 w-full" /> : null}
38
+ {query.data && !query.error ? <FactList data={data(query.data)} empty={empty} /> : null}
39
+ </SectionCard>
40
+ );
41
+ }
42
+
43
+ export function ConnectionHealth({ connectionId, provider, className }: ConnectionHealthProps) {
44
+ const connection = useG8Query((signal) => g8Get(`/integrations/crm/connections/${enc(connectionId)}`, undefined, { signal }), [connectionId]);
45
+ const status = useG8Query((signal) => g8Get(`/integrations/crm/connections/${enc(connectionId)}/status`, undefined, { signal }), [connectionId]);
46
+ const metadata = useG8Query((signal) => g8Get(`/integrations/crm/connections/${enc(connectionId)}/metadata`, { crm_type: crmType(provider) }, { signal }), [connectionId, provider]);
47
+ const last = useG8Query((signal) => g8Get(`/integrations/connections/${enc(connectionId)}/last-migration`, undefined, { signal }), [connectionId]);
48
+ const s = data(status.data);
49
+ const ok = s.connected ?? s.is_connected ?? s.healthy;
50
+ return (
51
+ <div className={`flex flex-col gap-4 ${className ?? ""}`}>
52
+ {status.data && !status.error ? <div className="flex items-center gap-2"><StatusPill tone={ok === false ? "negative" : "positive"} dot>{ok === false ? "Not healthy" : "Healthy"}</StatusPill><span className="text-xs text-muted-foreground">Checked just now</span></div> : null}
53
+ <Card title="Live status" query={status} empty="The CRM reported no details." />
54
+ <Card title="Connection" query={connection} empty="No connection details." />
55
+ <Card title="CRM account" query={metadata} empty="The CRM reported no account details." />
56
+ <Card title="Last historic import" query={last} empty="No historic import has completed for this connection." />
57
+ </div>
58
+ );
59
+ }
@@ -11,6 +11,10 @@
11
11
  * 2. listConnections GET /integrations/connections (read; the connection to disconnect)
12
12
  * 3. deleteConnection DELETE /integrations/connections/{id}?provider_config_key= (destructive, confirmed)
13
13
  * 4. SyncStatus see sync-status.tsx (getCrmStatus, getCrmFields, set push/pull enabled, getSyncJob, pushContactsToCrm)
14
+ * A connected CRM also gets: Health (ConnectionHealth), Sync now (SyncTriggers: push/pull contacts, deals and leads as background jobs),
15
+ * CRM records (CrmRecords: browse what is in the CRM), Fields (FieldMappings) and Historic import (MigrationPanel). SugarCRM, whose sign-in
16
+ * the API can run, gets a Connection tab (SugarCrmConnect) instead of the link-out; the other CRMs link out because their OAuth screen only
17
+ * runs in the graph8 app.
14
18
  */
15
19
  import type { ReactNode } from "react";
16
20
  import { Skeleton } from "@graph8/sdk/ui";
@@ -25,7 +29,13 @@ import { arrayOf, formatWhen, GRAPH8_APP_INTEGRATIONS_PATH, PROVIDER_LABELS } fr
25
29
  import type { ConnectionRow, CrmProvider, CrmSyncRow } from "../lib/integrations-utils";
26
30
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
27
31
  import { AskButton, useConfirmAction } from "@/g8/components/confirmed-action";
32
+ import { ConnectionHealth } from "./connection-health";
33
+ import { CrmRecords } from "./crm-records";
34
+ import { FieldMappings } from "./field-mappings";
35
+ import { MigrationPanel } from "./migration-panel";
36
+ import { SugarCrmConnect } from "./sugarcrm-connect";
28
37
  import { SyncStatus } from "./sync-status";
38
+ import { SyncTriggers } from "./sync-triggers";
29
39
  import { brandUrl, useBrand } from "@/g8/hooks/use-brand";
30
40
 
31
41
  export interface CrmDetailProps {
@@ -60,6 +70,7 @@ export function CrmDetail({ provider, connectUrl: connectUrlProp, records, onClo
60
70
  const connected = Boolean(row && CONNECTED.includes(row.status?.toLowerCase()));
61
71
  const all = arrayOf<ConnectionRow>(connections.data).filter((c) => (c.provider ?? "").toLowerCase() === provider || (c.provider_config_key ?? "").toLowerCase().split(/[-_]/)[0] === provider);
62
72
  const connection = all.find((c) => row?.connection_id && c.connection_id === row.connection_id) ?? (all.length === 1 ? all[0] : undefined);
73
+ const connectionId = row?.connection_id ?? connection?.connection_id ?? "";
63
74
  const changed = () => { void syncs.refetch(); void connections.refetch(); onChanged?.(); };
64
75
 
65
76
  const actions = connected ? (
@@ -68,7 +79,7 @@ export function CrmDetail({ provider, connectUrl: connectUrlProp, records, onClo
68
79
  description="Syncs configured against this connection stop working. Records already synced stay."
69
80
  run={() => g8Delete(`/integrations/connections/${connection!.connection_id}`, { query: { provider_config_key: connection!.provider_config_key } })} onDone={changed} />
70
81
  ) : (
71
- <a className="text-sm underline" href={connectUrl} target="_blank" rel="noreferrer">Connect in {brand.linkLabel}</a>
82
+ provider === "sugarcrm" ? null : <a className="text-sm underline" href={connectUrl} target="_blank" rel="noreferrer">Connect in {brand.linkLabel}</a>
72
83
  );
73
84
 
74
85
  const tabs = [
@@ -87,6 +98,14 @@ export function CrmDetail({ provider, connectUrl: connectUrlProp, records, onClo
87
98
  ),
88
99
  },
89
100
  { id: "sync", label: "Sync", content: <SyncStatus provider={provider} records={records} /> },
101
+ ...(provider === "sugarcrm" ? [{ id: "connection", label: "Connection", content: <SugarCrmConnect connected={connected} onChanged={changed} /> }] : []),
102
+ ...(connected && connectionId ? [
103
+ { id: "health", label: "Health", content: <ConnectionHealth connectionId={connectionId} provider={provider} /> },
104
+ { id: "sync-now", label: "Sync now", content: <SyncTriggers connectionId={connectionId} provider={provider} /> },
105
+ { id: "records", label: "CRM records", content: <CrmRecords connectionId={connectionId} /> },
106
+ { id: "fields", label: "Fields", content: <FieldMappings connectionId={connectionId} /> },
107
+ { id: "import", label: "Historic import", content: <MigrationPanel connectionId={connectionId} provider={provider} /> },
108
+ ] : []),
90
109
  ];
91
110
  return (
92
111
  <>
@@ -0,0 +1,103 @@
1
+ "use client";
2
+
3
+ /**
4
+ * CrmRecords: look inside a connected CRM without leaving graph8. Contacts, Companies, Deals and Leads as lists (25 at a time, "Load more"
5
+ * pages through all of them), the CRM's own activity log, and a record's fields (plus a contact's activities) when you open one.
6
+ * Read only: changing CRM records from here is deliberately not offered (use the sync to push graph8 data; edit in the CRM itself).
7
+ * Not every CRM has every kind (Pipedrive has no leads in the same sense); a kind the CRM refuses shows its error with Retry.
8
+ *
9
+ * SDK operations, in order (connection_id from the connection):
10
+ * 1. listCrmContacts / listCrmCompanies / listCrmDeals / listCrmLeads GET /integrations/crm/connections/{connection_id}/{kind}?limit&offset[&name] (read)
11
+ * 2. getCrmContact / getCrmCompany / getCrmDeal / getCrmLead GET /integrations/crm/connections/{connection_id}/{kind}/{id} (read)
12
+ * 3. listCrmContactActivities GET /integrations/crm/connections/{connection_id}/contacts/{contact_id}/activities?limit (read)
13
+ * 4. listCrmActivities GET /integrations/crm/activities?connection_id&limit&offset (read)
14
+ */
15
+ import { useEffect, useState } from "react";
16
+ import { Button, Skeleton } from "@graph8/sdk/ui";
17
+ import { ApiError } from "@/g8/components/api-error";
18
+ import { EmptyState } from "@/g8/components/empty-state";
19
+ import { SectionCard } from "@/g8/components/screen/section-card";
20
+ import { Toolbar } from "@/g8/components/screen/toolbar";
21
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
22
+ import { g8Get } from "@/g8/lib/g8-client";
23
+ import { CRM_ENTITIES, crmRecordSubtitle, crmRecordTitle, flattenRecord } from "../lib/crm-utils";
24
+ import type { CrmEntity, CrmRecord } from "../lib/crm-utils";
25
+ import { FactList } from "../lib/facts";
26
+ import { LoadMore, useLoadMoreList } from "../lib/load-more";
27
+ import { arrayOf, formatWhen } from "../lib/integrations-utils";
28
+
29
+ export interface CrmRecordsProps {
30
+ connectionId: string;
31
+ className?: string;
32
+ }
33
+
34
+ type Tab = CrmEntity | "activity";
35
+ const enc = encodeURIComponent;
36
+
37
+ function RecordFacts({ connectionId, entity, id }: { connectionId: string; entity: CrmEntity; id: string }) {
38
+ const rec = useG8Query((signal) => g8Get<CrmRecord>(`/integrations/crm/connections/${enc(connectionId)}/${entity}/${enc(id)}`, undefined, { signal }), [connectionId, entity, id]);
39
+ const acts = useG8Query((signal) => g8Get(`/integrations/crm/connections/${enc(connectionId)}/contacts/${enc(id)}/activities`, { limit: 20 }, { signal }), [connectionId, id], { enabled: entity === "contacts" });
40
+ const data = ((rec.data as { data?: CrmRecord } | undefined)?.data ?? {}) as CrmRecord;
41
+ const rows = arrayOf<Record<string, unknown>>(acts.data);
42
+ return (
43
+ <SectionCard title={crmRecordTitle(data) || "Record"} description={rec.loading ? "Loading..." : crmRecordSubtitle(data) || undefined}>
44
+ {rec.error ? <ApiError error={rec.error} title="Could not load the record" onRetry={() => void rec.refetch()} /> : null}
45
+ {rec.loading && !rec.data ? <Skeleton className="h-16 w-full" /> : null}
46
+ {rec.data ? <FactList data={flattenRecord(data)} empty="This record has no fields we can show." /> : null}
47
+ {entity === "contacts" ? (
48
+ <div className="mt-4">
49
+ <h4 className="mb-1 text-sm font-medium">Recent activity</h4>
50
+ {acts.error ? <ApiError compact error={acts.error} title="Could not load the activities" onRetry={() => void acts.refetch()} /> : null}
51
+ {acts.loading && !acts.data ? <Skeleton className="h-8 w-full" /> : null}
52
+ {acts.data && rows.length === 0 ? <p className="text-sm text-muted-foreground">No activity in the CRM.</p> : null}
53
+ {rows.length > 0 ? <><ul className="divide-y rounded-md border text-sm">{rows.map((a, i) => <li key={String(a.id ?? i)} className="px-3 py-1.5">{String(a.subject ?? a.type ?? a.title ?? "Activity")}<span className="ml-2 text-xs text-muted-foreground">{formatWhen(String(a.timestamp ?? a.created_at ?? a.date ?? "") || null)}</span></li>)}</ul>{rows.length >= 20 ? <p className="mt-1 text-xs text-muted-foreground">Showing the latest 20 activities.</p> : null}</> : null}
54
+ </div>
55
+ ) : null}
56
+ </SectionCard>
57
+ );
58
+ }
59
+
60
+ export function CrmRecords({ connectionId, className }: CrmRecordsProps) {
61
+ const [tab, setTab] = useState<Tab>("contacts");
62
+ const [search, setSearch] = useState("");
63
+ const [open, setOpen] = useState<string | null>(null);
64
+ useEffect(() => { setOpen(null); setSearch(""); }, [connectionId, tab]);
65
+ const q = search.trim();
66
+ const list = useLoadMoreList<CrmRecord>(async (offset, limit, signal) => {
67
+ const base = `/integrations/crm/connections/${enc(connectionId)}`;
68
+ const res = tab === "activity" ? await g8Get("/integrations/crm/activities", { connection_id: connectionId, limit, offset }, { signal })
69
+ : await g8Get(`${base}/${tab}`, { limit, offset, name: tab === "contacts" ? q || undefined : undefined }, { signal });
70
+ return arrayOf<CrmRecord>(res);
71
+ }, [connectionId, tab, q], { pageSize: 25 });
72
+
73
+ return (
74
+ <div className={`flex flex-col gap-3 ${className ?? ""}`}>
75
+ <div role="tablist" aria-label="CRM record kinds" className="flex flex-wrap gap-1 border-b">
76
+ {([...CRM_ENTITIES, { id: "activity", label: "Activity log" }] as const).map((t) => (
77
+ <button key={t.id} type="button" role="tab" aria-selected={tab === t.id} onClick={() => setTab(t.id as Tab)} className={`px-3 py-2 text-sm ${tab === t.id ? "border-b-2 border-foreground font-medium" : "text-muted-foreground"}`}>{t.label}</button>
78
+ ))}
79
+ </div>
80
+ {tab === "contacts" ? <Toolbar search={{ value: search, onChange: setSearch, placeholder: "Search contacts by name" }} /> : null}
81
+ {list.error ? <ApiError error={list.error} title="Could not load these records" onRetry={list.reload} /> : null}
82
+ {list.loading ? <Skeleton className="h-24 w-full" aria-label="Loading records" /> : null}
83
+ <ul className="divide-y rounded-md border" aria-label="CRM records">
84
+ {list.items.map((r, i) => {
85
+ const id = String(r.id ?? "");
86
+ return tab === "activity" ? (
87
+ <li key={id || i} className="px-3 py-2 text-sm">{String(r.subject ?? r.type ?? r.title ?? "Activity")}<span className="ml-2 text-xs text-muted-foreground">{formatWhen(String(r.timestamp ?? r.created_at ?? r.date ?? "") || null)}</span></li>
88
+ ) : (
89
+ <li key={id || i} className={open === id ? "bg-accent" : ""}>
90
+ <button type="button" className="block w-full px-3 py-2 text-left text-sm" onClick={() => setOpen(open === id ? null : id)} disabled={!id}>
91
+ <span className="block font-medium">{crmRecordTitle(r)}</span><span className="block text-xs text-muted-foreground">{crmRecordSubtitle(r)}</span>
92
+ </button>
93
+ </li>
94
+ );
95
+ })}
96
+ </ul>
97
+ {!list.loading && !list.error && list.items.length === 0 ? <EmptyState compact title={q ? "No contacts match" : "Nothing here"} description={tab === "activity" ? "The CRM has no activity for this connection yet." : "This CRM has no records of this kind."} /> : null}
98
+ <LoadMore list={list} noun="records" />
99
+ {open && tab !== "activity" ? <RecordFacts key={`${tab}:${open}`} connectionId={connectionId} entity={tab} id={open} /> : null}
100
+ {open ? <Button size="sm" variant="ghost" className="self-start" onClick={() => setOpen(null)}>Close record</Button> : null}
101
+ </div>
102
+ );
103
+ }
@@ -0,0 +1,61 @@
1
+ "use client";
2
+
3
+ /**
4
+ * FieldMappings: which fields exist on each side of the sync. The CRM's fields for a kind of record (contact, company, deal) next to the
5
+ * graph8 fields they can map to, each searchable. Read only: the mapping itself is chosen in graph8 (the API has no call to save one),
6
+ * so this view is for checking that a field you expect on the CRM side exists before you sync.
7
+ *
8
+ * SDK operations, in order:
9
+ * 1. discoverCrmFields GET /integrations/crm/fields?connection_id&entity_type (read) the CRM's fields
10
+ * 2. listMappableGraph8Fields GET /integrations/crm/fields/mappable?object_type (read) graph8 fields a CRM field can map to
11
+ */
12
+ import { useState } from "react";
13
+ import { Input, Skeleton } from "@graph8/sdk/ui";
14
+ import { ApiError } from "@/g8/components/api-error";
15
+ import { SectionCard } from "@/g8/components/screen/section-card";
16
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
17
+ import { g8Get } from "@/g8/lib/g8-client";
18
+ import { useBrand } from "@/g8/hooks/use-brand";
19
+ import { arrayOf } from "../lib/integrations-utils";
20
+
21
+ export interface FieldMappingsProps {
22
+ connectionId: string;
23
+ className?: string;
24
+ }
25
+
26
+ interface Field { name?: string; key?: string; label?: string; type?: string; field_type?: string }
27
+ // The API accepts only the plural object names (CrmFieldEntityType / Graph8FieldObjectType).
28
+ const KINDS = [{ id: "contacts", label: "Contacts" }, { id: "companies", label: "Companies" }, { id: "deals", label: "Deals" }];
29
+ const nameOf = (f: Field): string => f.label ?? f.name ?? f.key ?? "";
30
+
31
+ function FieldColumn({ title, query, filter }: { title: string; query: { data: unknown; error: unknown; loading: boolean; refetch: () => Promise<unknown> }; filter: string }) {
32
+ const rows = arrayOf<Field>(query.data).filter((f) => nameOf(f).toLowerCase().includes(filter.toLowerCase()));
33
+ return (
34
+ <SectionCard title={title} description={query.data ? `${rows.length} fields` : undefined} flush>
35
+ {query.error ? <div className="p-3"><ApiError error={query.error} title="Could not load the fields" onRetry={() => void query.refetch()} /></div> : null}
36
+ {query.loading && !query.data ? <Skeleton className="m-3 h-16" /> : null}
37
+ <ul className="max-h-80 divide-y overflow-y-auto text-sm">{rows.map((f, i) => <li key={`${nameOf(f)}-${i}`} className="flex justify-between gap-2 px-3 py-1.5"><span>{nameOf(f)}</span><span className="text-xs text-muted-foreground">{f.type ?? f.field_type ?? ""}</span></li>)}</ul>
38
+ {query.data && !query.error && rows.length === 0 ? <p className="px-3 py-2 text-sm text-muted-foreground">No fields match.</p> : null}
39
+ </SectionCard>
40
+ );
41
+ }
42
+
43
+ export function FieldMappings({ connectionId, className }: FieldMappingsProps) {
44
+ const brand = useBrand();
45
+ const [kind, setKind] = useState("contacts");
46
+ const [filter, setFilter] = useState("");
47
+ const crm = useG8Query((signal) => g8Get("/integrations/crm/fields", { connection_id: connectionId, entity_type: kind }, { signal }), [connectionId, kind]);
48
+ const mine = useG8Query((signal) => g8Get("/integrations/crm/fields/mappable", { object_type: kind }, { signal }), [kind]);
49
+ return (
50
+ <div className={`flex flex-col gap-3 ${className ?? ""}`}>
51
+ <div className="flex flex-wrap items-center gap-2">
52
+ <div role="group" aria-label="Record kind" className="flex gap-1">{KINDS.map((k) => <button key={k.id} type="button" aria-pressed={kind === k.id} onClick={() => setKind(k.id)} className={`rounded-md border px-3 py-1 text-sm ${kind === k.id ? "bg-foreground text-background" : ""}`}>{k.label}</button>)}</div>
53
+ <Input aria-label="Filter fields" className="h-8 max-w-xs" placeholder="Filter fields" value={filter} onChange={(e) => setFilter(e.target.value)} />
54
+ </div>
55
+ <div className="grid gap-4 md:grid-cols-2">
56
+ <FieldColumn title="In the CRM" query={crm} filter={filter} />
57
+ <FieldColumn title={`In ${brand.name}`} query={mine} filter={filter} />
58
+ </div>
59
+ </div>
60
+ );
61
+ }