@graph8/sdk 0.303.0 → 0.357.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 (664) hide show
  1. package/README.md +81 -0
  2. package/blocks/README.md +32 -0
  3. package/blocks/_foundation/README.md +78 -0
  4. package/blocks/_foundation/block.json +79 -0
  5. package/blocks/_foundation/components/api-error.tsx +62 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +142 -0
  7. package/blocks/_foundation/components/empty-state.tsx +24 -0
  8. package/blocks/_foundation/hooks/use-g8-mutation.ts +88 -0
  9. package/blocks/_foundation/hooks/use-g8-query.ts +87 -0
  10. package/blocks/_foundation/hooks/use-job-poll.ts +130 -0
  11. package/blocks/_foundation/hooks/use-paginated.ts +105 -0
  12. package/blocks/_foundation/lib/g8-client.ts +128 -0
  13. package/blocks/_foundation/recipes/verify-proxy-key.ts +18 -0
  14. package/blocks/_foundation/server/proxy.ts +233 -0
  15. package/blocks/_foundation/server/route.ts +30 -0
  16. package/blocks/agents/README.md +133 -0
  17. package/blocks/agents/block.json +223 -0
  18. package/blocks/agents/components/agent-builder.tsx +199 -0
  19. package/blocks/agents/components/agent-knowledge-panel.tsx +144 -0
  20. package/blocks/agents/components/agent-runs.tsx +175 -0
  21. package/blocks/agents/components/agent-skills-panel.tsx +126 -0
  22. package/blocks/agents/components/agent-test-console.tsx +175 -0
  23. package/blocks/agents/lib/agent-types.ts +114 -0
  24. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +50 -0
  25. package/blocks/agents/recipes/run-and-follow-agent-run.ts +46 -0
  26. package/blocks/agents/recipes/test-agent-before-activating.ts +36 -0
  27. package/blocks/analytics/README.md +73 -0
  28. package/blocks/analytics/block.json +178 -0
  29. package/blocks/analytics/components/funnel-chart.tsx +79 -0
  30. package/blocks/analytics/components/kpi-tiles.tsx +74 -0
  31. package/blocks/analytics/components/leaderboard.tsx +86 -0
  32. package/blocks/analytics/components/report-builder.tsx +200 -0
  33. package/blocks/analytics/hooks/use-report.ts +26 -0
  34. package/blocks/analytics/lib/analytics-types.ts +108 -0
  35. package/blocks/analytics/lib/use-debounced.ts +16 -0
  36. package/blocks/analytics/recipes/01-weekly-outbound-report.ts +32 -0
  37. package/blocks/analytics/recipes/02-revenue-snapshot.ts +22 -0
  38. package/blocks/analytics/recipes/03-saved-report.ts +30 -0
  39. package/blocks/appointments/README.md +80 -0
  40. package/blocks/appointments/block.json +218 -0
  41. package/blocks/appointments/components/availability-editor.tsx +192 -0
  42. package/blocks/appointments/components/booking-page.tsx +154 -0
  43. package/blocks/appointments/components/event-type-editor.tsx +214 -0
  44. package/blocks/appointments/components/meeting-detail.tsx +147 -0
  45. package/blocks/appointments/components/meeting-list.tsx +148 -0
  46. package/blocks/appointments/hooks/use-available-slots.ts +29 -0
  47. package/blocks/appointments/hooks/use-schedules.ts +18 -0
  48. package/blocks/appointments/lib/appointments-types.ts +159 -0
  49. package/blocks/appointments/lib/idempotency.ts +30 -0
  50. package/blocks/appointments/recipes/01-create-event-type-and-hours.ts +27 -0
  51. package/blocks/appointments/recipes/02-find-slots-and-book.ts +34 -0
  52. package/blocks/appointments/recipes/03-bookings-digest.ts +22 -0
  53. package/blocks/appointments/recipes/04-calendar-invite.ts +21 -0
  54. package/blocks/block.schema.json +130 -0
  55. package/blocks/block.types.ts +109 -0
  56. package/blocks/campaigns/README.md +118 -0
  57. package/blocks/campaigns/block.json +361 -0
  58. package/blocks/campaigns/components/campaign-assets.tsx +180 -0
  59. package/blocks/campaigns/components/campaign-audience-step.tsx +91 -0
  60. package/blocks/campaigns/components/campaign-launch-step.tsx +147 -0
  61. package/blocks/campaigns/components/campaign-list.tsx +120 -0
  62. package/blocks/campaigns/components/campaign-metrics.tsx +93 -0
  63. package/blocks/campaigns/components/campaign-select.tsx +52 -0
  64. package/blocks/campaigns/components/campaign-sequence-step.tsx +158 -0
  65. package/blocks/campaigns/components/campaign-wizard.tsx +238 -0
  66. package/blocks/campaigns/components/form-field.tsx +29 -0
  67. package/blocks/campaigns/components/icp-editor.tsx +230 -0
  68. package/blocks/campaigns/components/persona-editor.tsx +210 -0
  69. package/blocks/campaigns/hooks/use-company-profile.ts +34 -0
  70. package/blocks/campaigns/lib/campaigns-utils.ts +155 -0
  71. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +33 -0
  72. package/blocks/campaigns/recipes/campaign-report.ts +22 -0
  73. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +51 -0
  74. package/blocks/campaigns/recipes/set-up-icp-and-personas.ts +37 -0
  75. package/blocks/coach/README.md +93 -0
  76. package/blocks/coach/block.json +125 -0
  77. package/blocks/coach/components/call-scorecard.tsx +126 -0
  78. package/blocks/coach/components/coaching-feed.tsx +140 -0
  79. package/blocks/coach/lib/coach-types.ts +116 -0
  80. package/blocks/coach/recipes/read-a-scorecard.ts +27 -0
  81. package/blocks/coach/recipes/weekly-coaching-digest.ts +53 -0
  82. package/blocks/copilot/README.md +99 -0
  83. package/blocks/copilot/block.json +151 -0
  84. package/blocks/copilot/components/copilot-memory.tsx +87 -0
  85. package/blocks/copilot/components/copilot-panel.tsx +189 -0
  86. package/blocks/copilot/components/copilot-team-chat.tsx +111 -0
  87. package/blocks/copilot/lib/copilot-types.ts +111 -0
  88. package/blocks/copilot/recipes/ask-the-copilot.ts +35 -0
  89. package/blocks/copilot/recipes/post-in-a-work-channel.ts +33 -0
  90. package/blocks/copilot/recipes/remember-and-recall.ts +19 -0
  91. package/blocks/crm/README.md +122 -0
  92. package/blocks/crm/block.json +378 -0
  93. package/blocks/crm/components/activity-timeline.tsx +99 -0
  94. package/blocks/crm/components/company-detail.tsx +118 -0
  95. package/blocks/crm/components/company-table.tsx +173 -0
  96. package/blocks/crm/components/contact-detail.tsx +135 -0
  97. package/blocks/crm/components/contact-table.tsx +217 -0
  98. package/blocks/crm/components/dedupe-review.tsx +164 -0
  99. package/blocks/crm/components/import-wizard.tsx +193 -0
  100. package/blocks/crm/components/notes-panel.tsx +112 -0
  101. package/blocks/crm/components/record-form.tsx +191 -0
  102. package/blocks/crm/components/task-list.tsx +107 -0
  103. package/blocks/crm/hooks/use-custom-fields.ts +18 -0
  104. package/blocks/crm/hooks/use-debounced.ts +13 -0
  105. package/blocks/crm/hooks/use-page-query.ts +52 -0
  106. package/blocks/crm/lib/crm-types.ts +136 -0
  107. package/blocks/crm/recipes/custom-fields-and-values.ts +28 -0
  108. package/blocks/crm/recipes/dedupe-and-merge.ts +30 -0
  109. package/blocks/crm/recipes/notes-tasks-and-activity.ts +23 -0
  110. package/blocks/crm/recipes/upsert-contact-with-company.ts +33 -0
  111. package/blocks/deals/README.md +78 -0
  112. package/blocks/deals/block.json +198 -0
  113. package/blocks/deals/components/deal-detail.tsx +134 -0
  114. package/blocks/deals/components/forecast-view.tsx +112 -0
  115. package/blocks/deals/components/pipeline-board.tsx +142 -0
  116. package/blocks/deals/components/stage-editor.tsx +96 -0
  117. package/blocks/deals/hooks/use-pipeline-deals.ts +67 -0
  118. package/blocks/deals/hooks/use-pipelines.ts +19 -0
  119. package/blocks/deals/lib/deals-types.ts +128 -0
  120. package/blocks/deals/recipes/01-create-deal-and-advance.ts +32 -0
  121. package/blocks/deals/recipes/02-pipeline-report.ts +35 -0
  122. package/blocks/deals/recipes/03-stale-deals-sweep.ts +20 -0
  123. package/blocks/dialer/README.md +114 -0
  124. package/blocks/dialer/block.json +260 -0
  125. package/blocks/dialer/components/call-log.tsx +78 -0
  126. package/blocks/dialer/components/call-queue.tsx +188 -0
  127. package/blocks/dialer/components/dialer-workspace.tsx +47 -0
  128. package/blocks/dialer/components/disposition-panel.tsx +75 -0
  129. package/blocks/dialer/components/live-calls-table.tsx +107 -0
  130. package/blocks/dialer/components/session-start-form.tsx +111 -0
  131. package/blocks/dialer/components/softphone.tsx +122 -0
  132. package/blocks/dialer/components/transcript-viewer.tsx +82 -0
  133. package/blocks/dialer/hooks/use-dialer-numbers.ts +19 -0
  134. package/blocks/dialer/hooks/use-dialer-session.ts +68 -0
  135. package/blocks/dialer/hooks/use-dispositions.ts +29 -0
  136. package/blocks/dialer/lib/dialer-types.ts +158 -0
  137. package/blocks/dialer/lib/softphone-transport.ts +67 -0
  138. package/blocks/dialer/lib/twilio-transport.ts +74 -0
  139. package/blocks/dialer/recipes/01-create-session-and-dial.ts +39 -0
  140. package/blocks/dialer/recipes/02-disposition-calls.ts +26 -0
  141. package/blocks/dialer/recipes/03-review-a-call.ts +23 -0
  142. package/blocks/enrichment/README.md +105 -0
  143. package/blocks/enrichment/block.json +204 -0
  144. package/blocks/enrichment/components/bulk-enrich-panel.tsx +205 -0
  145. package/blocks/enrichment/components/email-verify-badge.tsx +82 -0
  146. package/blocks/enrichment/components/enrich-button.tsx +136 -0
  147. package/blocks/enrichment/components/waterfall-config.tsx +127 -0
  148. package/blocks/enrichment/hooks/use-providers.ts +18 -0
  149. package/blocks/enrichment/lib/enrichment-types.ts +104 -0
  150. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +41 -0
  151. package/blocks/enrichment/recipes/lookup-and-verify.ts +25 -0
  152. package/blocks/enrichment/recipes/waterfall-pipeline.ts +26 -0
  153. package/blocks/forms/README.md +112 -0
  154. package/blocks/forms/block.json +200 -0
  155. package/blocks/forms/components/form-builder.tsx +150 -0
  156. package/blocks/forms/components/form-embed.tsx +103 -0
  157. package/blocks/forms/components/form-mapping-editor.tsx +125 -0
  158. package/blocks/forms/components/site-audit-panel.tsx +73 -0
  159. package/blocks/forms/components/submissions-table.tsx +158 -0
  160. package/blocks/forms/lib/form-types.ts +127 -0
  161. package/blocks/forms/recipes/install-snippet-and-form.ts +21 -0
  162. package/blocks/forms/recipes/retry-failed-deliveries.ts +26 -0
  163. package/blocks/forms/recipes/route-form-to-list.ts +44 -0
  164. package/blocks/inbox/README.md +72 -0
  165. package/blocks/inbox/block.json +194 -0
  166. package/blocks/inbox/components/inbox-list.tsx +156 -0
  167. package/blocks/inbox/components/inbox-screen.tsx +49 -0
  168. package/blocks/inbox/components/reply-composer.tsx +183 -0
  169. package/blocks/inbox/components/tag-and-assign.tsx +189 -0
  170. package/blocks/inbox/components/thread-badge.tsx +17 -0
  171. package/blocks/inbox/components/thread-view.tsx +108 -0
  172. package/blocks/inbox/hooks/use-inbox-thread.ts +24 -0
  173. package/blocks/inbox/hooks/use-inbox-threads.ts +40 -0
  174. package/blocks/inbox/lib/idempotency.ts +30 -0
  175. package/blocks/inbox/lib/inbox-types.ts +100 -0
  176. package/blocks/inbox/recipes/01-triage-and-reply.ts +43 -0
  177. package/blocks/inbox/recipes/02-tag-and-assign.ts +31 -0
  178. package/blocks/inbox/recipes/03-hot-contacts-digest.ts +16 -0
  179. package/blocks/integrations/README.md +75 -0
  180. package/blocks/integrations/block.json +172 -0
  181. package/blocks/integrations/components/integration-list.tsx +165 -0
  182. package/blocks/integrations/components/sync-status.tsx +223 -0
  183. package/blocks/integrations/components/webhook-manager.tsx +300 -0
  184. package/blocks/integrations/lib/integrations-utils.ts +62 -0
  185. package/blocks/integrations/recipes/crm-sync-health.ts +19 -0
  186. package/blocks/integrations/recipes/push-contacts-to-crm.ts +20 -0
  187. package/blocks/integrations/recipes/register-webhook.ts +18 -0
  188. package/blocks/integrations/recipes/verify-webhook-route.ts +33 -0
  189. package/blocks/intent/README.md +57 -0
  190. package/blocks/intent/block.json +138 -0
  191. package/blocks/intent/components/account-intent-score.tsx +91 -0
  192. package/blocks/intent/components/intent-feed.tsx +215 -0
  193. package/blocks/intent/components/intent-keyword-manager.tsx +331 -0
  194. package/blocks/intent/hooks/use-account-intent.ts +46 -0
  195. package/blocks/intent/lib/intent-utils.ts +95 -0
  196. package/blocks/intent/recipes/rank-intent-accounts.ts +36 -0
  197. package/blocks/intent/recipes/track-competitor-domain.ts +23 -0
  198. package/blocks/intent/recipes/who-visited-url.ts +18 -0
  199. package/blocks/lifecycle/README.md +79 -0
  200. package/blocks/lifecycle/block.json +198 -0
  201. package/blocks/lifecycle/components/churn-list.tsx +92 -0
  202. package/blocks/lifecycle/components/customer-table.tsx +119 -0
  203. package/blocks/lifecycle/components/lead-queue.tsx +127 -0
  204. package/blocks/lifecycle/components/lifecycle-board.tsx +102 -0
  205. package/blocks/lifecycle/components/stage-move-dialog.tsx +75 -0
  206. package/blocks/lifecycle/hooks/use-debounced.ts +13 -0
  207. package/blocks/lifecycle/hooks/use-lifecycle-stages.ts +27 -0
  208. package/blocks/lifecycle/lib/lifecycle.ts +99 -0
  209. package/blocks/lifecycle/recipes/customer-brief.ts +23 -0
  210. package/blocks/lifecycle/recipes/move-company-stage.ts +17 -0
  211. package/blocks/lifecycle/recipes/read-lifecycle-board.ts +21 -0
  212. package/blocks/lifecycle/recipes/unlock-stage-contacts.ts +25 -0
  213. package/blocks/lists/README.md +96 -0
  214. package/blocks/lists/block.json +166 -0
  215. package/blocks/lists/components/add-to-list-button.tsx +111 -0
  216. package/blocks/lists/components/list-picker.tsx +94 -0
  217. package/blocks/lists/components/list-table.tsx +160 -0
  218. package/blocks/lists/components/suppression-manager.tsx +153 -0
  219. package/blocks/lists/recipes/create-list-and-add-contacts.ts +35 -0
  220. package/blocks/lists/recipes/refresh-and-check-lists.ts +21 -0
  221. package/blocks/lists/recipes/suppress-and-reinstate.ts +30 -0
  222. package/blocks/mailboxes/README.md +71 -0
  223. package/blocks/mailboxes/block.json +187 -0
  224. package/blocks/mailboxes/components/connect-mailbox.tsx +284 -0
  225. package/blocks/mailboxes/components/domain-purchase.tsx +271 -0
  226. package/blocks/mailboxes/components/mailbox-list.tsx +185 -0
  227. package/blocks/mailboxes/components/warmup-health.tsx +232 -0
  228. package/blocks/mailboxes/lib/mailboxes-utils.ts +69 -0
  229. package/blocks/mailboxes/recipes/buy-domain-and-mailboxes.ts +64 -0
  230. package/blocks/mailboxes/recipes/connect-imap-mailbox.ts +38 -0
  231. package/blocks/mailboxes/recipes/is-it-safe-to-send.ts +17 -0
  232. package/blocks/mailboxes/recipes/warmup-report.ts +31 -0
  233. package/blocks/newsletter/README.md +122 -0
  234. package/blocks/newsletter/block.json +224 -0
  235. package/blocks/newsletter/components/issue-form.tsx +124 -0
  236. package/blocks/newsletter/components/issue-scheduler.tsx +152 -0
  237. package/blocks/newsletter/components/newsletter-editor.tsx +131 -0
  238. package/blocks/newsletter/components/newsletter-stats.tsx +93 -0
  239. package/blocks/newsletter/components/subscriber-table.tsx +173 -0
  240. package/blocks/newsletter/lib/newsletter-types.ts +97 -0
  241. package/blocks/newsletter/recipes/create-newsletter-and-draft-issue.ts +27 -0
  242. package/blocks/newsletter/recipes/import-audience-and-send.ts +43 -0
  243. package/blocks/newsletter/recipes/issue-performance-report.ts +26 -0
  244. package/blocks/nurture/README.md +110 -0
  245. package/blocks/nurture/block.json +191 -0
  246. package/blocks/nurture/components/nurture-builder.tsx +114 -0
  247. package/blocks/nurture/components/nurture-enroll.tsx +107 -0
  248. package/blocks/nurture/components/nurture-form.tsx +120 -0
  249. package/blocks/nurture/components/nurture-stats.tsx +81 -0
  250. package/blocks/nurture/lib/nurture-types.ts +109 -0
  251. package/blocks/nurture/recipes/create-a-nurture.ts +34 -0
  252. package/blocks/nurture/recipes/enroll-contacts-and-launch.ts +24 -0
  253. package/blocks/nurture/recipes/nurture-health-check.ts +28 -0
  254. package/blocks/objects/README.md +120 -0
  255. package/blocks/objects/block.json +243 -0
  256. package/blocks/objects/components/attribute-field-input.tsx +82 -0
  257. package/blocks/objects/components/attribute-form-dialog.tsx +159 -0
  258. package/blocks/objects/components/object-not-found.tsx +34 -0
  259. package/blocks/objects/components/object-record-detail.tsx +143 -0
  260. package/blocks/objects/components/object-schema-editor.tsx +197 -0
  261. package/blocks/objects/components/object-table.tsx +130 -0
  262. package/blocks/objects/components/record-form-dialog.tsx +112 -0
  263. package/blocks/objects/components/record-roles-panel.tsx +106 -0
  264. package/blocks/objects/hooks/use-object-schema.ts +36 -0
  265. package/blocks/objects/lib/object-types.ts +214 -0
  266. package/blocks/objects/recipes/define-object-and-add-records.ts +24 -0
  267. package/blocks/objects/recipes/update-audit-and-archive-records.ts +39 -0
  268. package/blocks/objects/recipes/upsert-records-from-your-system.ts +30 -0
  269. package/blocks/pages/README.md +104 -0
  270. package/blocks/pages/block.json +223 -0
  271. package/blocks/pages/components/brand-kit-editor.tsx +152 -0
  272. package/blocks/pages/components/content-library.tsx +163 -0
  273. package/blocks/pages/components/page-editor.tsx +253 -0
  274. package/blocks/pages/components/page-list.tsx +159 -0
  275. package/blocks/pages/components/seo-report.tsx +159 -0
  276. package/blocks/pages/lib/studio.ts +77 -0
  277. package/blocks/pages/recipes/audit-site-seo.ts +28 -0
  278. package/blocks/pages/recipes/create-and-publish-page.ts +26 -0
  279. package/blocks/pages/recipes/edit-page-section.ts +25 -0
  280. package/blocks/pages/recipes/refresh-brand-kit.ts +22 -0
  281. package/blocks/quotes/README.md +79 -0
  282. package/blocks/quotes/block.json +183 -0
  283. package/blocks/quotes/components/product-picker.tsx +80 -0
  284. package/blocks/quotes/components/quote-builder.tsx +195 -0
  285. package/blocks/quotes/components/quote-list.tsx +79 -0
  286. package/blocks/quotes/components/quote-viewer.tsx +135 -0
  287. package/blocks/quotes/components/signature-status.tsx +77 -0
  288. package/blocks/quotes/hooks/use-debounced.ts +13 -0
  289. package/blocks/quotes/lib/quotes.ts +142 -0
  290. package/blocks/quotes/recipes/create-and-send-quote.ts +44 -0
  291. package/blocks/quotes/recipes/revise-sent-quote.ts +18 -0
  292. package/blocks/quotes/recipes/track-quote-signature.ts +22 -0
  293. package/blocks/radar/README.md +106 -0
  294. package/blocks/radar/block.json +189 -0
  295. package/blocks/radar/components/competitor-battle-card.tsx +53 -0
  296. package/blocks/radar/components/competitor-tracker.tsx +158 -0
  297. package/blocks/radar/components/radar-overview.tsx +67 -0
  298. package/blocks/radar/components/radar-report.tsx +86 -0
  299. package/blocks/radar/hooks/use-competitors.ts +23 -0
  300. package/blocks/radar/lib/radar-types.ts +124 -0
  301. package/blocks/radar/recipes/battle-card.ts +28 -0
  302. package/blocks/radar/recipes/deep-dive-report.ts +32 -0
  303. package/blocks/radar/recipes/marketing-snapshot.ts +18 -0
  304. package/blocks/radar/recipes/track-and-scan.ts +39 -0
  305. package/blocks/search/README.md +129 -0
  306. package/blocks/search/block.json +185 -0
  307. package/blocks/search/components/filter-builder.tsx +149 -0
  308. package/blocks/search/components/prospect-results.tsx +175 -0
  309. package/blocks/search/components/save-to-list-dialog.tsx +135 -0
  310. package/blocks/search/components/saved-searches.tsx +95 -0
  311. package/blocks/search/hooks/use-filter-options.ts +41 -0
  312. package/blocks/search/lib/search-types.ts +149 -0
  313. package/blocks/search/recipes/explore-filter-values.ts +27 -0
  314. package/blocks/search/recipes/saved-searches.ts +34 -0
  315. package/blocks/search/recipes/search-and-save-to-list.ts +37 -0
  316. package/blocks/sequencer/README.md +87 -0
  317. package/blocks/sequencer/block.json +214 -0
  318. package/blocks/sequencer/components/enroll-dialog.tsx +93 -0
  319. package/blocks/sequencer/components/sequence-builder.tsx +306 -0
  320. package/blocks/sequencer/components/sequence-list.tsx +200 -0
  321. package/blocks/sequencer/components/sequence-stats.tsx +256 -0
  322. package/blocks/sequencer/components/step-editor.tsx +213 -0
  323. package/blocks/sequencer/lib/sequencer-utils.ts +168 -0
  324. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +51 -0
  325. package/blocks/sequencer/recipes/launch-and-enroll.ts +44 -0
  326. package/blocks/sequencer/recipes/pause-when-unsafe.ts +30 -0
  327. package/blocks/sequencer/recipes/sequence-report.ts +22 -0
  328. package/blocks/social-ads/README.md +128 -0
  329. package/blocks/social-ads/block.json +256 -0
  330. package/blocks/social-ads/components/ad-campaign-list.tsx +136 -0
  331. package/blocks/social-ads/components/audience-sync.tsx +150 -0
  332. package/blocks/social-ads/components/creative-gallery.tsx +121 -0
  333. package/blocks/social-ads/components/post-composer.tsx +122 -0
  334. package/blocks/social-ads/components/social-post-list.tsx +116 -0
  335. package/blocks/social-ads/hooks/use-social-accounts.ts +16 -0
  336. package/blocks/social-ads/lib/social-ads-types.ts +167 -0
  337. package/blocks/social-ads/recipes/creative-to-gallery.ts +41 -0
  338. package/blocks/social-ads/recipes/schedule-post.ts +51 -0
  339. package/blocks/social-ads/recipes/sync-audience-and-launch.ts +41 -0
  340. package/blocks/visitors/README.md +56 -0
  341. package/blocks/visitors/block.json +139 -0
  342. package/blocks/visitors/components/snippet-install.tsx +169 -0
  343. package/blocks/visitors/components/traffic-chart.tsx +156 -0
  344. package/blocks/visitors/components/visitor-company-card.tsx +92 -0
  345. package/blocks/visitors/components/visitor-feed.tsx +157 -0
  346. package/blocks/visitors/lib/visitors-utils.ts +78 -0
  347. package/blocks/visitors/recipes/check-snippet-installed.ts +24 -0
  348. package/blocks/visitors/recipes/recent-company-visits.ts +27 -0
  349. package/blocks/visitors/recipes/traffic-summary.ts +24 -0
  350. package/blocks/webchat/README.md +87 -0
  351. package/blocks/webchat/block.json +134 -0
  352. package/blocks/webchat/components/chat-widget-config.tsx +160 -0
  353. package/blocks/webchat/components/conversation-list.tsx +74 -0
  354. package/blocks/webchat/lib/webchat-types.ts +83 -0
  355. package/blocks/webchat/recipes/create-bot-and-style-widget.ts +24 -0
  356. package/blocks/webchat/recipes/list-chat-visitors.ts +24 -0
  357. package/blocks/workbench/README.md +135 -0
  358. package/blocks/workbench/block.json +259 -0
  359. package/blocks/workbench/components/column-recipe-editor.tsx +183 -0
  360. package/blocks/workbench/components/run-progress.tsx +132 -0
  361. package/blocks/workbench/components/workbench-add-rows.tsx +100 -0
  362. package/blocks/workbench/components/workbench-grid.tsx +159 -0
  363. package/blocks/workbench/components/workbench-row-actions.tsx +102 -0
  364. package/blocks/workbench/components/workbench-table-list.tsx +105 -0
  365. package/blocks/workbench/hooks/use-workbench-table.ts +30 -0
  366. package/blocks/workbench/lib/waterfall-presets.ts +119 -0
  367. package/blocks/workbench/lib/workbench-types.ts +191 -0
  368. package/blocks/workbench/recipes/build-enriched-table.ts +65 -0
  369. package/blocks/workbench/recipes/import-from-search.ts +42 -0
  370. package/blocks/workbench/recipes/scrape-into-table.ts +36 -0
  371. package/blocks/workbench/recipes/waterfall-column.ts +55 -0
  372. package/blocks/workflows/README.md +100 -0
  373. package/blocks/workflows/block.json +188 -0
  374. package/blocks/workflows/components/run-history.tsx +136 -0
  375. package/blocks/workflows/components/trigger-config.tsx +134 -0
  376. package/blocks/workflows/components/workflow-editor.tsx +142 -0
  377. package/blocks/workflows/components/workflow-list.tsx +153 -0
  378. package/blocks/workflows/hooks/use-workflow.ts +51 -0
  379. package/blocks/workflows/lib/graph.ts +187 -0
  380. package/blocks/workflows/recipes/create-workflow.ts +35 -0
  381. package/blocks/workflows/recipes/find-failed-runs.ts +15 -0
  382. package/blocks/workflows/recipes/run-workflow-and-wait.ts +32 -0
  383. package/blocks/workflows/recipes/set-trigger.ts +22 -0
  384. package/dist/cli.js +5330 -29
  385. package/dist/index.browser.js +7740 -0
  386. package/dist/index.browser.js.map +1 -0
  387. package/dist/index.browser.mjs +7717 -0
  388. package/dist/index.browser.mjs.map +1 -0
  389. package/dist/index.d.mts +2220 -23
  390. package/dist/index.d.ts +2220 -23
  391. package/dist/index.js +64 -3
  392. package/dist/index.js.map +1 -1
  393. package/dist/index.mjs +64 -3
  394. package/dist/index.mjs.map +1 -1
  395. package/dist/react.browser.js +7598 -0
  396. package/dist/react.browser.js.map +1 -0
  397. package/dist/react.browser.mjs +7568 -0
  398. package/dist/react.browser.mjs.map +1 -0
  399. package/dist/react.d.mts +2082 -20
  400. package/dist/react.d.ts +2082 -20
  401. package/dist/react.js +63 -2
  402. package/dist/react.js.map +1 -1
  403. package/dist/react.mjs +63 -2
  404. package/dist/react.mjs.map +1 -1
  405. package/dist/types/agentSetup.d.ts +6 -6
  406. package/dist/types/blocks/add.d.ts +53 -0
  407. package/dist/types/blocks/create.d.ts +46 -0
  408. package/dist/types/blocks/doctor.d.ts +26 -0
  409. package/dist/types/blocks/registry.d.ts +134 -0
  410. package/dist/types/cli.d.ts +16 -0
  411. package/dist/types/core.d.ts +1 -1
  412. package/dist/types/crypto-browser.d.ts +2 -0
  413. package/dist/types/crypto-node.d.ts +6 -0
  414. package/dist/types/react.d.ts +1 -1
  415. package/dist/types/version.d.ts +2 -2
  416. package/dist/types/webhooks.d.ts +2 -2
  417. package/dist/ui/index.d.mts +54 -0
  418. package/dist/ui/index.d.ts +54 -0
  419. package/dist/ui/index.js +105 -0
  420. package/dist/ui/index.js.map +1 -0
  421. package/dist/ui/index.mjs +44 -0
  422. package/dist/ui/index.mjs.map +1 -0
  423. package/dist/ui/lib/compose-refs.d.mts +19 -0
  424. package/dist/ui/lib/compose-refs.d.ts +19 -0
  425. package/dist/ui/lib/compose-refs.js +76 -0
  426. package/dist/ui/lib/compose-refs.js.map +1 -0
  427. package/dist/ui/lib/compose-refs.mjs +41 -0
  428. package/dist/ui/lib/compose-refs.mjs.map +1 -0
  429. package/dist/ui/lib/use-mobile.d.mts +4 -0
  430. package/dist/ui/lib/use-mobile.d.ts +4 -0
  431. package/dist/ui/lib/use-mobile.js +57 -0
  432. package/dist/ui/lib/use-mobile.js.map +1 -0
  433. package/dist/ui/lib/use-mobile.mjs +22 -0
  434. package/dist/ui/lib/use-mobile.mjs.map +1 -0
  435. package/dist/ui/lib/utils.d.mts +10 -0
  436. package/dist/ui/lib/utils.d.ts +10 -0
  437. package/dist/ui/lib/utils.js +33 -0
  438. package/dist/ui/lib/utils.js.map +1 -0
  439. package/dist/ui/lib/utils.mjs +9 -0
  440. package/dist/ui/lib/utils.mjs.map +1 -0
  441. package/dist/ui/primitives/alert-dialog.d.mts +22 -0
  442. package/dist/ui/primitives/alert-dialog.d.ts +22 -0
  443. package/dist/ui/primitives/alert-dialog.js +163 -0
  444. package/dist/ui/primitives/alert-dialog.js.map +1 -0
  445. package/dist/ui/primitives/alert-dialog.mjs +119 -0
  446. package/dist/ui/primitives/alert-dialog.mjs.map +1 -0
  447. package/dist/ui/primitives/avatar.d.mts +8 -0
  448. package/dist/ui/primitives/avatar.d.ts +8 -0
  449. package/dist/ui/primitives/avatar.js +91 -0
  450. package/dist/ui/primitives/avatar.js.map +1 -0
  451. package/dist/ui/primitives/avatar.mjs +55 -0
  452. package/dist/ui/primitives/avatar.mjs.map +1 -0
  453. package/dist/ui/primitives/breadcrumb.d.mts +13 -0
  454. package/dist/ui/primitives/breadcrumb.d.ts +13 -0
  455. package/dist/ui/primitives/breadcrumb.js +127 -0
  456. package/dist/ui/primitives/breadcrumb.js.map +1 -0
  457. package/dist/ui/primitives/breadcrumb.mjs +97 -0
  458. package/dist/ui/primitives/breadcrumb.mjs.map +1 -0
  459. package/dist/ui/primitives/button.d.mts +15 -0
  460. package/dist/ui/primitives/button.d.ts +15 -0
  461. package/dist/ui/primitives/button.js +91 -0
  462. package/dist/ui/primitives/button.js.map +1 -0
  463. package/dist/ui/primitives/button.mjs +56 -0
  464. package/dist/ui/primitives/button.mjs.map +1 -0
  465. package/dist/ui/primitives/card.d.mts +10 -0
  466. package/dist/ui/primitives/card.d.ts +10 -0
  467. package/dist/ui/primitives/card.js +101 -0
  468. package/dist/ui/primitives/card.js.map +1 -0
  469. package/dist/ui/primitives/card.mjs +62 -0
  470. package/dist/ui/primitives/card.mjs.map +1 -0
  471. package/dist/ui/primitives/carousel.d.mts +45 -0
  472. package/dist/ui/primitives/carousel.d.ts +45 -0
  473. package/dist/ui/primitives/carousel.js +300 -0
  474. package/dist/ui/primitives/carousel.js.map +1 -0
  475. package/dist/ui/primitives/carousel.mjs +261 -0
  476. package/dist/ui/primitives/carousel.mjs.map +1 -0
  477. package/dist/ui/primitives/chart.d.mts +68 -0
  478. package/dist/ui/primitives/chart.d.ts +68 -0
  479. package/dist/ui/primitives/chart.js +280 -0
  480. package/dist/ui/primitives/chart.js.map +1 -0
  481. package/dist/ui/primitives/chart.mjs +241 -0
  482. package/dist/ui/primitives/chart.mjs.map +1 -0
  483. package/dist/ui/primitives/collapsible.d.mts +8 -0
  484. package/dist/ui/primitives/collapsible.d.ts +8 -0
  485. package/dist/ui/primitives/collapsible.js +72 -0
  486. package/dist/ui/primitives/collapsible.js.map +1 -0
  487. package/dist/ui/primitives/collapsible.mjs +36 -0
  488. package/dist/ui/primitives/collapsible.mjs.map +1 -0
  489. package/dist/ui/primitives/context-menu.d.mts +29 -0
  490. package/dist/ui/primitives/context-menu.d.ts +29 -0
  491. package/dist/ui/primitives/context-menu.js +203 -0
  492. package/dist/ui/primitives/context-menu.js.map +1 -0
  493. package/dist/ui/primitives/context-menu.mjs +155 -0
  494. package/dist/ui/primitives/context-menu.mjs.map +1 -0
  495. package/dist/ui/primitives/dialog.d.mts +26 -0
  496. package/dist/ui/primitives/dialog.d.ts +26 -0
  497. package/dist/ui/primitives/dialog.js +192 -0
  498. package/dist/ui/primitives/dialog.js.map +1 -0
  499. package/dist/ui/primitives/dialog.mjs +148 -0
  500. package/dist/ui/primitives/dialog.mjs.map +1 -0
  501. package/dist/ui/primitives/drawer.d.mts +26 -0
  502. package/dist/ui/primitives/drawer.d.ts +26 -0
  503. package/dist/ui/primitives/drawer.js +146 -0
  504. package/dist/ui/primitives/drawer.js.map +1 -0
  505. package/dist/ui/primitives/drawer.mjs +103 -0
  506. package/dist/ui/primitives/drawer.mjs.map +1 -0
  507. package/dist/ui/primitives/input.d.mts +5 -0
  508. package/dist/ui/primitives/input.d.ts +5 -0
  509. package/dist/ui/primitives/input.js +46 -0
  510. package/dist/ui/primitives/input.js.map +1 -0
  511. package/dist/ui/primitives/input.mjs +22 -0
  512. package/dist/ui/primitives/input.mjs.map +1 -0
  513. package/dist/ui/primitives/item.d.mts +27 -0
  514. package/dist/ui/primitives/item.d.ts +27 -0
  515. package/dist/ui/primitives/item.js +221 -0
  516. package/dist/ui/primitives/item.js.map +1 -0
  517. package/dist/ui/primitives/item.mjs +188 -0
  518. package/dist/ui/primitives/item.mjs.map +1 -0
  519. package/dist/ui/primitives/kbd.d.mts +15 -0
  520. package/dist/ui/primitives/kbd.d.ts +15 -0
  521. package/dist/ui/primitives/kbd.js +45 -0
  522. package/dist/ui/primitives/kbd.js.map +1 -0
  523. package/dist/ui/primitives/kbd.mjs +21 -0
  524. package/dist/ui/primitives/kbd.mjs.map +1 -0
  525. package/dist/ui/primitives/label.d.mts +6 -0
  526. package/dist/ui/primitives/label.d.ts +6 -0
  527. package/dist/ui/primitives/label.js +55 -0
  528. package/dist/ui/primitives/label.js.map +1 -0
  529. package/dist/ui/primitives/label.mjs +21 -0
  530. package/dist/ui/primitives/label.mjs.map +1 -0
  531. package/dist/ui/primitives/resizable.d.mts +10 -0
  532. package/dist/ui/primitives/resizable.d.ts +10 -0
  533. package/dist/ui/primitives/resizable.js +68 -0
  534. package/dist/ui/primitives/resizable.js.map +1 -0
  535. package/dist/ui/primitives/resizable.mjs +42 -0
  536. package/dist/ui/primitives/resizable.mjs.map +1 -0
  537. package/dist/ui/primitives/save-status.d.mts +31 -0
  538. package/dist/ui/primitives/save-status.d.ts +31 -0
  539. package/dist/ui/primitives/save-status.js +150 -0
  540. package/dist/ui/primitives/save-status.js.map +1 -0
  541. package/dist/ui/primitives/save-status.mjs +126 -0
  542. package/dist/ui/primitives/save-status.mjs.map +1 -0
  543. package/dist/ui/primitives/separator.d.mts +6 -0
  544. package/dist/ui/primitives/separator.d.ts +6 -0
  545. package/dist/ui/primitives/separator.js +62 -0
  546. package/dist/ui/primitives/separator.js.map +1 -0
  547. package/dist/ui/primitives/separator.mjs +28 -0
  548. package/dist/ui/primitives/separator.mjs.map +1 -0
  549. package/dist/ui/primitives/sheet.d.mts +16 -0
  550. package/dist/ui/primitives/sheet.d.ts +16 -0
  551. package/dist/ui/primitives/sheet.js +170 -0
  552. package/dist/ui/primitives/sheet.js.map +1 -0
  553. package/dist/ui/primitives/sheet.mjs +129 -0
  554. package/dist/ui/primitives/sheet.mjs.map +1 -0
  555. package/dist/ui/primitives/sidebar.d.mts +74 -0
  556. package/dist/ui/primitives/sidebar.d.ts +74 -0
  557. package/dist/ui/primitives/sidebar.js +697 -0
  558. package/dist/ui/primitives/sidebar.js.map +1 -0
  559. package/dist/ui/primitives/sidebar.mjs +651 -0
  560. package/dist/ui/primitives/sidebar.mjs.map +1 -0
  561. package/dist/ui/primitives/skeleton.d.mts +5 -0
  562. package/dist/ui/primitives/skeleton.d.ts +5 -0
  563. package/dist/ui/primitives/skeleton.js +40 -0
  564. package/dist/ui/primitives/skeleton.js.map +1 -0
  565. package/dist/ui/primitives/skeleton.mjs +16 -0
  566. package/dist/ui/primitives/skeleton.mjs.map +1 -0
  567. package/dist/ui/primitives/slider.d.mts +20 -0
  568. package/dist/ui/primitives/slider.d.ts +20 -0
  569. package/dist/ui/primitives/slider.js +112 -0
  570. package/dist/ui/primitives/slider.js.map +1 -0
  571. package/dist/ui/primitives/slider.mjs +78 -0
  572. package/dist/ui/primitives/slider.mjs.map +1 -0
  573. package/dist/ui/primitives/sortable.d.mts +50 -0
  574. package/dist/ui/primitives/sortable.d.ts +50 -0
  575. package/dist/ui/primitives/sortable.js +456 -0
  576. package/dist/ui/primitives/sortable.js.map +1 -0
  577. package/dist/ui/primitives/sortable.mjs +435 -0
  578. package/dist/ui/primitives/sortable.mjs.map +1 -0
  579. package/dist/ui/primitives/switch.d.mts +6 -0
  580. package/dist/ui/primitives/switch.d.ts +6 -0
  581. package/dist/ui/primitives/switch.js +67 -0
  582. package/dist/ui/primitives/switch.js.map +1 -0
  583. package/dist/ui/primitives/switch.mjs +33 -0
  584. package/dist/ui/primitives/switch.mjs.map +1 -0
  585. package/dist/ui/primitives/table.d.mts +12 -0
  586. package/dist/ui/primitives/table.d.ts +12 -0
  587. package/dist/ui/primitives/table.js +130 -0
  588. package/dist/ui/primitives/table.js.map +1 -0
  589. package/dist/ui/primitives/table.mjs +89 -0
  590. package/dist/ui/primitives/table.mjs.map +1 -0
  591. package/dist/ui/primitives/tabs.d.mts +9 -0
  592. package/dist/ui/primitives/tabs.d.ts +9 -0
  593. package/dist/ui/primitives/tabs.js +86 -0
  594. package/dist/ui/primitives/tabs.js.map +1 -0
  595. package/dist/ui/primitives/tabs.mjs +49 -0
  596. package/dist/ui/primitives/tabs.mjs.map +1 -0
  597. package/dist/ui/primitives/textarea.d.mts +5 -0
  598. package/dist/ui/primitives/textarea.d.ts +5 -0
  599. package/dist/ui/primitives/textarea.js +43 -0
  600. package/dist/ui/primitives/textarea.js.map +1 -0
  601. package/dist/ui/primitives/textarea.mjs +19 -0
  602. package/dist/ui/primitives/textarea.mjs.map +1 -0
  603. package/dist/ui/primitives/tooltip.d.mts +9 -0
  604. package/dist/ui/primitives/tooltip.d.ts +9 -0
  605. package/dist/ui/primitives/tooltip.js +94 -0
  606. package/dist/ui/primitives/tooltip.js.map +1 -0
  607. package/dist/ui/primitives/tooltip.mjs +57 -0
  608. package/dist/ui/primitives/tooltip.mjs.map +1 -0
  609. package/docs/agents.md +72 -29
  610. package/docs/api/README.md +7 -3
  611. package/docs/api/appObjects.d.ts +3 -1
  612. package/docs/api/appObjects.md +2 -1
  613. package/docs/api/by-path.md +51 -0
  614. package/docs/api/dataPipelines.d.ts +149 -0
  615. package/docs/api/dataPipelines.md +232 -0
  616. package/docs/api/forms.d.ts +16 -0
  617. package/docs/api/forms.md +8 -0
  618. package/docs/api/objects.d.ts +705 -5
  619. package/docs/api/objects.md +1099 -14
  620. package/docs/api/ops.d.ts +1 -1
  621. package/docs/api/ops.md +1 -1
  622. package/docs/api/processLists.d.ts +247 -0
  623. package/docs/api/processLists.md +409 -0
  624. package/docs/api/quotes.d.ts +3 -1
  625. package/docs/api/quotes.md +7 -2
  626. package/docs/api/radar.d.ts +4 -0
  627. package/docs/api/radar.md +1 -0
  628. package/docs/api/reports.d.ts +476 -0
  629. package/docs/api/reports.md +539 -0
  630. package/docs/api/scheduleBuy.d.ts +12 -8
  631. package/docs/api/scheduleBuy.md +13 -10
  632. package/docs/api/seo.d.ts +406 -3
  633. package/docs/api/seo.md +505 -1
  634. package/docs/api/workspaceBackup.d.ts +324 -0
  635. package/docs/api/workspaceBackup.md +427 -0
  636. package/package.json +174 -5
  637. package/stacks/6sense.json +46 -0
  638. package/stacks/README.md +20 -0
  639. package/stacks/apollo.json +128 -0
  640. package/stacks/clay.json +59 -0
  641. package/stacks/gong.json +72 -0
  642. package/stacks/hubspot.json +162 -0
  643. package/stacks/intercom.json +53 -0
  644. package/stacks/mailchimp.json +71 -0
  645. package/stacks/outreach.json +86 -0
  646. package/stacks/salesforce.json +94 -0
  647. package/stacks/stack.schema.json +32 -0
  648. package/stacks/starter.json +22 -0
  649. package/styles/styles.css +2 -0
  650. package/styles/theme.css +181 -0
  651. package/styles/tokens.css +284 -0
  652. package/templates/next-app/README.md.tmpl +35 -0
  653. package/templates/next-app/_pages/redirect-page.tsx.tmpl +5 -0
  654. package/templates/next-app/_pages/route-page.tsx.tmpl +9 -0
  655. package/templates/next-app/app/globals.css.tmpl +8 -0
  656. package/templates/next-app/app/layout.tsx.tmpl +19 -0
  657. package/templates/next-app/components/app-shell.tsx.tmpl +34 -0
  658. package/templates/next-app/dot-env.local.example.tmpl +6 -0
  659. package/templates/next-app/dot-gitignore.tmpl +7 -0
  660. package/templates/next-app/lib/nav.ts.tmpl +2 -0
  661. package/templates/next-app/next.config.mjs.tmpl +4 -0
  662. package/templates/next-app/package.json.tmpl +25 -0
  663. package/templates/next-app/postcss.config.mjs.tmpl +4 -0
  664. package/templates/next-app/tsconfig.json.tmpl +21 -0
@@ -0,0 +1,378 @@
1
+ {
2
+ "$schema": "../block.schema.json",
3
+ "id": "crm",
4
+ "title": "Contacts & companies",
5
+ "summary": "Contact and company tables, detail pages, create/edit forms with custom fields, notes, tasks, activity, CSV import and duplicate merging.",
6
+ "features": [
7
+ "paginated, filterable contact and company tables with selection and custom-field columns",
8
+ "contact and company detail pages with tabs for activity, notes and tasks",
9
+ "create/edit form that renders the org's custom fields",
10
+ "CSV/Excel import wizard with column mapping and preview",
11
+ "duplicate review with merge, bulk merge and undo",
12
+ "locked (masked) emails and phones shown as Locked, never as values"
13
+ ],
14
+ "dependsOn": [
15
+ "foundation"
16
+ ],
17
+ "operations": [
18
+ "assert_companies_batch_companies_assert_batch_put",
19
+ "assert_company_companies_assert_put",
20
+ "assert_contact_contacts_assert_put",
21
+ "assert_contacts_batch_contacts_assert_batch_put",
22
+ "create_company_companies_post",
23
+ "create_company_note_companies__company_id__notes_post",
24
+ "create_company_task_companies__company_id__tasks_post",
25
+ "create_contacts_contacts_post",
26
+ "create_field_fields_post",
27
+ "create_list_lists_post",
28
+ "create_note_contacts__contact_id__notes_post",
29
+ "create_task_contacts__contact_id__tasks_post",
30
+ "delete_company_companies__company_id__delete",
31
+ "delete_contact_contacts__contact_id__delete",
32
+ "delete_note_notes__note_id__delete",
33
+ "delete_task_tasks__task_id__delete",
34
+ "duplicate_counts_duplicates_counts_get",
35
+ "get_activity_feed_activity_feed_get",
36
+ "get_bulk_merge_status_duplicates_bulk_merge__run_id__get",
37
+ "get_company_companies__company_id__get",
38
+ "get_contact_contacts__contact_id__get",
39
+ "import_contacts_imports__import_id__contacts_post",
40
+ "list_companies_companies_get",
41
+ "list_company_fields_fields_companies_get",
42
+ "list_company_notes_companies__company_id__notes_get",
43
+ "list_company_tasks_companies__company_id__tasks_get",
44
+ "list_contact_fields_fields_get",
45
+ "list_contact_tasks_contacts__contact_id__tasks_get",
46
+ "list_contacts_contacts_get",
47
+ "list_duplicates_duplicates_get",
48
+ "list_lists_lists_get",
49
+ "list_notes_contacts__contact_id__notes_get",
50
+ "list_recent_merges_duplicates_recent_merges_get",
51
+ "merge_company_companies__company_id__merge_post",
52
+ "merge_contact_contacts__contact_id__merge_post",
53
+ "preview_import_file_imports__import_id__preview_post",
54
+ "reject_suggestion_duplicates__suggestion_id__reject_post",
55
+ "set_field_values_batch_fields_values_batch_patch",
56
+ "start_bulk_merge_duplicates_bulk_merge_post",
57
+ "undo_merge_duplicates_undo_merge__merge_history_id__post",
58
+ "update_company_companies__company_id__patch",
59
+ "update_contact_contacts__contact_id__patch",
60
+ "update_note_notes__note_id__patch",
61
+ "update_task_tasks__task_id__patch",
62
+ "upload_import_file_imports_post"
63
+ ],
64
+ "components": [
65
+ {
66
+ "name": "ContactTable",
67
+ "file": "components/contact-table.tsx",
68
+ "purpose": "Filterable, paginated contacts grid with selection, bulk delete, custom-field columns and slots for other blocks' bulk actions. Embed with listId or companyId.",
69
+ "operations": [
70
+ "list_contact_fields_fields_get",
71
+ "list_contacts_contacts_get",
72
+ "delete_contact_contacts__contact_id__delete"
73
+ ],
74
+ "tiers": [
75
+ "read",
76
+ "destructive"
77
+ ]
78
+ },
79
+ {
80
+ "name": "ContactDetail",
81
+ "file": "components/contact-detail.tsx",
82
+ "purpose": "Full contact page: header, editable overview, Activity/Notes/Tasks tabs, delete. renderActions and extraTabs let other blocks add buttons and tabs.",
83
+ "operations": [
84
+ "get_contact_contacts__contact_id__get",
85
+ "delete_contact_contacts__contact_id__delete",
86
+ "list_contact_fields_fields_get",
87
+ "list_company_fields_fields_companies_get",
88
+ "create_contacts_contacts_post",
89
+ "update_contact_contacts__contact_id__patch",
90
+ "create_company_companies_post",
91
+ "update_company_companies__company_id__patch",
92
+ "set_field_values_batch_fields_values_batch_patch",
93
+ "create_field_fields_post",
94
+ "get_activity_feed_activity_feed_get",
95
+ "list_notes_contacts__contact_id__notes_get",
96
+ "create_note_contacts__contact_id__notes_post",
97
+ "create_company_note_companies__company_id__notes_post",
98
+ "update_note_notes__note_id__patch",
99
+ "list_contact_tasks_contacts__contact_id__tasks_get",
100
+ "create_task_contacts__contact_id__tasks_post",
101
+ "update_task_tasks__task_id__patch",
102
+ "delete_task_tasks__task_id__delete"
103
+ ],
104
+ "tiers": [
105
+ "read",
106
+ "write",
107
+ "destructive"
108
+ ]
109
+ },
110
+ {
111
+ "name": "CompanyTable",
112
+ "file": "components/company-table.tsx",
113
+ "purpose": "Filterable, paginated companies grid; same props shape as ContactTable.",
114
+ "operations": [
115
+ "list_company_fields_fields_companies_get",
116
+ "list_companies_companies_get",
117
+ "delete_company_companies__company_id__delete"
118
+ ],
119
+ "tiers": [
120
+ "read",
121
+ "destructive"
122
+ ]
123
+ },
124
+ {
125
+ "name": "CompanyDetail",
126
+ "file": "components/company-detail.tsx",
127
+ "purpose": "Full company page: header, editable overview, People tab (ContactTable), Activity/Notes/Tasks tabs, delete.",
128
+ "operations": [
129
+ "get_company_companies__company_id__get",
130
+ "delete_company_companies__company_id__delete",
131
+ "list_contact_fields_fields_get",
132
+ "list_company_fields_fields_companies_get",
133
+ "create_contacts_contacts_post",
134
+ "update_contact_contacts__contact_id__patch",
135
+ "create_company_companies_post",
136
+ "update_company_companies__company_id__patch",
137
+ "set_field_values_batch_fields_values_batch_patch",
138
+ "create_field_fields_post",
139
+ "list_contacts_contacts_get",
140
+ "delete_contact_contacts__contact_id__delete",
141
+ "get_activity_feed_activity_feed_get",
142
+ "list_company_notes_companies__company_id__notes_get",
143
+ "create_company_note_companies__company_id__notes_post",
144
+ "update_note_notes__note_id__patch",
145
+ "delete_note_notes__note_id__delete",
146
+ "list_company_tasks_companies__company_id__tasks_get",
147
+ "create_company_task_companies__company_id__tasks_post",
148
+ "update_task_tasks__task_id__patch",
149
+ "delete_task_tasks__task_id__delete"
150
+ ],
151
+ "tiers": [
152
+ "read",
153
+ "write",
154
+ "destructive"
155
+ ]
156
+ },
157
+ {
158
+ "name": "RecordForm",
159
+ "file": "components/record-form.tsx",
160
+ "purpose": "Create or edit one contact or company, rendering the org's custom fields and an inline 'add custom field'. Sends only changed fields on edit; surfaces duplicate-contact 409s.",
161
+ "operations": [
162
+ "list_contact_fields_fields_get",
163
+ "list_company_fields_fields_companies_get",
164
+ "create_contacts_contacts_post",
165
+ "update_contact_contacts__contact_id__patch",
166
+ "create_company_companies_post",
167
+ "update_company_companies__company_id__patch",
168
+ "set_field_values_batch_fields_values_batch_patch",
169
+ "create_field_fields_post"
170
+ ],
171
+ "tiers": [
172
+ "read",
173
+ "write"
174
+ ]
175
+ },
176
+ {
177
+ "name": "ActivityTimeline",
178
+ "file": "components/activity-timeline.tsx",
179
+ "purpose": "Chronological feed of everything that happened to a contact, company or deal.",
180
+ "operations": [
181
+ "get_activity_feed_activity_feed_get"
182
+ ],
183
+ "tiers": [
184
+ "read"
185
+ ]
186
+ },
187
+ {
188
+ "name": "NotesPanel",
189
+ "file": "components/notes-panel.tsx",
190
+ "purpose": "List, add, edit and delete notes on a contact or company; synced notes are read-only.",
191
+ "operations": [
192
+ "list_notes_contacts__contact_id__notes_get",
193
+ "list_company_notes_companies__company_id__notes_get",
194
+ "create_note_contacts__contact_id__notes_post",
195
+ "create_company_note_companies__company_id__notes_post",
196
+ "update_note_notes__note_id__patch",
197
+ "delete_note_notes__note_id__delete"
198
+ ],
199
+ "tiers": [
200
+ "read",
201
+ "write",
202
+ "destructive"
203
+ ]
204
+ },
205
+ {
206
+ "name": "TaskList",
207
+ "file": "components/task-list.tsx",
208
+ "purpose": "Tasks on a contact or company with quick add, complete/reopen and delete.",
209
+ "operations": [
210
+ "list_contact_tasks_contacts__contact_id__tasks_get",
211
+ "list_company_tasks_companies__company_id__tasks_get",
212
+ "create_task_contacts__contact_id__tasks_post",
213
+ "create_company_task_companies__company_id__tasks_post",
214
+ "update_task_tasks__task_id__patch",
215
+ "delete_task_tasks__task_id__delete"
216
+ ],
217
+ "tiers": [
218
+ "read",
219
+ "write",
220
+ "destructive"
221
+ ]
222
+ },
223
+ {
224
+ "name": "ImportWizard",
225
+ "file": "components/import-wizard.tsx",
226
+ "purpose": "Upload a CSV/Excel file, map columns with a live preview, pick the destination list and update policy, import.",
227
+ "operations": [
228
+ "upload_import_file_imports_post",
229
+ "preview_import_file_imports__import_id__preview_post",
230
+ "list_lists_lists_get",
231
+ "create_list_lists_post",
232
+ "import_contacts_imports__import_id__contacts_post"
233
+ ],
234
+ "tiers": [
235
+ "read",
236
+ "write"
237
+ ]
238
+ },
239
+ {
240
+ "name": "DedupeReview",
241
+ "file": "components/dedupe-review.tsx",
242
+ "purpose": "Review duplicate suggestions: merge a pair, mark not-duplicates, bulk merge with a rule (polled), undo recent merges.",
243
+ "operations": [
244
+ "duplicate_counts_duplicates_counts_get",
245
+ "list_duplicates_duplicates_get",
246
+ "reject_suggestion_duplicates__suggestion_id__reject_post",
247
+ "merge_contact_contacts__contact_id__merge_post",
248
+ "merge_company_companies__company_id__merge_post",
249
+ "start_bulk_merge_duplicates_bulk_merge_post",
250
+ "get_bulk_merge_status_duplicates_bulk_merge__run_id__get",
251
+ "list_recent_merges_duplicates_recent_merges_get",
252
+ "undo_merge_duplicates_undo_merge__merge_history_id__post"
253
+ ],
254
+ "tiers": [
255
+ "read",
256
+ "write",
257
+ "destructive"
258
+ ]
259
+ }
260
+ ],
261
+ "hooks": [
262
+ {
263
+ "name": "usePageQuery",
264
+ "file": "hooks/use-page-query.ts",
265
+ "purpose": "Page-number pagination over list endpoints that answer { data, pagination: { page, total, has_next } }; resets to page 1 when filters change."
266
+ },
267
+ {
268
+ "name": "useDebouncedValue",
269
+ "file": "hooks/use-debounced.ts",
270
+ "purpose": "A value that only updates after it stopped changing, for filter boxes."
271
+ },
272
+ {
273
+ "name": "useCustomFields",
274
+ "file": "hooks/use-custom-fields.ts",
275
+ "purpose": "The org's custom columns for contacts or companies.",
276
+ "operations": [
277
+ "list_contact_fields_fields_get",
278
+ "list_company_fields_fields_companies_get"
279
+ ]
280
+ }
281
+ ],
282
+ "lib": [
283
+ {
284
+ "file": "lib/crm-types.ts",
285
+ "purpose": "Record and field types plus helpers (isMasked, contactName, isCustomField) shared by the CRM components."
286
+ }
287
+ ],
288
+ "recipes": [
289
+ {
290
+ "file": "recipes/upsert-contact-with-company.ts",
291
+ "title": "Upsert a contact with its company and custom fields, then read it back",
292
+ "when": "Server-side sync or form handler that must write a person who may already exist. `upsert` matches on work_email, then linkedin_url, so it is safe to retry; `company_domain` links (or creates) the company; `custom_fields` are keyed by the field title or slug. The ids you need next come from the responses: look the contact up by email to get its id, never guess it."
293
+ },
294
+ {
295
+ "file": "recipes/custom-fields-and-values.ts",
296
+ "title": "Create a custom field and write values to many records at once",
297
+ "when": "Adding an org-specific column (a score, a segment) and filling it for a batch of contacts or companies. Field creation is a write; values go in with one batch call (up to 500 rows) instead of one request per record. Field ids come from the create or list response."
298
+ },
299
+ {
300
+ "file": "recipes/notes-tasks-and-activity.ts",
301
+ "title": "Log a note, create a follow-up task and read the activity feed",
302
+ "when": "Recording what happened on a contact or company and showing the combined timeline. The feed is one read over notes, tasks, emails, calls, meetings and field changes, newest first."
303
+ },
304
+ {
305
+ "file": "recipes/dedupe-and-merge.ts",
306
+ "title": "Review duplicate suggestions and merge one pair",
307
+ "when": "Cleaning the CRM. A merge is destructive (the secondary record stops existing; undo works for 30 days), so list and preview freely but ask a human before calling `mergeContact`. The suggestion and contact ids come from the list response."
308
+ }
309
+ ],
310
+ "prerequisites": [
311
+ {
312
+ "id": "proxy-allowlist",
313
+ "where": "app",
314
+ "description": "The /api/g8 proxy route allows the CRM areas: allowedPrefixes must include contacts, companies, fields, notes, tasks, activity-feed, imports, duplicates and lists, and `authorize` must check your own session.",
315
+ "verify": "app/api/g8/[...path]/route.ts lists those prefixes in allowedPrefixes and sets authorize; GET /api/g8/contacts?limit=1 returns 200 when signed in and 401/403 when not."
316
+ },
317
+ {
318
+ "id": "api-key-scopes",
319
+ "where": "api",
320
+ "description": "The server API key carries the scopes in this block's `scopes` (contacts, companies, lists and notes/tasks read+write; contacts:delete for bulk merge).",
321
+ "verify": "recipes/verify-proxy-key.ts passes and g8.api.me.describeCurrentKey() lists the scopes."
322
+ },
323
+ {
324
+ "id": "import-size-limit",
325
+ "where": "app",
326
+ "description": "ImportWizard uploads through the proxy, which caps request bodies at 5 MiB. For larger files raise maxBodyBytes in app/api/g8/[...path]/route.ts (and pass maxBytes to ImportWizard) or run the import server-side.",
327
+ "verify": "A file near the limit uploads without a 413."
328
+ }
329
+ ],
330
+ "scopes": [
331
+ "companies:delete",
332
+ "companies:read",
333
+ "companies:write",
334
+ "contacts:delete",
335
+ "contacts:read",
336
+ "contacts:write",
337
+ "fields:read",
338
+ "fields:write",
339
+ "lists:read",
340
+ "lists:write",
341
+ "notes:delete",
342
+ "notes:read",
343
+ "notes:write",
344
+ "tasks:delete",
345
+ "tasks:read",
346
+ "tasks:write"
347
+ ],
348
+ "defaults": [
349
+ {
350
+ "decision": "Which list new contacts created by RecordForm join",
351
+ "default": "None: the contact is created without a list (pass listId to put it in one)",
352
+ "askHuman": false
353
+ },
354
+ {
355
+ "decision": "Import update policy",
356
+ "default": "autofill (only fill blanks); overwrite is opt-in and confirmed",
357
+ "askHuman": false
358
+ },
359
+ {
360
+ "decision": "Whether to fill missing import fields from graph8 (uses credits)",
361
+ "default": "Off",
362
+ "askHuman": false
363
+ },
364
+ {
365
+ "decision": "Page size of tables",
366
+ "default": "25 rows",
367
+ "askHuman": false
368
+ },
369
+ {
370
+ "decision": "Who may delete or merge records",
371
+ "default": "Anyone the app lets through the proxy; the API key's scopes are the only other limit",
372
+ "askHuman": true
373
+ }
374
+ ],
375
+ "peerDependencies": {
376
+ "react": ">=18"
377
+ }
378
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ /**
4
+ * ActivityTimeline: one chronological feed of everything that happened to a
5
+ * contact, company or deal (notes, tasks, emails, calls, meetings, stage and field changes).
6
+ *
7
+ * SDK operations, in order (read only, no confirm dialog):
8
+ * 1. getActivityFeed GET /activity-feed?entity_type&entity_id&types&source&limit
9
+ *
10
+ * The feed is newest first. "Load more" asks for a bigger `limit` (the endpoint
11
+ * has no cursor); the page never shows more than `maxLimit` events.
12
+ */
13
+
14
+ import { useState } from "react";
15
+ import { Button, Skeleton } from "@graph8/sdk/ui";
16
+ import { ApiError } from "@/g8/components/api-error";
17
+ import { EmptyState } from "@/g8/components/empty-state";
18
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
19
+ import { g8Get } from "@/g8/lib/g8-client";
20
+
21
+ export interface ActivityEvent {
22
+ id: string;
23
+ record_type: string;
24
+ occurred_at: string;
25
+ title: string;
26
+ summary?: string | null;
27
+ source?: string;
28
+ actor?: { name?: string | null; email?: string | null } | null;
29
+ changes?: Array<{ field: string; field_label: string; from_value?: unknown; to_value?: unknown }>;
30
+ }
31
+
32
+ export interface ActivityTimelineProps {
33
+ entityType: "contact" | "company" | "deal";
34
+ entityId: number | string;
35
+ /** Restrict to some event types, for example ["note", "email", "call"]. */
36
+ types?: string[];
37
+ pageSize?: number;
38
+ maxLimit?: number;
39
+ /** Change it to reload after you wrote a note or task next to the feed. */
40
+ refreshKey?: number;
41
+ className?: string;
42
+ }
43
+
44
+ const TYPE_LABEL: Record<string, string> = {
45
+ task: "Task", note: "Note", email: "Email", call: "Call", meeting: "Meeting", chat: "Chat", sms: "SMS",
46
+ stage_change: "Stage", quote: "Quote", record_change: "Change", deal: "Deal",
47
+ };
48
+
49
+ function show(value: unknown): string {
50
+ return value == null || value === "" ? "empty" : String(value);
51
+ }
52
+
53
+ export function ActivityTimeline({ entityType, entityId, types, pageSize = 25, maxLimit = 200, refreshKey, className }: ActivityTimelineProps) {
54
+ const [limit, setLimit] = useState(pageSize);
55
+ const feed = useG8Query(
56
+ async (signal) => {
57
+ const res = await g8Get<{ items?: ActivityEvent[] } | ActivityEvent[]>(
58
+ "/activity-feed",
59
+ { entity_type: entityType, entity_id: String(entityId), types: types?.join(","), limit },
60
+ { signal },
61
+ );
62
+ const body = res.data;
63
+ return Array.isArray(body) ? body : (body?.items ?? []);
64
+ },
65
+ [entityType, entityId, types?.join(","), limit, refreshKey],
66
+ );
67
+
68
+ if (feed.error && !feed.data) return <ApiError error={feed.error} title="Could not load activity" onRetry={() => void feed.refetch()} />;
69
+ if (feed.loading && !feed.data) return <div className={`flex flex-col gap-3 ${className ?? ""}`}>{[0, 1, 2].map((i) => <Skeleton key={i} className="h-12 w-full" />)}</div>;
70
+ const events = feed.data ?? [];
71
+ if (events.length === 0) return <EmptyState className={className} title="No activity yet" description="Notes, tasks, emails, calls and changes show up here." />;
72
+
73
+ return (
74
+ <div className={className}>
75
+ <ol className="flex flex-col gap-3" aria-label="Activity">
76
+ {events.map((e) => (
77
+ <li key={e.id} className="rounded-md border p-3" data-testid="activity-item">
78
+ <div className="flex items-center justify-between gap-2 text-xs text-muted-foreground">
79
+ <span className="font-medium uppercase tracking-wide">{TYPE_LABEL[e.record_type] ?? e.record_type}</span>
80
+ <time dateTime={e.occurred_at}>{new Date(e.occurred_at).toLocaleString()}</time>
81
+ </div>
82
+ <p className="mt-1 text-sm font-medium">{e.title}</p>
83
+ {e.summary ? <p className="mt-0.5 whitespace-pre-wrap text-sm text-muted-foreground">{e.summary}</p> : null}
84
+ {e.changes?.length ? (
85
+ <ul className="mt-1 text-xs text-muted-foreground">
86
+ {e.changes.map((c) => <li key={c.field}>{c.field_label}: {show(c.from_value)} to {show(c.to_value)}</li>)}
87
+ </ul>
88
+ ) : null}
89
+ {e.actor?.name || e.actor?.email ? <p className="mt-1 text-xs text-muted-foreground">by {e.actor.name ?? e.actor.email}</p> : null}
90
+ </li>
91
+ ))}
92
+ </ol>
93
+ {feed.error ? <ApiError className="mt-3" error={feed.error} title="Could not refresh activity" onRetry={() => void feed.refetch()} /> : null}
94
+ {events.length >= limit && limit < maxLimit ? (
95
+ <div className="mt-3 flex justify-center"><Button variant="outline" size="sm" loading={feed.loading} onClick={() => setLimit((l) => Math.min(maxLimit, l + pageSize))}>Load more</Button></div>
96
+ ) : null}
97
+ </div>
98
+ );
99
+ }
@@ -0,0 +1,118 @@
1
+ "use client";
2
+
3
+ /**
4
+ * CompanyDetail: the full company record. Header, Overview (editable through
5
+ * RecordForm), a People tab that embeds ContactTable for this company, and tabs
6
+ * for Activity, Notes and Tasks.
7
+ *
8
+ * SDK operations, in order:
9
+ * 1. getCompany GET /companies/{id}?include_custom_fields=true (read)
10
+ * 2. updateCompany + setFieldValuesBatch via RecordForm (write)
11
+ * 3. listContacts (company_id), deleteContact via ContactTable (read, destructive)
12
+ * 4. getActivityFeed, notes and tasks ops via ActivityTimeline, NotesPanel, TaskList
13
+ * 5. deleteCompany DELETE /companies/{id} (destructive, ConfirmSpendDialog)
14
+ */
15
+
16
+ import { useState } from "react";
17
+ import type { ReactNode } from "react";
18
+ import { Button, Skeleton, Tabs, TabsContent, TabsList, TabsTrigger } from "@graph8/sdk/ui";
19
+ import { ApiError } from "@/g8/components/api-error";
20
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
21
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
22
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
23
+ import { g8Delete, g8Get } from "@/g8/lib/g8-client";
24
+ import { initials, location } from "../lib/crm-types";
25
+ import type { CompanyRecord, ContactRecord } from "../lib/crm-types";
26
+ import { ActivityTimeline } from "./activity-timeline";
27
+ import { ContactTable } from "./contact-table";
28
+ import { NotesPanel } from "./notes-panel";
29
+ import { RecordForm } from "./record-form";
30
+ import { TaskList } from "./task-list";
31
+
32
+ export interface CompanyDetailProps {
33
+ companyId: number;
34
+ onDeleted?: (companyId: number) => void;
35
+ /** Called when a person in the People tab is clicked: route to your ContactDetail. */
36
+ onOpenContact?: (contact: ContactRecord) => void;
37
+ renderActions?: (company: CompanyRecord) => ReactNode;
38
+ extraTabs?: Array<{ id: string; label: string; render: (company: CompanyRecord) => ReactNode }>;
39
+ className?: string;
40
+ }
41
+
42
+ function Fact({ label, value }: { label: string; value?: string | number | null }) {
43
+ return (
44
+ <div>
45
+ <dt className="text-xs uppercase tracking-wide text-muted-foreground">{label}</dt>
46
+ <dd className="text-sm">{value || <span className="text-muted-foreground">-</span>}</dd>
47
+ </div>
48
+ );
49
+ }
50
+
51
+ export function CompanyDetail({ companyId, onDeleted, onOpenContact, renderActions, extraTabs = [], className }: CompanyDetailProps) {
52
+ const [editing, setEditing] = useState(false);
53
+ const [confirmDelete, setConfirmDelete] = useState(false);
54
+ const [feedKey, setFeedKey] = useState(0);
55
+ const query = useG8Query((signal) => g8Get<CompanyRecord>(`/companies/${companyId}`, { include_custom_fields: true }, { signal }), [companyId]);
56
+ const remove = useG8Mutation(() => g8Delete(`/companies/${companyId}`), { onSuccess: () => { setConfirmDelete(false); onDeleted?.(companyId); } });
57
+ const bump = () => setFeedKey((k) => k + 1);
58
+
59
+ if (query.loading && !query.data) return <div className={`flex flex-col gap-3 ${className ?? ""}`}><Skeleton className="h-16 w-full" /><Skeleton className="h-40 w-full" /></div>;
60
+ if (query.error && !query.data) return <ApiError error={query.error} title="Could not load this company" onRetry={() => void query.refetch()} />;
61
+ const company = query.data!.data;
62
+ const name = company.name || company.domain || "Unnamed company";
63
+
64
+ return (
65
+ <div className={`flex flex-col gap-4 ${className ?? ""}`}>
66
+ <header className="flex flex-wrap items-center gap-3">
67
+ <div aria-hidden className="flex size-12 items-center justify-center rounded-md bg-muted text-sm font-semibold">{initials(name)}</div>
68
+ <div className="min-w-0 flex-1">
69
+ <h2 className="truncate text-lg font-semibold">{name}</h2>
70
+ <p className="truncate text-sm text-muted-foreground">{[company.domain, company.industry].filter(Boolean).join(" - ") || "No domain or industry"}</p>
71
+ </div>
72
+ <div className="flex flex-wrap items-center gap-2">
73
+ {renderActions?.(company)}
74
+ <Button variant="outline" size="sm" onClick={() => setEditing((v) => !v)}>{editing ? "Close editor" : "Edit"}</Button>
75
+ <Button variant="destructive" size="sm" onClick={() => setConfirmDelete(true)}>Delete</Button>
76
+ </div>
77
+ </header>
78
+ {remove.error ? <ApiError error={remove.error} title="Could not delete the company" /> : null}
79
+
80
+ <Tabs defaultValue="overview">
81
+ <TabsList>
82
+ <TabsTrigger value="overview">Overview</TabsTrigger>
83
+ <TabsTrigger value="people">People</TabsTrigger>
84
+ <TabsTrigger value="activity">Activity</TabsTrigger>
85
+ <TabsTrigger value="notes">Notes</TabsTrigger>
86
+ <TabsTrigger value="tasks">Tasks</TabsTrigger>
87
+ {extraTabs.map((t) => <TabsTrigger key={t.id} value={t.id}>{t.label}</TabsTrigger>)}
88
+ </TabsList>
89
+ <TabsContent value="overview" className="pt-4">
90
+ {editing ? (
91
+ <RecordForm entity="company" record={company} onCancel={() => setEditing(false)} onSaved={() => { setEditing(false); void query.refetch(); bump(); }} />
92
+ ) : (
93
+ <dl className="grid grid-cols-1 gap-4 sm:grid-cols-2">
94
+ <Fact label="Website" value={company.website} />
95
+ <Fact label="Phone" value={company.phone} />
96
+ <Fact label="Employees" value={company.employee_count} />
97
+ <Fact label="Revenue" value={company.revenue} />
98
+ <Fact label="Founded" value={company.founded_year} />
99
+ <Fact label="Location" value={location(company)} />
100
+ <Fact label="LinkedIn" value={company.linkedin_url} />
101
+ <Fact label="Owner" value={company.owner_name} />
102
+ {Object.entries(company.custom_fields ?? {}).map(([key, v]) => <Fact key={key} label={key} value={v} />)}
103
+ {company.description ? <div className="sm:col-span-2"><Fact label="About" value={company.description} /></div> : null}
104
+ </dl>
105
+ )}
106
+ </TabsContent>
107
+ <TabsContent value="people" className="pt-4"><ContactTable companyId={companyId} pageSize={10} onOpen={onOpenContact} /></TabsContent>
108
+ <TabsContent value="activity" className="pt-4"><ActivityTimeline entityType="company" entityId={companyId} refreshKey={feedKey} /></TabsContent>
109
+ <TabsContent value="notes" className="pt-4"><NotesPanel entity="company" entityId={companyId} onChanged={bump} /></TabsContent>
110
+ <TabsContent value="tasks" className="pt-4"><TaskList entity="company" entityId={companyId} onChanged={bump} /></TabsContent>
111
+ {extraTabs.map((t) => <TabsContent key={t.id} value={t.id} className="pt-4">{t.render(company)}</TabsContent>)}
112
+ </Tabs>
113
+
114
+ <ConfirmSpendDialog open={confirmDelete} tier="destructive" title={`Delete ${name}`} description="Its contacts stay but lose the company link."
115
+ affected={1} confirming={remove.loading} onConfirm={() => void remove.mutate(undefined)} onCancel={() => setConfirmDelete(false)} />
116
+ </div>
117
+ );
118
+ }