@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
@@ -10,7 +10,12 @@
10
10
  "CSV/Excel import wizard with column mapping and preview",
11
11
  "duplicate review with merge, bulk merge and undo",
12
12
  "locked (masked) emails and phones shown as Locked, never as values",
13
- "finished ContactsScreen, CompaniesScreen and TasksScreen (list, detail, create, edit, delete, import, duplicates)"
13
+ "finished ContactsScreen, CompaniesScreen and TasksScreen (list, detail, create, edit, delete, import, duplicates)",
14
+ "assign owner to all contacts of a list with an injected team-member picker (never for a selection), confirmed with the list and N",
15
+ "contact tags: chips on the contact, add or remove on a selection, and a tag manager (create, rename, recolour, archive, delete, merge)",
16
+ "bulk edit of one field on a selection",
17
+ "verify emails and unlock locked contacts, each confirmed with the real count and cost",
18
+ "AI documents, signals and an on-demand next best step on the open contact"
14
19
  ],
15
20
  "dependsOn": [
16
21
  "foundation"
@@ -20,9 +25,13 @@
20
25
  "assert_company_companies_assert_put",
21
26
  "assert_contact_contacts_assert_put",
22
27
  "assert_contacts_batch_contacts_assert_batch_put",
28
+ "assign_contact_owner_contacts_assign_owner_post",
29
+ "assign_contact_tags_contacts_tags_assign_post",
30
+ "bulk_update_contacts_contacts_bulk_update_post",
23
31
  "create_company_companies_post",
24
32
  "create_company_note_companies__company_id__notes_post",
25
33
  "create_company_task_companies__company_id__tasks_post",
34
+ "create_contact_tag_contacts_tags_post",
26
35
  "create_contacts_contacts_post",
27
36
  "create_field_fields_post",
28
37
  "create_global_task_tasks_post",
@@ -31,13 +40,25 @@
31
40
  "create_task_contacts__contact_id__tasks_post",
32
41
  "delete_company_companies__company_id__delete",
33
42
  "delete_contact_contacts__contact_id__delete",
43
+ "delete_contact_tag_contacts_tags__tag_id__delete",
34
44
  "delete_note_notes__note_id__delete",
35
45
  "delete_task_tasks__task_id__delete",
36
46
  "duplicate_counts_duplicates_counts_get",
47
+ "generate_all_contact_documents_contacts__contact_id__documents_generate_all_post",
48
+ "generate_contact_document_contacts__contact_id__documents_types__doc_type__generate_post",
37
49
  "get_activity_feed_activity_feed_get",
38
50
  "get_bulk_merge_status_duplicates_bulk_merge__run_id__get",
39
51
  "get_company_companies__company_id__get",
52
+ "get_company_engagement_summary_companies__company_id__engagement_summary_get",
53
+ "get_company_sites_visited_companies__company_id__sites_visited_get",
40
54
  "get_contact_contacts__contact_id__get",
55
+ "get_contact_document_contacts__contact_id__documents_types__doc_type__get",
56
+ "get_contact_engagement_summary_contacts__contact_id__engagement_summary_get",
57
+ "get_contact_generation_status_contacts__contact_id__documents_generation_status_get",
58
+ "get_contact_next_best_step_contacts__contact_id__next_best_step_get",
59
+ "get_contact_signals_contacts__contact_id__signals_get",
60
+ "get_contact_sites_visited_contacts__contact_id__sites_visited_get",
61
+ "get_contact_tag_ids_contacts__contact_id__tags_get",
41
62
  "get_task_tasks__task_id__get",
42
63
  "import_contacts_imports__import_id__contacts_post",
43
64
  "list_all_tasks_tasks_get",
@@ -46,6 +67,7 @@
46
67
  "list_company_notes_companies__company_id__notes_get",
47
68
  "list_company_tasks_companies__company_id__tasks_get",
48
69
  "list_contact_fields_fields_get",
70
+ "list_contact_tags_contacts_tags_get",
49
71
  "list_contact_tasks_contacts__contact_id__tasks_get",
50
72
  "list_contacts_contacts_get",
51
73
  "list_duplicates_duplicates_get",
@@ -54,16 +76,22 @@
54
76
  "list_recent_merges_duplicates_recent_merges_get",
55
77
  "merge_company_companies__company_id__merge_post",
56
78
  "merge_contact_contacts__contact_id__merge_post",
79
+ "merge_contact_tags_contacts_tags_merge_post",
57
80
  "preview_import_file_imports__import_id__preview_post",
81
+ "regenerate_contact_document_contacts__contact_id__documents_types__doc_type__regenerate_post",
58
82
  "reject_suggestion_duplicates__suggestion_id__reject_post",
59
83
  "set_field_values_batch_fields_values_batch_patch",
60
84
  "start_bulk_merge_duplicates_bulk_merge_post",
85
+ "unassign_contact_tags_contacts_tags_unassign_post",
61
86
  "undo_merge_duplicates_undo_merge__merge_history_id__post",
87
+ "unlock_contact_info_contacts_unlock_info_post",
62
88
  "update_company_companies__company_id__patch",
63
89
  "update_contact_contacts__contact_id__patch",
90
+ "update_contact_tag_contacts_tags__tag_id__patch",
64
91
  "update_note_notes__note_id__patch",
65
92
  "update_task_tasks__task_id__patch",
66
- "upload_import_file_imports_post"
93
+ "upload_import_file_imports_post",
94
+ "verify_contact_emails_contacts_verify_email_post"
67
95
  ],
68
96
  "components": [
69
97
  {
@@ -264,15 +292,36 @@
264
292
  {
265
293
  "name": "ContactsScreen",
266
294
  "file": "components/contacts-screen.tsx",
267
- "purpose": "Finished contacts workspace: search, filters, sort, paging, bulk delete, detail panel with activity/notes/tasks, create, edit, CSV import and duplicate review. No required props; slot props for other blocks.",
295
+ "purpose": "Finished contacts workspace: search, filters, sort, paging, owner column, bulk delete, detail panel with activity/notes/tasks/AI documents/insights, create, edit, CSV import and duplicate review, plus record actions: assign owner to a whole list (with an injected picker), tags (chips, bulk, manager), bulk edit, verify email and unlock info. No required props; slot props for other blocks.",
268
296
  "operations": [
297
+ "assign_contact_owner_contacts_assign_owner_post",
298
+ "assign_contact_tags_contacts_tags_assign_post",
299
+ "bulk_update_contacts_contacts_bulk_update_post",
300
+ "create_contact_tag_contacts_tags_post",
269
301
  "delete_contact_contacts__contact_id__delete",
302
+ "delete_contact_tag_contacts_tags__tag_id__delete",
270
303
  "duplicate_counts_duplicates_counts_get",
304
+ "generate_all_contact_documents_contacts__contact_id__documents_generate_all_post",
305
+ "generate_contact_document_contacts__contact_id__documents_types__doc_type__generate_post",
271
306
  "get_contact_contacts__contact_id__get",
272
- "list_contacts_contacts_get"
307
+ "get_contact_document_contacts__contact_id__documents_types__doc_type__get",
308
+ "get_contact_generation_status_contacts__contact_id__documents_generation_status_get",
309
+ "get_contact_next_best_step_contacts__contact_id__next_best_step_get",
310
+ "get_contact_signals_contacts__contact_id__signals_get",
311
+ "get_contact_tag_ids_contacts__contact_id__tags_get",
312
+ "list_contact_tags_contacts_tags_get",
313
+ "list_contacts_contacts_get",
314
+ "merge_contact_tags_contacts_tags_merge_post",
315
+ "regenerate_contact_document_contacts__contact_id__documents_types__doc_type__regenerate_post",
316
+ "unassign_contact_tags_contacts_tags_unassign_post",
317
+ "unlock_contact_info_contacts_unlock_info_post",
318
+ "update_contact_tag_contacts_tags__tag_id__patch",
319
+ "verify_contact_emails_contacts_verify_email_post"
273
320
  ],
274
321
  "tiers": [
275
322
  "read",
323
+ "write",
324
+ "billable",
276
325
  "destructive"
277
326
  ]
278
327
  },
@@ -296,17 +345,194 @@
296
345
  "file": "components/tasks-screen.tsx",
297
346
  "purpose": "Workspace-wide task queue: Open/Completed/All, search and filters, complete or reopen, create, edit, delete, bulk complete and delete, detail panel.",
298
347
  "operations": [
299
- "list_all_tasks_tasks_get",
300
- "get_task_tasks__task_id__get",
301
348
  "create_global_task_tasks_post",
302
- "update_task_tasks__task_id__patch",
303
- "delete_task_tasks__task_id__delete"
349
+ "delete_task_tasks__task_id__delete",
350
+ "get_task_tasks__task_id__get",
351
+ "list_all_tasks_tasks_get",
352
+ "update_task_tasks__task_id__patch"
353
+ ],
354
+ "tiers": [
355
+ "read",
356
+ "write",
357
+ "destructive"
358
+ ]
359
+ },
360
+ {
361
+ "name": "ListSelect",
362
+ "file": "components/list-select.tsx",
363
+ "purpose": "Audience-list dropdown, searchable and paged (the foundation ListSelect with GET /lists wired in). Replaces a select fed by one capped list read.",
364
+ "operations": [
365
+ "list_lists_lists_get"
366
+ ],
367
+ "tiers": [
368
+ "read"
369
+ ]
370
+ },
371
+ {
372
+ "name": "ContactLookupLink",
373
+ "file": "components/contact-lookup-link.tsx",
374
+ "purpose": "\"Open contact\" button for something that only knows an email (inbox thread, meeting attendee, visitor, form submission): looks the email up in the CRM and calls onOpen(contactId); renders nothing when nobody has it, or with createIfMissing offers \"Add to contacts\" (creates the contact from the email and opens it).",
375
+ "operations": [
376
+ "create_contacts_contacts_post",
377
+ "list_contacts_contacts_get"
378
+ ],
379
+ "tiers": [
380
+ "read",
381
+ "write"
382
+ ]
383
+ },
384
+ {
385
+ "name": "RecordOpenLinks",
386
+ "file": "components/record-open-links.tsx",
387
+ "purpose": "\"Open contact\" and \"Open company\" buttons for something that only knows ids (a call log entry, a website visitor). The ids are verified against the CRM first (and a company's domain, when given), so a button never points at a missing or wrong record.",
388
+ "operations": [
389
+ "get_company_companies__company_id__get",
390
+ "get_contact_contacts__contact_id__get"
391
+ ],
392
+ "tiers": [
393
+ "read"
394
+ ]
395
+ },
396
+ {
397
+ "name": "RecordEngagementTab",
398
+ "file": "components/record-engagement-tab.tsx",
399
+ "purpose": "Engagement counts, last touch and pages visited for one contact or company; an extraDetailTabs entry for ContactsScreen and CompaniesScreen.",
400
+ "operations": [
401
+ "get_contact_engagement_summary_contacts__contact_id__engagement_summary_get",
402
+ "get_company_engagement_summary_companies__company_id__engagement_summary_get",
403
+ "get_contact_sites_visited_contacts__contact_id__sites_visited_get",
404
+ "get_company_sites_visited_companies__company_id__sites_visited_get"
405
+ ],
406
+ "tiers": [
407
+ "read"
408
+ ]
409
+ },
410
+ {
411
+ "name": "OwnerAssignListButton",
412
+ "file": "components/owner-assign.tsx",
413
+ "purpose": "\"Assign owner to all N contacts in this list\" with an injected team-member picker (renderPicker), hidden without one. Never for a selection: the API assigns by filter. Both assign and remove are confirmed with the list and N.",
414
+ "operations": [
415
+ "assign_contact_owner_contacts_assign_owner_post"
416
+ ],
417
+ "tiers": [
418
+ "write"
419
+ ]
420
+ },
421
+ {
422
+ "name": "ContactTagChips",
423
+ "file": "components/contact-tag-chips.tsx",
424
+ "purpose": "The tags on one contact as chips with remove buttons and an Add tag picker that can create a tag.",
425
+ "operations": [
426
+ "assign_contact_tags_contacts_tags_assign_post",
427
+ "create_contact_tag_contacts_tags_post",
428
+ "get_contact_tag_ids_contacts__contact_id__tags_get",
429
+ "list_contact_tags_contacts_tags_get",
430
+ "unassign_contact_tags_contacts_tags_unassign_post"
431
+ ],
432
+ "tiers": [
433
+ "read",
434
+ "write"
435
+ ]
436
+ },
437
+ {
438
+ "name": "TagAssignButton",
439
+ "file": "components/tag-assign.tsx",
440
+ "purpose": "Add tags to, or remove tags from, a selection of contacts.",
441
+ "operations": [
442
+ "assign_contact_tags_contacts_tags_assign_post",
443
+ "list_contact_tags_contacts_tags_get",
444
+ "unassign_contact_tags_contacts_tags_unassign_post"
445
+ ],
446
+ "tiers": [
447
+ "read",
448
+ "write"
449
+ ]
450
+ },
451
+ {
452
+ "name": "TagManager",
453
+ "file": "components/tag-manager.tsx",
454
+ "purpose": "Create, rename, recolour, archive, delete and merge contact tags. Delete and merge are confirmed.",
455
+ "operations": [
456
+ "create_contact_tag_contacts_tags_post",
457
+ "delete_contact_tag_contacts_tags__tag_id__delete",
458
+ "list_contact_tags_contacts_tags_get",
459
+ "merge_contact_tags_contacts_tags_merge_post",
460
+ "update_contact_tag_contacts_tags__tag_id__patch"
304
461
  ],
305
462
  "tiers": [
306
463
  "read",
307
464
  "write",
308
465
  "destructive"
309
466
  ]
467
+ },
468
+ {
469
+ "name": "BulkUpdateButton",
470
+ "file": "components/bulk-update.tsx",
471
+ "purpose": "Set one field (title, department, seniority, city, state, country) on every selected contact.",
472
+ "operations": [
473
+ "bulk_update_contacts_contacts_bulk_update_post"
474
+ ],
475
+ "tiers": [
476
+ "write"
477
+ ]
478
+ },
479
+ {
480
+ "name": "VerifyEmailButton",
481
+ "file": "components/verify-email-button.tsx",
482
+ "purpose": "Verify the work emails of a selection (billable, confirmed with the real count and cost).",
483
+ "operations": [
484
+ "verify_contact_emails_contacts_verify_email_post"
485
+ ],
486
+ "tiers": [
487
+ "billable"
488
+ ]
489
+ },
490
+ {
491
+ "name": "UnlockInfoButton",
492
+ "file": "components/unlock-info-button.tsx",
493
+ "purpose": "Unlock the locked contacts of a selection (billable, confirmed with the locked count and cost).",
494
+ "operations": [
495
+ "unlock_contact_info_contacts_unlock_info_post"
496
+ ],
497
+ "tiers": [
498
+ "billable"
499
+ ]
500
+ },
501
+ {
502
+ "name": "ContactRowActions",
503
+ "file": "components/contact-row-actions.tsx",
504
+ "purpose": "The row's ... button: owner, tags, verify and unlock for one contact.",
505
+ "operations": [],
506
+ "tiers": []
507
+ },
508
+ {
509
+ "name": "ContactDocumentsTab",
510
+ "file": "components/contact-documents-tab.tsx",
511
+ "purpose": "AI documents about one contact: status, write, rewrite, write all (billable, confirmed) and read.",
512
+ "operations": [
513
+ "generate_all_contact_documents_contacts__contact_id__documents_generate_all_post",
514
+ "generate_contact_document_contacts__contact_id__documents_types__doc_type__generate_post",
515
+ "get_contact_document_contacts__contact_id__documents_types__doc_type__get",
516
+ "get_contact_generation_status_contacts__contact_id__documents_generation_status_get",
517
+ "regenerate_contact_document_contacts__contact_id__documents_types__doc_type__regenerate_post"
518
+ ],
519
+ "tiers": [
520
+ "read",
521
+ "billable"
522
+ ]
523
+ },
524
+ {
525
+ "name": "ContactInsightsTab",
526
+ "file": "components/contact-insights-tab.tsx",
527
+ "purpose": "Signals for one contact and an on-demand next best step (billable, confirmed).",
528
+ "operations": [
529
+ "get_contact_next_best_step_contacts__contact_id__next_best_step_get",
530
+ "get_contact_signals_contacts__contact_id__signals_get"
531
+ ],
532
+ "tiers": [
533
+ "read",
534
+ "billable"
535
+ ]
310
536
  }
311
537
  ],
312
538
  "hooks": [
@@ -328,6 +554,19 @@
328
554
  "list_contact_fields_fields_get",
329
555
  "list_company_fields_fields_companies_get"
330
556
  ]
557
+ },
558
+ {
559
+ "name": "useAllLists",
560
+ "file": "hooks/use-all-lists.ts",
561
+ "purpose": "Every list, page after page, for lookups by id (a list's size or name); ListSelect is the picker."
562
+ },
563
+ {
564
+ "name": "useContactTags",
565
+ "file": "hooks/use-contact-tags.ts",
566
+ "purpose": "The org's contact tags with counts, cached so chips, dialogs and the manager refresh together.",
567
+ "operations": [
568
+ "list_contact_tags_contacts_tags_get"
569
+ ]
331
570
  }
332
571
  ],
333
572
  "lib": [
@@ -338,6 +577,14 @@
338
577
  {
339
578
  "file": "lib/screen-parts.tsx",
340
579
  "purpose": "Sorting, row selection, bulk delete and pager shared by ContactsScreen, CompaniesScreen and TasksScreen."
580
+ },
581
+ {
582
+ "file": "lib/read-lists.ts",
583
+ "purpose": "One page of audience lists (GET /lists), the read behind ListSelect and useAllLists."
584
+ },
585
+ {
586
+ "file": "lib/record-actions.ts",
587
+ "purpose": "Tag type, the id-set grid filter, the paid-batch cap, unlock and verify target sets and cost constants, bulk-edit fields and the verify summary, shared by the record-action components."
341
588
  }
342
589
  ],
343
590
  "recipes": [
@@ -372,7 +619,7 @@
372
619
  {
373
620
  "id": "api-key-scopes",
374
621
  "where": "api",
375
- "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).",
622
+ "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; enrichment:run for verify email, unlock info, AI documents and next best step, all of which spend credits).",
376
623
  "verify": "recipes/verify-proxy-key.ts passes and g8.api.me.describeCurrentKey() lists the scopes."
377
624
  },
378
625
  {
@@ -389,6 +636,7 @@
389
636
  "contacts:delete",
390
637
  "contacts:read",
391
638
  "contacts:write",
639
+ "enrichment:run",
392
640
  "fields:read",
393
641
  "fields:write",
394
642
  "lists:read",
@@ -0,0 +1,119 @@
1
+ "use client";
2
+
3
+ /**
4
+ * BulkUpdateButton: set ONE field to the same value on every selected contact (job title, department, seniority, city,
5
+ * state, country). A dialog asks for the field and the value and says how many contacts change.
6
+ *
7
+ * SDK operations:
8
+ * 1. bulkUpdateContacts POST /contacts/bulk-update [{ id, CONTACT_<FIELD>: value }, ...] (write)
9
+ * The body is one object per contact. The API authorises the WHOLE set before the first write, so a contact the
10
+ * key cannot write refuses the whole chunk; failures after that are reported per contact in `errors`, not as an
11
+ * HTTP error. The selection is sent in chunks of 100 with Promise.allSettled so one refused chunk does not hide
12
+ * the others, and the dialog reports "N of M updated".
13
+ *
14
+ * The value is required: the API gives no documented way to clear a field in bulk, so clearing is not offered (use the
15
+ * contact's own Edit form). The owner and tags have their own actions.
16
+ */
17
+
18
+ import { useState } from "react";
19
+ import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@graph8/sdk/ui";
20
+ import { ListChecks } from "lucide-react";
21
+ import { ApiError } from "@/g8/components/api-error";
22
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
23
+ import { g8Post } from "@/g8/lib/g8-client";
24
+ import { plural } from "@/g8/lib/plural";
25
+ import { BULK_FIELDS } from "../lib/record-actions";
26
+ import type { BulkFieldKey } from "../lib/record-actions";
27
+
28
+ const CHUNK = 100;
29
+
30
+ export interface BulkUpdateButtonProps {
31
+ contactIds: ReadonlyArray<number>;
32
+ /** Called with the ids that were updated, so the screen can refetch. */
33
+ onDone?: (updated: number[]) => void;
34
+ variant?: "default" | "outline" | "secondary" | "ghost";
35
+ size?: "default" | "sm";
36
+ className?: string;
37
+ }
38
+
39
+ interface BulkResult { updated: number[]; failed: Array<{ id: number; error: string }>; refusedChunks: number }
40
+
41
+ /** `{ updated, errors: [{ id, error }] }` from one chunk; ids not named as failed count as updated. */
42
+ export function chunkOutcome(ids: number[], data: unknown): { ok: number[]; failed: Array<{ id: number; error: string }> } {
43
+ const errors = ((data as { errors?: Array<{ id?: unknown; error?: unknown }> } | null)?.errors ?? [])
44
+ .map((e) => ({ id: Number(e.id), error: String(e.error ?? "Update failed.") }))
45
+ .filter((e) => Number.isFinite(e.id));
46
+ const bad = new Set(errors.map((e) => e.id));
47
+ return { ok: ids.filter((id) => !bad.has(id)), failed: errors };
48
+ }
49
+
50
+ export function BulkUpdateButton({ contactIds, onDone, variant = "outline", size = "sm", className }: BulkUpdateButtonProps) {
51
+ const [open, setOpen] = useState(false);
52
+ const [field, setField] = useState<BulkFieldKey>(BULK_FIELDS[0].key);
53
+ const [value, setValue] = useState("");
54
+ const [result, setResult] = useState<(BulkResult & { total: number; label: string }) | null>(null);
55
+ const label = BULK_FIELDS.find((f) => f.key === field)?.label ?? field;
56
+
57
+ const run = useG8Mutation(async (v: { ids: number[]; field: BulkFieldKey; value: string }): Promise<BulkResult> => {
58
+ const chunks: number[][] = [];
59
+ for (let i = 0; i < v.ids.length; i += CHUNK) chunks.push(v.ids.slice(i, i + CHUNK));
60
+ const settled = await Promise.allSettled(chunks.map((ids) => g8Post("/contacts/bulk-update", ids.map((id) => ({ id, [v.field]: v.value })))));
61
+ const out: BulkResult = { updated: [], failed: [], refusedChunks: 0 };
62
+ settled.forEach((s, i) => {
63
+ if (s.status === "rejected") { out.refusedChunks += 1; return; }
64
+ const o = chunkOutcome(chunks[i], s.value.data);
65
+ out.updated.push(...o.ok);
66
+ out.failed.push(...o.failed);
67
+ });
68
+ if (out.updated.length === 0 && out.refusedChunks === chunks.length && settled[0]?.status === "rejected") throw (settled[0] as PromiseRejectedResult).reason;
69
+ return out;
70
+ }, {
71
+ invalidates: [["contacts", "list"], ["contacts", "detail"], ["contacts", "record"]],
72
+ onSuccess: (r, v) => { setResult({ ...r, total: v.ids.length, label }); onDone?.(r.updated); },
73
+ });
74
+
75
+ const close = () => { setOpen(false); setValue(""); setResult(null); run.reset(); };
76
+ const count = contactIds.length;
77
+ const trimmed = value.trim();
78
+
79
+ return (
80
+ <>
81
+ <Button className={className} variant={variant} size={size} disabled={count === 0} onClick={() => setOpen(true)}><ListChecks className="size-4" aria-hidden />Bulk edit</Button>
82
+ <Dialog open={open} onOpenChange={(o) => (o ? setOpen(true) : close())}>
83
+ <DialogContent>
84
+ <DialogHeader>
85
+ <DialogTitle>Edit {plural(count, "contact")}</DialogTitle>
86
+ <DialogDescription>The same value is written to every selected contact. Existing values are replaced.</DialogDescription>
87
+ </DialogHeader>
88
+ {result ? (
89
+ <div role="status" className="text-sm">
90
+ <p className="font-medium">Updated {result.updated.length.toLocaleString()} of {result.total.toLocaleString()} ({result.label}).</p>
91
+ {result.failed.length > 0 ? <p className="mt-1 text-destructive">{plural(result.failed.length, "contact")} could not be updated: {[...new Set(result.failed.map((f) => f.error))].join("; ")}</p> : null}
92
+ {result.refusedChunks > 0 ? <p className="mt-1 text-destructive">{plural(result.refusedChunks, "batch", "batches")} of up to {CHUNK} was refused by the server. Those contacts are unchanged.</p> : null}
93
+ </div>
94
+ ) : (
95
+ <div className="flex flex-col gap-3">
96
+ <label className="flex flex-col gap-1 text-sm">Field
97
+ <select className="h-9 rounded-md border bg-background px-2" value={field} onChange={(e) => setField(e.target.value as BulkFieldKey)}>
98
+ {BULK_FIELDS.map((f) => <option key={f.key} value={f.key}>{f.label}</option>)}
99
+ </select>
100
+ </label>
101
+ <label className="flex flex-col gap-1 text-sm">New value
102
+ <input className="h-9 rounded-md border bg-background px-3" maxLength={200} value={value} onChange={(e) => setValue(e.target.value)} />
103
+ </label>
104
+ {run.error ? <ApiError error={run.error} title="Could not update the contacts" /> : null}
105
+ </div>
106
+ )}
107
+ <DialogFooter>
108
+ {result ? <Button onClick={close}>Done</Button> : (
109
+ <>
110
+ <Button variant="ghost" onClick={close}>Cancel</Button>
111
+ <Button loading={run.loading} disabled={!trimmed || count === 0} onClick={() => void run.mutate({ ids: [...contactIds], field, value: trimmed })}>Update {plural(count, "contact")}</Button>
112
+ </>
113
+ )}
114
+ </DialogFooter>
115
+ </DialogContent>
116
+ </Dialog>
117
+ </>
118
+ );
119
+ }
@@ -37,8 +37,9 @@ import { RecordLink } from "@/g8/components/screen/record-link";
37
37
  import { plural } from "@/g8/lib/plural";
38
38
  import { useG8Query } from "@/g8/hooks/use-g8-query";
39
39
  import { useUrlState } from "@/g8/hooks/use-url-state";
40
+ import { useUrlFilters } from "@/g8/hooks/use-url-filters";
41
+ import { MoreFilters, RangeFilter, TextFilter } from "@/g8/components/screen/filter-controls";
40
42
  import { g8Delete, g8Get } from "@/g8/lib/g8-client";
41
- import { useDebouncedValue } from "../hooks/use-debounced";
42
43
  import { usePageQuery } from "../hooks/use-page-query";
43
44
  import { companyName, errorStatus, location } from "../lib/crm-types";
44
45
  import type { CompanyRecord, ContactRecord, ScreenActionContext } from "../lib/crm-types";
@@ -67,8 +68,10 @@ export interface CompaniesScreenProps {
67
68
  className?: string;
68
69
  }
69
70
 
70
- interface Filters { name: string; domain: string; industry: string; country: string }
71
- const NO_FILTERS: Filters = { name: "", domain: "", industry: "", country: "" };
71
+ /** Every filter the companies API takes: text (name, domain, industry, country, state, city, description) and four from/to ranges. */
72
+ const FILTER_KEYS = ["name", "domain", "industry", "country", "state", "city", "description", "employee_count_min", "employee_count_max", "revenue_min", "revenue_max", "founded_year_min", "founded_year_max", "linkedin_followers_min", "linkedin_followers_max"] as const;
73
+ type FilterKey = (typeof FILTER_KEYS)[number];
74
+ const MORE_KEYS: readonly FilterKey[] = FILTER_KEYS.filter((k) => !["name", "domain", "industry", "country"].includes(k));
72
75
 
73
76
  function sortValue(row: CompanyRecord, key: string): string | null | undefined {
74
77
  if (key === "name") return row.name || row.domain;
@@ -91,8 +94,9 @@ export function CompaniesScreen(props: CompaniesScreenProps) {
91
94
  const parsedId = idParam ? Number(idParam) : null;
92
95
  /** A malformed deep link (`?id=abc`) opens nothing instead of fetching /companies/NaN. */
93
96
  const openId = parsedId != null && Number.isFinite(parsedId) ? parsedId : null;
94
- const [draft, setDraft] = useState<Filters>(NO_FILTERS);
95
- const filters = useDebouncedValue(draft);
97
+ // The filters live in the URL (`co_city=...`), so a filtered view survives reload, Back and sharing.
98
+ const urlFilters = useUrlFilters(FILTER_KEYS, { prefix: "co_" });
99
+ const { draft, filters } = urlFilters;
96
100
  const [sheet, setSheet] = useState<"new" | "dupes" | null>(null);
97
101
  const [confirmDelete, setConfirmDelete] = useState(false);
98
102
  const [note, setNote] = useState<string | null>(null);
@@ -103,15 +107,16 @@ export function CompaniesScreen(props: CompaniesScreenProps) {
103
107
  [filters],
104
108
  pageSize,
105
109
  ["companies", "list", "screen"],
110
+ urlFilters.ready,
106
111
  );
107
112
  const dupes = useG8Query((signal) => g8Get<{ pending?: number; count?: number }>("/duplicates/counts", { entity_type: "company" }, { signal }), [], { key: ["duplicates", "counts", "company"] });
108
113
  const selection = useRowSelection<CompanyRecord>(JSON.stringify(filters));
109
114
  const rows = useMemo(() => sortRows(list.rows, sort, sortValue), [list.rows, sort]);
110
- const hasFilters = Object.values(draft).some(Boolean);
115
+ const hasFilters = urlFilters.active;
111
116
  const pending = dupes.data?.data.pending ?? dupes.data?.data.count;
112
117
  const industries = new Set(list.rows.map((r) => r.industry).filter(Boolean)).size;
113
118
  const reload = () => { void list.refetch(); void dupes.refetch(); };
114
- const set = (key: keyof Filters, v: string) => setDraft((f) => ({ ...f, [key]: v }));
119
+ const set = (key: FilterKey, v: string) => urlFilters.set(key, v);
115
120
  const allOnPage = rows.length > 0 && rows.every((r) => selection.selected.has(r.id));
116
121
 
117
122
  const bulk = useBulkDelete("/companies", (gone, total) => {
@@ -157,7 +162,16 @@ export function CompaniesScreen(props: CompaniesScreenProps) {
157
162
  <input aria-label="Filter by domain" placeholder="Domain" className="h-9 w-36 rounded-md border bg-background px-3 text-sm" value={draft.domain} onChange={(e) => set("domain", e.target.value)} />
158
163
  <input aria-label="Filter by industry" placeholder="Industry" className="h-9 w-36 rounded-md border bg-background px-3 text-sm" value={draft.industry} onChange={(e) => set("industry", e.target.value)} />
159
164
  <input aria-label="Filter by country" placeholder="Country" className="h-9 w-32 rounded-md border bg-background px-3 text-sm" value={draft.country} onChange={(e) => set("country", e.target.value)} />
160
- {hasFilters ? <Button variant="ghost" size="sm" onClick={() => setDraft(NO_FILTERS)}>Clear filters</Button> : null}
165
+ <MoreFilters activeCount={MORE_KEYS.filter((k) => draft[k]).length}>
166
+ <TextFilter label="state" value={draft.state} onChange={(v) => set("state", v)} className="w-28" />
167
+ <TextFilter label="city" value={draft.city} onChange={(v) => set("city", v)} className="w-32" />
168
+ <TextFilter label="description" value={draft.description} onChange={(v) => set("description", v)} className="w-44" />
169
+ <RangeFilter label="Employees" min={draft.employee_count_min} max={draft.employee_count_max} onMinChange={(v) => set("employee_count_min", v)} onMaxChange={(v) => set("employee_count_max", v)} />
170
+ <RangeFilter label="Revenue" min={draft.revenue_min} max={draft.revenue_max} onMinChange={(v) => set("revenue_min", v)} onMaxChange={(v) => set("revenue_max", v)} />
171
+ <RangeFilter label="Founded" min={draft.founded_year_min} max={draft.founded_year_max} onMinChange={(v) => set("founded_year_min", v)} onMaxChange={(v) => set("founded_year_max", v)} />
172
+ <RangeFilter label="LinkedIn followers" min={draft.linkedin_followers_min} max={draft.linkedin_followers_max} onMinChange={(v) => set("linkedin_followers_min", v)} onMaxChange={(v) => set("linkedin_followers_max", v)} />
173
+ </MoreFilters>
174
+ {hasFilters ? <Button variant="ghost" size="sm" onClick={urlFilters.clear}>Clear filters</Button> : null}
161
175
  </>
162
176
  }
163
177
  />
@@ -210,7 +224,7 @@ export function CompaniesScreen(props: CompaniesScreenProps) {
210
224
  <EmptyState
211
225
  title={hasFilters ? "No companies match these filters" : "No companies yet"}
212
226
  description={hasFilters ? "Try fewer filters." : "Add a company, or import contacts and their companies are created from their domains."}
213
- action={hasFilters ? <Button variant="outline" onClick={() => setDraft(NO_FILTERS)}>Clear filters</Button> : <Button onClick={() => setSheet("new")}>New company</Button>}
227
+ action={hasFilters ? <Button variant="outline" onClick={urlFilters.clear}>Clear filters</Button> : <Button onClick={() => setSheet("new")}>New company</Button>}
214
228
  />
215
229
  ) : null}
216
230
  <Pager page={list.page} pageCount={list.pageCount} total={list.total} noun="companies" hasNext={list.hasNext} loading={list.loading} onPage={list.setPage} />
@@ -66,9 +66,9 @@ export function CompanyDetail({ companyId, onDeleted, onOpenContact, renderActio
66
66
  return (
67
67
  <div className={`flex flex-col gap-4 ${className ?? ""}`}>
68
68
  <header className="flex flex-wrap items-center gap-3">
69
- <div aria-hidden className="flex size-12 items-center justify-center rounded-md bg-muted text-sm font-semibold">{initials(name)}</div>
69
+ <div aria-hidden className="flex size-12 items-center justify-center rounded-md bg-muted text-sm font-medium">{initials(name)}</div>
70
70
  <div className="min-w-0 flex-1">
71
- <h2 className="truncate text-lg font-semibold">{name}</h2>
71
+ <h2 className="truncate text-lg font-medium">{name}</h2>
72
72
  <p className="truncate text-sm text-muted-foreground">{[company.domain, company.industry].filter(Boolean).join(" - ") || "No domain or industry"}</p>
73
73
  </div>
74
74
  <div className="flex flex-wrap items-center gap-2">
@@ -25,7 +25,7 @@ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
25
25
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
26
26
  import { useG8Query } from "@/g8/hooks/use-g8-query";
27
27
  import { g8Delete, g8Get } from "@/g8/lib/g8-client";
28
- import { contactName, initials, isMasked, location } from "../lib/crm-types";
28
+ import { contactName, contactSubtitle, initials, isMasked, location } from "../lib/crm-types";
29
29
  import type { ContactRecord } from "../lib/crm-types";
30
30
  import { ActivityTimeline } from "./activity-timeline";
31
31
  import { NotesPanel } from "./notes-panel";
@@ -75,11 +75,11 @@ export function ContactDetail({ contactId, onDeleted, onOpenCompany, renderActio
75
75
  return (
76
76
  <div className={`flex flex-col gap-4 ${className ?? ""}`}>
77
77
  <header className="flex flex-wrap items-center gap-3">
78
- <div aria-hidden className="flex size-12 items-center justify-center rounded-full bg-muted text-sm font-semibold">{initials(name)}</div>
78
+ <div aria-hidden className="flex size-12 items-center justify-center rounded-full bg-muted text-sm font-medium">{initials(name)}</div>
79
79
  <div className="min-w-0 flex-1">
80
- <h2 className="truncate text-lg font-semibold">{name}</h2>
80
+ <h2 className="truncate text-lg font-medium">{name}</h2>
81
81
  <p className="truncate text-sm text-muted-foreground">
82
- {[contact.job_title, contact.company?.name].filter(Boolean).join(" at ") || "No title or company"}
82
+ {contactSubtitle(contact)}
83
83
  </p>
84
84
  </div>
85
85
  <div className="flex flex-wrap items-center gap-2">