@graph8/sdk 0.362.0 → 0.373.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 (615) hide show
  1. package/README.md +33 -10
  2. package/blocks/_foundation/README.md +78 -1
  3. package/blocks/_foundation/block.json +123 -6
  4. package/blocks/_foundation/components/api-error.tsx +9 -7
  5. package/blocks/_foundation/components/brand-provider.tsx +23 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  7. package/blocks/_foundation/components/confirmed-action.tsx +90 -0
  8. package/blocks/_foundation/components/connect-graph8.tsx +280 -0
  9. package/blocks/_foundation/components/empty-state.tsx +5 -2
  10. package/blocks/_foundation/components/list-select.tsx +112 -0
  11. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  12. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  13. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  14. package/blocks/_foundation/components/screen/index.ts +2 -0
  15. package/blocks/_foundation/components/screen/record-link.tsx +76 -0
  16. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  17. package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
  18. package/blocks/_foundation/components/screen/time-series-chart.tsx +15 -97
  19. package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
  20. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  21. package/blocks/_foundation/hooks/use-brand.ts +52 -0
  22. package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
  23. package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
  24. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  25. package/blocks/_foundation/hooks/use-paginated.ts +120 -0
  26. package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
  27. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  28. package/blocks/_foundation/lib/g8-cache.ts +454 -0
  29. package/blocks/_foundation/lib/g8-client.ts +1 -1
  30. package/blocks/_foundation/lib/paging.ts +57 -0
  31. package/blocks/_foundation/lib/plural.ts +23 -0
  32. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  33. package/blocks/_foundation/server/connect-route.ts +15 -0
  34. package/blocks/_foundation/server/connect.ts +127 -0
  35. package/blocks/_foundation/server/local-key.ts +70 -0
  36. package/blocks/_foundation/server/proxy.ts +103 -3
  37. package/blocks/_foundation/server/route.ts +16 -4
  38. package/blocks/agents/README.md +93 -2
  39. package/blocks/agents/block.json +343 -2
  40. package/blocks/agents/components/agent-activity.tsx +112 -0
  41. package/blocks/agents/components/agent-approvals.tsx +135 -0
  42. package/blocks/agents/components/agent-builder.tsx +29 -10
  43. package/blocks/agents/components/agent-conversations.tsx +143 -0
  44. package/blocks/agents/components/agent-knowledge-panel.tsx +4 -3
  45. package/blocks/agents/components/agent-operators.tsx +164 -0
  46. package/blocks/agents/components/agent-run-detail.tsx +119 -0
  47. package/blocks/agents/components/agent-runs.tsx +19 -10
  48. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  49. package/blocks/agents/components/agent-test-history.tsx +121 -0
  50. package/blocks/agents/components/agents-screen.tsx +226 -0
  51. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  52. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  53. package/blocks/agents/components/from-template-button.tsx +65 -0
  54. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  55. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  56. package/blocks/agents/components/knowledge-form.tsx +69 -0
  57. package/blocks/agents/components/name-dialog.tsx +32 -0
  58. package/blocks/agents/components/receptionist-button.tsx +46 -0
  59. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  60. package/blocks/agents/components/skill-editor.tsx +147 -0
  61. package/blocks/agents/components/skill-library.tsx +111 -0
  62. package/blocks/agents/lib/agent-types.ts +7 -1
  63. package/blocks/agents/lib/facts.tsx +13 -0
  64. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  65. package/blocks/agents/lib/load-more.tsx +141 -0
  66. package/blocks/agents/lib/skill-utils.ts +91 -0
  67. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  68. package/blocks/analytics/README.md +13 -0
  69. package/blocks/analytics/block.json +45 -1
  70. package/blocks/analytics/components/analytics-screen.tsx +222 -0
  71. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  72. package/blocks/analytics/components/leaderboard.tsx +14 -7
  73. package/blocks/analytics/components/report-builder.tsx +38 -11
  74. package/blocks/analytics/components/report-detail.tsx +84 -0
  75. package/blocks/analytics/components/trend-chart.tsx +62 -0
  76. package/blocks/appointments/README.md +6 -0
  77. package/blocks/appointments/block.json +33 -2
  78. package/blocks/appointments/components/availability-editor.tsx +17 -5
  79. package/blocks/appointments/components/booking-page.tsx +2 -2
  80. package/blocks/appointments/components/event-type-editor.tsx +2 -1
  81. package/blocks/appointments/components/event-type-related.tsx +4 -2
  82. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  83. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  84. package/blocks/appointments/components/meeting-list.tsx +38 -18
  85. package/blocks/appointments/components/meetings-screen.tsx +7 -4
  86. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  87. package/blocks/appointments/components/scheduling-screen.tsx +2 -1
  88. package/blocks/block.schema.json +18 -0
  89. package/blocks/block.types.ts +17 -0
  90. package/blocks/campaigns/README.md +41 -11
  91. package/blocks/campaigns/block.json +105 -42
  92. package/blocks/campaigns/components/campaign-assets.tsx +17 -6
  93. package/blocks/campaigns/components/campaign-launch-step.tsx +114 -26
  94. package/blocks/campaigns/components/campaign-list.tsx +7 -3
  95. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  96. package/blocks/campaigns/components/campaign-wizard.tsx +52 -14
  97. package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
  98. package/blocks/campaigns/components/icp-detail.tsx +163 -0
  99. package/blocks/campaigns/components/icp-editor.tsx +22 -17
  100. package/blocks/campaigns/components/persona-detail.tsx +181 -0
  101. package/blocks/campaigns/components/persona-editor.tsx +19 -11
  102. package/blocks/campaigns/lib/campaigns-utils.ts +138 -5
  103. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
  104. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
  105. package/blocks/coach/README.md +10 -0
  106. package/blocks/coach/block.json +16 -1
  107. package/blocks/coach/components/call-scorecard.tsx +7 -7
  108. package/blocks/coach/components/coach-screen.tsx +222 -0
  109. package/blocks/company-intelligence/README.md +132 -0
  110. package/blocks/company-intelligence/block.json +318 -0
  111. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  112. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  113. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  114. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  115. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  116. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  117. package/blocks/company-intelligence/components/research-run.tsx +161 -0
  118. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  119. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  120. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  121. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  122. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  123. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  124. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  125. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  126. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  127. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  128. package/blocks/copilot/README.md +13 -0
  129. package/blocks/copilot/block.json +14 -1
  130. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  131. package/blocks/copilot/components/copilot-panel.tsx +14 -5
  132. package/blocks/copilot/components/copilot-screen.tsx +148 -0
  133. package/blocks/copilot/components/copilot-team-chat.tsx +13 -6
  134. package/blocks/crm/README.md +34 -0
  135. package/blocks/crm/block.json +107 -2
  136. package/blocks/crm/components/companies-screen.tsx +359 -0
  137. package/blocks/crm/components/company-detail.tsx +10 -8
  138. package/blocks/crm/components/company-table.tsx +11 -4
  139. package/blocks/crm/components/contact-detail.tsx +10 -7
  140. package/blocks/crm/components/contact-lookup-link.tsx +44 -0
  141. package/blocks/crm/components/contact-table.tsx +10 -3
  142. package/blocks/crm/components/contacts-screen.tsx +377 -0
  143. package/blocks/crm/components/dedupe-review.tsx +13 -6
  144. package/blocks/crm/components/import-wizard.tsx +7 -9
  145. package/blocks/crm/components/list-select.tsx +20 -0
  146. package/blocks/crm/components/notes-panel.tsx +4 -4
  147. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  148. package/blocks/crm/components/record-form.tsx +6 -4
  149. package/blocks/crm/components/task-list.tsx +4 -4
  150. package/blocks/crm/components/tasks-screen.tsx +399 -0
  151. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  152. package/blocks/crm/hooks/use-custom-fields.ts +6 -1
  153. package/blocks/crm/hooks/use-page-query.ts +9 -1
  154. package/blocks/crm/lib/crm-types.ts +26 -2
  155. package/blocks/crm/lib/read-lists.ts +16 -0
  156. package/blocks/crm/lib/screen-parts.tsx +105 -0
  157. package/blocks/deals/README.md +24 -6
  158. package/blocks/deals/block.json +160 -5
  159. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  160. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  161. package/blocks/deals/components/deal-detail.tsx +25 -13
  162. package/blocks/deals/components/deal-filters.tsx +65 -0
  163. package/blocks/deals/components/deal-line-items.tsx +120 -0
  164. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  165. package/blocks/deals/components/deal-related.tsx +31 -10
  166. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  167. package/blocks/deals/components/deals-screen.tsx +66 -26
  168. package/blocks/deals/components/forecast-view.tsx +4 -4
  169. package/blocks/deals/components/pipeline-board.tsx +8 -6
  170. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  171. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  172. package/blocks/deals/components/stage-editor.tsx +5 -5
  173. package/blocks/deals/hooks/use-pipeline-deals.ts +21 -4
  174. package/blocks/deals/hooks/use-pipelines.ts +1 -1
  175. package/blocks/deals/lib/deal-extras.ts +156 -0
  176. package/blocks/dialer/README.md +45 -4
  177. package/blocks/dialer/block.json +84 -3
  178. package/blocks/dialer/components/call-contact-button.tsx +269 -0
  179. package/blocks/dialer/components/call-log-screen.tsx +208 -0
  180. package/blocks/dialer/components/call-log.tsx +23 -7
  181. package/blocks/dialer/components/call-queue.tsx +4 -4
  182. package/blocks/dialer/components/dialer-screen.tsx +361 -0
  183. package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
  184. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  185. package/blocks/dialer/components/live-calls-table.tsx +7 -3
  186. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  187. package/blocks/dialer/components/session-start-form.tsx +61 -12
  188. package/blocks/dialer/components/softphone.tsx +4 -2
  189. package/blocks/enrichment/README.md +29 -7
  190. package/blocks/enrichment/block.json +49 -9
  191. package/blocks/enrichment/components/bulk-enrich-panel.tsx +83 -29
  192. package/blocks/enrichment/components/email-verify-badge.tsx +31 -11
  193. package/blocks/enrichment/components/enrich-button.tsx +32 -9
  194. package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
  195. package/blocks/enrichment/components/enrichment-screen.tsx +341 -0
  196. package/blocks/enrichment/components/waterfall-config.tsx +66 -17
  197. package/blocks/enrichment/lib/enrichment-types.ts +39 -0
  198. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  199. package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
  200. package/blocks/forms/README.md +8 -0
  201. package/blocks/forms/block.json +139 -18
  202. package/blocks/forms/components/form-activity.tsx +71 -0
  203. package/blocks/forms/components/form-backfill.tsx +108 -0
  204. package/blocks/forms/components/form-builder.tsx +4 -2
  205. package/blocks/forms/components/form-embed.tsx +4 -2
  206. package/blocks/forms/components/form-groups.tsx +151 -0
  207. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  208. package/blocks/forms/components/form-route-history.tsx +72 -0
  209. package/blocks/forms/components/form-saved-views.tsx +81 -0
  210. package/blocks/forms/components/forms-screen.tsx +56 -19
  211. package/blocks/forms/components/more-rows.tsx +30 -0
  212. package/blocks/forms/components/submissions-export.tsx +67 -0
  213. package/blocks/forms/components/submissions-table.tsx +17 -6
  214. package/blocks/forms/lib/forms-extra.ts +67 -0
  215. package/blocks/inbox/README.md +32 -3
  216. package/blocks/inbox/block.json +29 -2
  217. package/blocks/inbox/components/inbox-list.tsx +7 -3
  218. package/blocks/inbox/components/inbox-screen.tsx +294 -27
  219. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  220. package/blocks/inbox/components/reply-composer.tsx +65 -9
  221. package/blocks/inbox/components/tag-and-assign.tsx +4 -2
  222. package/blocks/inbox/components/thread-badge.tsx +2 -2
  223. package/blocks/inbox/components/thread-view.tsx +1 -1
  224. package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
  225. package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
  226. package/blocks/inbox/lib/inbox-types.ts +47 -1
  227. package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
  228. package/blocks/integrations/README.md +63 -1
  229. package/blocks/integrations/block.json +234 -15
  230. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  231. package/blocks/integrations/components/connection-health.tsx +59 -0
  232. package/blocks/integrations/components/crm-detail.tsx +119 -0
  233. package/blocks/integrations/components/crm-records.tsx +103 -0
  234. package/blocks/integrations/components/field-mappings.tsx +61 -0
  235. package/blocks/integrations/components/integration-list.tsx +13 -8
  236. package/blocks/integrations/components/integrations-screen.tsx +152 -0
  237. package/blocks/integrations/components/migration-panel.tsx +133 -0
  238. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  239. package/blocks/integrations/components/sync-status.tsx +11 -9
  240. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  241. package/blocks/integrations/components/webhook-detail.tsx +179 -0
  242. package/blocks/integrations/components/webhook-manager.tsx +43 -25
  243. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  244. package/blocks/integrations/lib/crm-utils.ts +39 -0
  245. package/blocks/integrations/lib/facts.tsx +13 -0
  246. package/blocks/integrations/lib/integrations-utils.ts +11 -1
  247. package/blocks/integrations/lib/load-more.tsx +141 -0
  248. package/blocks/intent/README.md +14 -0
  249. package/blocks/intent/block.json +27 -1
  250. package/blocks/intent/components/account-intent-score.tsx +5 -5
  251. package/blocks/intent/components/intent-feed.tsx +13 -10
  252. package/blocks/intent/components/intent-keyword-manager.tsx +16 -12
  253. package/blocks/intent/components/intent-screen.tsx +265 -0
  254. package/blocks/intent/components/keyword-detail.tsx +124 -0
  255. package/blocks/intent/lib/intent-utils.ts +4 -4
  256. package/blocks/lifecycle/block.json +13 -8
  257. package/blocks/lifecycle/components/account-related.tsx +8 -1
  258. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  259. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  260. package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
  261. package/blocks/lifecycle/components/lifecycle-screen.tsx +3 -1
  262. package/blocks/lifecycle/lib/lifecycle.ts +1 -1
  263. package/blocks/lists/README.md +20 -1
  264. package/blocks/lists/block.json +69 -3
  265. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  266. package/blocks/lists/components/add-to-list-button.tsx +20 -9
  267. package/blocks/lists/components/list-picker.tsx +17 -3
  268. package/blocks/lists/components/list-table.tsx +12 -6
  269. package/blocks/lists/components/lists-screen.tsx +346 -0
  270. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  271. package/blocks/lists/components/suppression-manager.tsx +33 -8
  272. package/blocks/lists/components/suppressions-screen.tsx +264 -0
  273. package/blocks/mailboxes/README.md +17 -0
  274. package/blocks/mailboxes/block.json +69 -17
  275. package/blocks/mailboxes/components/connect-mailbox.tsx +32 -21
  276. package/blocks/mailboxes/components/domain-detail.tsx +148 -0
  277. package/blocks/mailboxes/components/domain-purchase.tsx +20 -14
  278. package/blocks/mailboxes/components/mailbox-detail.tsx +157 -0
  279. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  280. package/blocks/mailboxes/components/mailboxes-screen.tsx +228 -0
  281. package/blocks/mailboxes/components/warmup-health.tsx +24 -22
  282. package/blocks/mailboxes/lib/mailboxes-utils.ts +26 -5
  283. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  284. package/blocks/newsletter/README.md +12 -0
  285. package/blocks/newsletter/block.json +157 -24
  286. package/blocks/newsletter/components/issue-scheduler.tsx +4 -3
  287. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  288. package/blocks/newsletter/components/more-rows.tsx +30 -0
  289. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  290. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  291. package/blocks/newsletter/components/newsletter-screen.tsx +39 -14
  292. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  293. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  294. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  295. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  296. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  297. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  298. package/blocks/nurture/block.json +28 -14
  299. package/blocks/nurture/components/nurture-builder.tsx +13 -3
  300. package/blocks/nurture/components/nurture-enroll.tsx +12 -10
  301. package/blocks/nurture/components/nurture-form.tsx +27 -10
  302. package/blocks/nurture/components/nurture-screen.tsx +26 -11
  303. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  304. package/blocks/nurture/lib/nurture-types.ts +1 -1
  305. package/blocks/objects/README.md +18 -1
  306. package/blocks/objects/block.json +42 -21
  307. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  308. package/blocks/objects/components/object-not-found.tsx +3 -1
  309. package/blocks/objects/components/object-record-detail.tsx +10 -2
  310. package/blocks/objects/components/object-schema-editor.tsx +4 -2
  311. package/blocks/objects/components/object-table.tsx +13 -3
  312. package/blocks/objects/components/objects-screen.tsx +147 -0
  313. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  314. package/blocks/objects/lib/object-types.ts +1 -1
  315. package/blocks/pages/README.md +14 -0
  316. package/blocks/pages/block.json +140 -23
  317. package/blocks/pages/components/brand-kit-editor.tsx +5 -2
  318. package/blocks/pages/components/content-library.tsx +9 -4
  319. package/blocks/pages/components/more-rows.tsx +30 -0
  320. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  321. package/blocks/pages/components/page-analytics.tsx +176 -0
  322. package/blocks/pages/components/page-clone.tsx +99 -0
  323. package/blocks/pages/components/page-domains.tsx +167 -0
  324. package/blocks/pages/components/page-editor.tsx +1 -1
  325. package/blocks/pages/components/page-generation-status.tsx +48 -0
  326. package/blocks/pages/components/page-list.tsx +2 -1
  327. package/blocks/pages/components/page-tracking.tsx +137 -0
  328. package/blocks/pages/components/pages-screen.tsx +57 -16
  329. package/blocks/pages/components/seo-report.tsx +6 -1
  330. package/blocks/pages/lib/studio.ts +7 -0
  331. package/blocks/phone-numbers/README.md +74 -0
  332. package/blocks/phone-numbers/block.json +262 -0
  333. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  334. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  335. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  336. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  337. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  338. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  339. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  340. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  341. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  342. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  343. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  344. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  345. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  346. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  347. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  348. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  349. package/blocks/pickers/README.md +23 -0
  350. package/blocks/pickers/block.json +103 -0
  351. package/blocks/pickers/components/company-picker.tsx +90 -0
  352. package/blocks/pickers/components/contact-picker.tsx +131 -0
  353. package/blocks/pickers/components/member-picker.tsx +99 -0
  354. package/blocks/pickers/components/search-picker.tsx +98 -0
  355. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  356. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  357. package/blocks/pickers/lib/use-debounced.ts +13 -0
  358. package/blocks/quotes/README.md +13 -0
  359. package/blocks/quotes/block.json +65 -13
  360. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  361. package/blocks/quotes/components/product-picker.tsx +3 -1
  362. package/blocks/quotes/components/quote-builder.tsx +1 -1
  363. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  364. package/blocks/quotes/components/quote-list.tsx +5 -3
  365. package/blocks/quotes/components/quote-related.tsx +8 -4
  366. package/blocks/quotes/components/quote-viewer.tsx +7 -2
  367. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  368. package/blocks/radar/README.md +10 -0
  369. package/blocks/radar/block.json +41 -1
  370. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  371. package/blocks/radar/components/competitor-detail.tsx +117 -0
  372. package/blocks/radar/components/competitor-tracker.tsx +3 -1
  373. package/blocks/radar/components/radar-overview.tsx +4 -4
  374. package/blocks/radar/components/radar-report.tsx +4 -2
  375. package/blocks/radar/components/radar-screen.tsx +214 -0
  376. package/blocks/radar/components/report-detail.tsx +104 -0
  377. package/blocks/search/README.md +10 -0
  378. package/blocks/search/block.json +16 -1
  379. package/blocks/search/components/filter-builder.tsx +3 -3
  380. package/blocks/search/components/prospect-results.tsx +5 -3
  381. package/blocks/search/components/prospect-search-screen.tsx +235 -0
  382. package/blocks/search/components/saved-searches.tsx +4 -1
  383. package/blocks/search/hooks/use-filter-options.ts +20 -6
  384. package/blocks/search/lib/search-types.ts +2 -0
  385. package/blocks/sequencer/README.md +55 -9
  386. package/blocks/sequencer/block.json +126 -19
  387. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  388. package/blocks/sequencer/components/enroll-dialog.tsx +11 -5
  389. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  390. package/blocks/sequencer/components/sequence-builder.tsx +224 -60
  391. package/blocks/sequencer/components/sequence-detail.tsx +261 -0
  392. package/blocks/sequencer/components/sequence-enrollments.tsx +116 -0
  393. package/blocks/sequencer/components/sequence-list.tsx +34 -9
  394. package/blocks/sequencer/components/sequence-stats.tsx +11 -10
  395. package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
  396. package/blocks/sequencer/components/sequences-screen.tsx +162 -0
  397. package/blocks/sequencer/components/step-editor.tsx +14 -12
  398. package/blocks/sequencer/lib/sequencer-utils.ts +133 -10
  399. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
  400. package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
  401. package/blocks/social-ads/README.md +19 -0
  402. package/blocks/social-ads/block.json +233 -1
  403. package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
  404. package/blocks/social-ads/components/ads-screen.tsx +152 -0
  405. package/blocks/social-ads/components/audience-sync-detail.tsx +194 -0
  406. package/blocks/social-ads/components/audience-sync.tsx +17 -9
  407. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  408. package/blocks/social-ads/components/campaign-detail.tsx +138 -0
  409. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  410. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  411. package/blocks/social-ads/components/creative-detail.tsx +155 -0
  412. package/blocks/social-ads/components/creative-gallery.tsx +35 -7
  413. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  414. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  415. package/blocks/social-ads/components/post-composer.tsx +7 -3
  416. package/blocks/social-ads/components/post-detail.tsx +163 -0
  417. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  418. package/blocks/social-ads/components/social-performance.tsx +80 -0
  419. package/blocks/social-ads/components/social-post-list.tsx +11 -3
  420. package/blocks/social-ads/components/social-screen.tsx +187 -0
  421. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  422. package/blocks/surveys/README.md +91 -0
  423. package/blocks/surveys/block.json +299 -0
  424. package/blocks/surveys/components/account-nps.tsx +69 -0
  425. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  426. package/blocks/surveys/components/org-summary.tsx +82 -0
  427. package/blocks/surveys/components/sending-domains.tsx +113 -0
  428. package/blocks/surveys/components/survey-form.tsx +126 -0
  429. package/blocks/surveys/components/survey-responses.tsx +145 -0
  430. package/blocks/surveys/components/survey-results.tsx +72 -0
  431. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  432. package/blocks/surveys/components/survey-send.tsx +162 -0
  433. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  434. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  435. package/blocks/surveys/lib/surveys-types.ts +228 -0
  436. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  437. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  438. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  439. package/blocks/visitors/README.md +10 -0
  440. package/blocks/visitors/block.json +25 -1
  441. package/blocks/visitors/components/snippet-install.tsx +21 -10
  442. package/blocks/visitors/components/traffic-chart.tsx +4 -4
  443. package/blocks/visitors/components/visitor-company-card.tsx +26 -16
  444. package/blocks/visitors/components/visitor-detail.tsx +105 -0
  445. package/blocks/visitors/components/visitor-feed.tsx +16 -7
  446. package/blocks/visitors/components/visitors-screen.tsx +166 -0
  447. package/blocks/webchat/README.md +10 -0
  448. package/blocks/webchat/block.json +21 -7
  449. package/blocks/webchat/components/chat-widget-config.tsx +8 -5
  450. package/blocks/webchat/components/conversation-list.tsx +8 -4
  451. package/blocks/webchat/components/webchat-screen.tsx +245 -0
  452. package/blocks/workbench/README.md +11 -0
  453. package/blocks/workbench/block.json +48 -1
  454. package/blocks/workbench/components/column-recipe-editor.tsx +5 -3
  455. package/blocks/workbench/components/run-progress.tsx +1 -1
  456. package/blocks/workbench/components/workbench-grid.tsx +10 -4
  457. package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
  458. package/blocks/workbench/components/workbench-screen.tsx +202 -0
  459. package/blocks/workbench/components/workbench-sources.tsx +194 -0
  460. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  461. package/blocks/workflows/README.md +13 -0
  462. package/blocks/workflows/block.json +28 -9
  463. package/blocks/workflows/components/run-history.tsx +31 -9
  464. package/blocks/workflows/components/trigger-config.tsx +3 -1
  465. package/blocks/workflows/components/workflow-editor.tsx +4 -2
  466. package/blocks/workflows/components/workflow-list.tsx +13 -6
  467. package/blocks/workflows/components/workflows-screen.tsx +193 -0
  468. package/dist/cli.js +3109 -1977
  469. package/dist/errors.d.mts +34 -0
  470. package/dist/errors.d.ts +34 -0
  471. package/dist/errors.js +46 -0
  472. package/dist/errors.js.map +1 -0
  473. package/dist/errors.mjs +21 -0
  474. package/dist/errors.mjs.map +1 -0
  475. package/dist/index.browser.js +11 -5
  476. package/dist/index.browser.js.map +1 -1
  477. package/dist/index.browser.mjs +11 -5
  478. package/dist/index.browser.mjs.map +1 -1
  479. package/dist/index.d.mts +164 -25
  480. package/dist/index.d.ts +164 -25
  481. package/dist/index.js +11 -5
  482. package/dist/index.js.map +1 -1
  483. package/dist/index.mjs +11 -5
  484. package/dist/index.mjs.map +1 -1
  485. package/dist/react.browser.js +10 -4
  486. package/dist/react.browser.js.map +1 -1
  487. package/dist/react.browser.mjs +10 -4
  488. package/dist/react.browser.mjs.map +1 -1
  489. package/dist/react.d.mts +142 -5
  490. package/dist/react.d.ts +142 -5
  491. package/dist/react.js +10 -4
  492. package/dist/react.js.map +1 -1
  493. package/dist/react.mjs +10 -4
  494. package/dist/react.mjs.map +1 -1
  495. package/dist/types/blocks/add.d.ts +11 -4
  496. package/dist/types/blocks/brand.d.ts +122 -0
  497. package/dist/types/blocks/compose.d.ts +53 -0
  498. package/dist/types/blocks/create.d.ts +27 -20
  499. package/dist/types/blocks/doctor.d.ts +6 -1
  500. package/dist/types/blocks/flows.d.ts +7 -1
  501. package/dist/types/blocks/navfile.d.ts +35 -0
  502. package/dist/types/blocks/orgBrand.d.ts +34 -0
  503. package/dist/types/blocks/paths.d.ts +4 -0
  504. package/dist/types/blocks/prefixes.d.ts +37 -0
  505. package/dist/types/blocks/registry.d.ts +17 -0
  506. package/dist/types/blocks/screens.d.ts +53 -0
  507. package/dist/types/cli.d.ts +5 -0
  508. package/dist/types/errors.d.ts +33 -0
  509. package/dist/types/http.d.ts +2 -18
  510. package/dist/types/version.d.ts +2 -2
  511. package/docs/agents.md +15 -4
  512. package/docs/api/README.md +2 -2
  513. package/docs/api/by-path.md +1 -0
  514. package/docs/api/desk.d.ts +98 -2
  515. package/docs/api/desk.md +123 -1
  516. package/docs/api/inbox.d.ts +10 -2
  517. package/docs/api/inbox.md +10 -2
  518. package/docs/api/seo.d.ts +19 -1
  519. package/docs/api/seo.md +24 -1
  520. package/package.json +16 -2
  521. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  522. package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
  523. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  524. package/stacks/6sense.json +55 -17
  525. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  526. package/stacks/apollo/pages/companies.tsx.tmpl +33 -0
  527. package/stacks/apollo/pages/contacts.tsx.tmpl +51 -0
  528. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  529. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  530. package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
  531. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  532. package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
  533. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  534. package/stacks/apollo.json +147 -37
  535. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  536. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  537. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  538. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  539. package/stacks/calendly.json +64 -0
  540. package/stacks/clay/pages/companies.tsx.tmpl +31 -0
  541. package/stacks/clay/pages/contacts.tsx.tmpl +31 -0
  542. package/stacks/clay/pages/lists.tsx.tmpl +12 -0
  543. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  544. package/stacks/clay.json +73 -27
  545. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  546. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  547. package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
  548. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  549. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  550. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  551. package/stacks/gong.json +98 -35
  552. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  553. package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
  554. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  555. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  556. package/stacks/hootsuite.json +104 -0
  557. package/stacks/hubspot/pages/companies.tsx.tmpl +26 -0
  558. package/stacks/hubspot/pages/contacts.tsx.tmpl +45 -0
  559. package/stacks/hubspot/pages/deals.tsx.tmpl +20 -0
  560. package/stacks/hubspot/pages/inbox.tsx.tmpl +103 -0
  561. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  562. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  563. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  564. package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
  565. package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
  566. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  567. package/stacks/hubspot.json +181 -64
  568. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  569. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  570. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  571. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  572. package/stacks/intercom.json +52 -20
  573. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  574. package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
  575. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  576. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  577. package/stacks/mailchimp.json +68 -34
  578. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  579. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  580. package/stacks/outreach/pages/contacts.tsx.tmpl +46 -0
  581. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  582. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  583. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  584. package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
  585. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  586. package/stacks/outreach.json +106 -25
  587. package/stacks/pipedrive/pages/companies.tsx.tmpl +23 -0
  588. package/stacks/pipedrive/pages/contacts.tsx.tmpl +27 -0
  589. package/stacks/pipedrive/pages/deals.tsx.tmpl +20 -0
  590. package/stacks/pipedrive/pages/inbox.tsx.tmpl +103 -0
  591. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  592. package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
  593. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  594. package/stacks/pipedrive.json +117 -0
  595. package/stacks/salesforce/pages/companies.tsx.tmpl +26 -0
  596. package/stacks/salesforce/pages/contacts.tsx.tmpl +32 -0
  597. package/stacks/salesforce/pages/deals.tsx.tmpl +20 -0
  598. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  599. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  600. package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
  601. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  602. package/stacks/salesforce.json +85 -31
  603. package/stacks/zoominfo/pages/companies.tsx.tmpl +31 -0
  604. package/stacks/zoominfo/pages/contacts.tsx.tmpl +31 -0
  605. package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
  606. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  607. package/stacks/zoominfo.json +115 -0
  608. package/styles/styles.css +1 -1
  609. package/templates/next-app/README.md.tmpl +40 -9
  610. package/templates/next-app/app/globals.css.tmpl +349 -64
  611. package/templates/next-app/app/layout.tsx.tmpl +7 -2
  612. package/templates/next-app/components/app-shell.tsx.tmpl +104 -88
  613. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
  614. package/templates/next-app/dot-gitignore.tmpl +2 -0
  615. package/templates/next-app/package.json.tmpl +1 -1
@@ -8,32 +8,35 @@
8
8
  "generic records table for any object",
9
9
  "record detail with owners, relationships and change history",
10
10
  "optimistic concurrency on edits",
11
- "archive and restore with history kept"
11
+ "archive and restore with history kept",
12
+ "finished ObjectsScreen composing the picker, table, record detail and schema editor"
12
13
  ],
13
14
  "dependsOn": [
14
- "foundation"
15
+ "foundation",
16
+ "pickers"
15
17
  ],
16
18
  "operations": [
17
- "list_objects_objects_get",
18
- "create_object_objects_post",
19
- "get_object_objects__object_slug__get",
20
- "list_object_attributes_objects__object_slug__attributes_get",
21
- "update_object_objects__object_slug__patch",
22
- "create_attribute_objects__object_slug__attributes_post",
23
- "update_attribute_objects__object_slug__attributes__attr_slug__patch",
24
19
  "archive_attribute_objects__object_slug__attributes__attr_slug__delete",
25
20
  "archive_object_objects__object_slug__delete",
26
- "list_object_records_objects__object_slug__records_get",
27
- "create_object_record_objects__object_slug__records_post",
28
- "update_object_record_objects__object_slug__records__record_id__patch",
29
21
  "archive_object_record_objects__object_slug__records__record_id__delete",
30
- "get_object_record_objects__object_slug__records__record_id__get",
22
+ "assign_record_role_records__entity_type___entity_id__role_assignments_post",
23
+ "clear_record_role_records__entity_type___entity_id__role_assignments__role_slot__delete",
24
+ "create_attribute_objects__object_slug__attributes_post",
25
+ "create_object_objects_post",
26
+ "create_object_record_objects__object_slug__records_post",
27
+ "get_object_objects__object_slug__get",
31
28
  "get_object_record_changes_objects__object_slug__records__record_id__changes_get",
29
+ "get_object_record_objects__object_slug__records__record_id__get",
32
30
  "get_object_relationships_objects__object_slug__records__record_id__relationships_get",
33
- "restore_object_record_objects__object_slug__records__record_id__restore_post",
34
31
  "get_record_role_slots_records__entity_type___entity_id__role_slots_get",
35
- "assign_record_role_records__entity_type___entity_id__role_assignments_post",
36
- "clear_record_role_records__entity_type___entity_id__role_assignments__role_slot__delete"
32
+ "list_object_attributes_objects__object_slug__attributes_get",
33
+ "list_object_records_objects__object_slug__records_get",
34
+ "list_objects_objects_get",
35
+ "list_team_members_team_members_get",
36
+ "restore_object_record_objects__object_slug__records__record_id__restore_post",
37
+ "update_attribute_objects__object_slug__attributes__attr_slug__patch",
38
+ "update_object_objects__object_slug__patch",
39
+ "update_object_record_objects__object_slug__records__record_id__patch"
37
40
  ],
38
41
  "components": [
39
42
  {
@@ -103,8 +106,12 @@
103
106
  "name": "AttributeFieldInput",
104
107
  "file": "components/attribute-field-input.tsx",
105
108
  "purpose": "One form control chosen by attribute type (text, number, date, select, checkbox...).",
106
- "operations": [],
107
- "tiers": []
109
+ "operations": [
110
+ "list_team_members_team_members_get"
111
+ ],
112
+ "tiers": [
113
+ "read"
114
+ ]
108
115
  },
109
116
  {
110
117
  "name": "ObjectNotFound",
@@ -144,13 +151,25 @@
144
151
  "operations": [
145
152
  "get_record_role_slots_records__entity_type___entity_id__role_slots_get",
146
153
  "assign_record_role_records__entity_type___entity_id__role_assignments_post",
147
- "clear_record_role_records__entity_type___entity_id__role_assignments__role_slot__delete"
154
+ "clear_record_role_records__entity_type___entity_id__role_assignments__role_slot__delete",
155
+ "list_team_members_team_members_get"
148
156
  ],
149
157
  "tiers": [
150
158
  "read",
151
159
  "write",
152
160
  "destructive"
153
161
  ]
162
+ },
163
+ {
164
+ "name": "ObjectsScreen",
165
+ "file": "components/objects-screen.tsx",
166
+ "purpose": "Finished custom-objects workspace: object picker, records table, record detail panel and the schema editor in a sheet. No required props.",
167
+ "operations": [
168
+ "list_objects_objects_get"
169
+ ],
170
+ "tiers": [
171
+ "read"
172
+ ]
154
173
  }
155
174
  ],
156
175
  "hooks": [
@@ -208,7 +227,8 @@
208
227
  "objects:delete",
209
228
  "objects:manage",
210
229
  "objects:read",
211
- "objects:write"
230
+ "objects:write",
231
+ "team:read"
212
232
  ],
213
233
  "defaults": [
214
234
  {
@@ -238,6 +258,7 @@
238
258
  }
239
259
  ],
240
260
  "peerDependencies": {
241
- "react": ">=18"
261
+ "react": ">=18",
262
+ "lucide-react": ">=0.400.0"
242
263
  }
243
264
  }
@@ -5,10 +5,12 @@
5
5
  *
6
6
  * Purpose: render the right input for an attribute's type (text, number, date, select, checkbox...)
7
7
  * and report the raw draft value. Convert it with `fromInputValue` from ../lib/object-types.
8
- * SDK operations: none (presentational). Used by RecordFormDialog and ObjectRecordDetail.
8
+ * SDK operations: none of its own. A person attribute (`actor_reference`) uses the pickers block MemberPicker (GET /team-members,
9
+ * read) so nobody types a user id; the raw id stays under "Use an id instead". Used by RecordFormDialog and ObjectRecordDetail.
9
10
  */
10
11
 
11
12
  import { Input } from "@graph8/sdk/ui";
13
+ import { MemberPicker } from "@/g8/pickers/components/member-picker";
12
14
  import { selectOptions } from "../lib/object-types";
13
15
  import type { ObjectAttribute } from "../lib/object-types";
14
16
 
@@ -58,6 +60,17 @@ export function AttributeFieldInput({ attribute, value, onChange, disabled, id }
58
60
  </select>
59
61
  );
60
62
  }
63
+ if (attribute.attribute_type === "actor_reference" && !attribute.is_multiselect) {
64
+ return (
65
+ <div className="flex flex-col gap-1">
66
+ <MemberPicker id={fieldId} label={attribute.title} value={String(value) || null} disabled={disabled} placeholder="Choose a person" onChange={(m) => onChange(m.id)} />
67
+ <details className="text-xs">
68
+ <summary className="text-muted-foreground cursor-pointer">Use an id instead</summary>
69
+ <Input aria-label={`${attribute.title} user id`} placeholder="User id" className="mt-1 h-8 w-40" value={String(value)} disabled={disabled} onChange={(e) => onChange(e.target.value)} />
70
+ </details>
71
+ </div>
72
+ );
73
+ }
61
74
  const hint = attribute.is_multiselect
62
75
  ? "Separate several values with commas"
63
76
  : attribute.attribute_type === "record_reference"
@@ -15,6 +15,7 @@ import { EmptyState } from "@/g8/components/empty-state";
15
15
  import { g8Get } from "@/g8/lib/g8-client";
16
16
  import { useG8Query } from "@/g8/hooks/use-g8-query";
17
17
  import { isObjectsApiMissing } from "../lib/object-types";
18
+ import { useBrand } from "@/g8/hooks/use-brand";
18
19
 
19
20
  export interface ObjectNotFoundProps {
20
21
  /** What was not found, for the message: "record" or "object". */
@@ -24,10 +25,11 @@ export interface ObjectNotFoundProps {
24
25
  }
25
26
 
26
27
  export function ObjectNotFound({ what, error }: ObjectNotFoundProps) {
28
+ const brand = useBrand();
27
29
  const probe = useG8Query((signal) => g8Get<unknown[]>("/objects", {}, { signal }).then(() => "enabled" as const), []);
28
30
  if (probe.loading && !probe.data && !probe.error) return <p className="text-muted-foreground text-sm" aria-busy="true">Checking...</p>;
29
31
  if (isObjectsApiMissing(probe.error)) {
30
- return <EmptyState title="Custom objects are unavailable" description="The graph8 API at this base URL does not serve custom objects. Check G8_API_URL and that it points at a current graph8 backend, then reload." />;
32
+ return <EmptyState title="Custom objects are unavailable" description={`The ${brand.linkLabel} API at this base URL does not serve custom objects. Check G8_API_URL and that it points at a current ${brand.linkLabel} backend, then reload.`} />;
31
33
  }
32
34
  if (probe.error) return <ApiError error={probe.error} title="Could not check custom objects" onRetry={() => void probe.refetch()} />;
33
35
  return <EmptyState title={`This ${what} no longer exists`} description={`It may have been deleted, or the link is out of date. Go back and pick another ${what}.`} />;
@@ -17,6 +17,7 @@
17
17
  import { useEffect, useState } from "react";
18
18
  import { Button, Card, CardContent, CardHeader, CardTitle, Skeleton } from "@graph8/sdk/ui";
19
19
  import { ApiError } from "@/g8/components/api-error";
20
+ import { FirstNotice } from "@/g8/components/paging-notices";
20
21
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
21
22
  import { EmptyState } from "@/g8/components/empty-state";
22
23
  import { g8Delete, g8Get, g8Post } from "@/g8/lib/g8-client";
@@ -37,6 +38,10 @@ export interface ObjectRecordDetailProps {
37
38
  onBack?: () => void;
38
39
  }
39
40
 
41
+ /** The relationships and history APIs have no paging: a full read is announced under the list. */
42
+ const CHANGE_LIMIT = 20;
43
+ const RELATION_LIMIT = 25;
44
+
40
45
  interface ChangeEntry { id: string; action: string; occurred_at: string; actor_type?: string; changes?: Record<string, unknown> }
41
46
  interface Relationship { id: string; relationship_label?: string; source_record_id: string; target_record_id: string; source_label?: string; target_label?: string }
42
47
 
@@ -59,7 +64,8 @@ export function ObjectRecordDetail({ objectSlug, recordId, onArchived, onBack }:
59
64
 
60
65
  if (isObjectsApiMissing(record.error)) return <ObjectNotFound what="record" error={record.error} />;
61
66
  if (record.error) return <ApiError error={record.error} title="Could not load the record" onRetry={() => void record.refetch()} />;
62
- if (!record.data || schema.loading) return <div className="grid gap-2" aria-busy="true"><Skeleton className="h-8 w-64" /><Skeleton className="h-40 w-full" /></div>;
67
+ // The hook keeps the previous record while the next loads; edit and archive act on recordId, so show only that record.
68
+ if (!record.data || String(record.data.id) !== String(recordId) || schema.loading) return <div className="grid gap-2" aria-busy="true"><Skeleton className="h-8 w-64" /><Skeleton className="h-40 w-full" /></div>;
63
69
 
64
70
  const current = record.data;
65
71
  const roleEntity = ROLE_ENTITY_TYPES[objectSlug];
@@ -69,7 +75,7 @@ export function ObjectRecordDetail({ objectSlug, recordId, onArchived, onBack }:
69
75
  <div className="flex items-center justify-between gap-2">
70
76
  <div>
71
77
  {onBack ? <Button variant="link" size="sm" onClick={onBack}>Back</Button> : null}
72
- <h2 className="text-lg font-semibold">{recordTitle(schema.definition, schema.attributes, current)}</h2>
78
+ <h2 className="text-lg font-medium">{recordTitle(schema.definition, schema.attributes, current)}</h2>
73
79
  {current.is_archived ? <p className="text-muted-foreground text-sm">This record is archived.</p> : null}
74
80
  </div>
75
81
  <div className="flex gap-2">
@@ -107,6 +113,7 @@ export function ObjectRecordDetail({ objectSlug, recordId, onArchived, onBack }:
107
113
  ))}
108
114
  </ul>
109
115
  )}
116
+ <FirstNotice shown={(relationships.data ?? []).length} limit={RELATION_LIMIT} noun="related records" />
110
117
  </CardContent>
111
118
  </Card>
112
119
  <Card>
@@ -125,6 +132,7 @@ export function ObjectRecordDetail({ objectSlug, recordId, onArchived, onBack }:
125
132
  ))}
126
133
  </ul>
127
134
  )}
135
+ <FirstNotice shown={(changes.data ?? []).length} limit={CHANGE_LIMIT} noun="changes" />
128
136
  </CardContent>
129
137
  </Card>
130
138
  <RecordFormDialog open={editing} objectSlug={objectSlug} singularNoun={noun} attributes={schema.attributes} record={current} onClose={() => setEditing(false)} onSaved={() => { setEditing(false); refreshAll(); }} />
@@ -27,6 +27,7 @@ import { useObjectSchema } from "../hooks/use-object-schema";
27
27
  import { OBJECT_ICONS, isObjectsApiMissing, isValidSlug, slugify } from "../lib/object-types";
28
28
  import type { ObjectAttribute, ObjectDefinition } from "../lib/object-types";
29
29
  import { AttributeFormDialog } from "./attribute-form-dialog";
30
+ import { useBrand } from "@/g8/hooks/use-brand";
30
31
 
31
32
  export interface ObjectSchemaEditorProps {
32
33
  /** Object to open first. */
@@ -93,6 +94,7 @@ function ObjectDetailsForm({ object, attributes, onSaved }: { object: ObjectDefi
93
94
  }
94
95
 
95
96
  export function ObjectSchemaEditor({ initialObjectSlug, onSelectObject }: ObjectSchemaEditorProps) {
97
+ const brand = useBrand();
96
98
  const [selected, setSelected] = useState<string | undefined>(initialObjectSlug);
97
99
  const [editing, setEditing] = useState<ObjectAttribute | "new" | null>(null);
98
100
  const [pending, setPending] = useState<Pending | null>(null);
@@ -113,7 +115,7 @@ export function ObjectSchemaEditor({ initialObjectSlug, onSelectObject }: Object
113
115
 
114
116
  if (objects.loading && !objects.data) return <div className="grid gap-2" aria-busy="true"><Skeleton className="h-8 w-48" /><Skeleton className="h-40 w-full" /></div>;
115
117
  if (isObjectsApiMissing(objects.error)) {
116
- return <EmptyState title="Custom objects are unavailable" description="The graph8 API at this base URL does not serve custom objects. Check G8_API_URL and that it points at a current graph8 backend, then reload." />;
118
+ return <EmptyState title="Custom objects are unavailable" description={`The ${brand.linkLabel} API at this base URL does not serve custom objects. Check G8_API_URL and that it points at a current ${brand.linkLabel} backend, then reload.`} />;
117
119
  }
118
120
  if (objects.error) return <ApiError error={objects.error} title="Could not load objects" onRetry={() => void objects.refetch()} />;
119
121
 
@@ -146,7 +148,7 @@ export function ObjectSchemaEditor({ initialObjectSlug, onSelectObject }: Object
146
148
  {schema.error ? <ApiError error={schema.error} title="Could not load the fields" onRetry={() => void schema.refetch()} /> : null}
147
149
  {!current.is_system ? <ObjectDetailsForm key={current.slug} object={current} attributes={schema.attributes} onSaved={() => void objects.refetch()} /> : <p className="text-muted-foreground text-sm">Standard objects can gain fields but cannot be renamed or archived.</p>}
148
150
  <div className="flex items-center justify-between">
149
- <h3 className="text-sm font-semibold">Fields</h3>
151
+ <h3 className="text-sm font-medium">Fields</h3>
150
152
  <Button size="sm" onClick={() => setEditing("new")}>Add field</Button>
151
153
  </div>
152
154
  {schema.loading ? <Skeleton className="h-24 w-full" /> : schema.attributes.length === 0 ? (
@@ -13,6 +13,7 @@
13
13
  */
14
14
 
15
15
  import { useEffect, useState } from "react";
16
+ import type { ReactNode } from "react";
16
17
  import { Button, Input, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@graph8/sdk/ui";
17
18
  import { ApiError } from "@/g8/components/api-error";
18
19
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
@@ -20,6 +21,8 @@ import { EmptyState } from "@/g8/components/empty-state";
20
21
  import { g8Delete, g8Get } from "@/g8/lib/g8-client";
21
22
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
22
23
  import { usePaginated } from "@/g8/hooks/use-paginated";
24
+ import { rowActivationProps } from "@/g8/hooks/use-row-activation";
25
+ import { RecordLink } from "@/g8/components/screen/record-link";
23
26
  import { useObjectSchema } from "../hooks/use-object-schema";
24
27
  import { formatValue, isObjectsApiMissing, recordTitle } from "../lib/object-types";
25
28
  import { ObjectNotFound } from "./object-not-found";
@@ -27,6 +30,8 @@ import type { ObjectRecord } from "../lib/object-types";
27
30
  import { RecordFormDialog } from "./record-form-dialog";
28
31
 
29
32
  export interface ObjectTableProps {
33
+ /** URL param the host screen reads for this record type. With it the name is a real link (Cmd-click opens it in a new tab); without it a plain button. */
34
+ linkParam?: string;
30
35
  objectSlug: string;
31
36
  /** Rows per request, 1-200. Default 50. */
32
37
  pageSize?: number;
@@ -36,7 +41,7 @@ export interface ObjectTableProps {
36
41
  maxColumns?: number;
37
42
  }
38
43
 
39
- export function ObjectTable({ objectSlug, pageSize = 50, onSelectRecord, maxColumns = 6 }: ObjectTableProps) {
44
+ export function ObjectTable({ linkParam, objectSlug, pageSize = 50, onSelectRecord, maxColumns = 6 }: ObjectTableProps) {
40
45
  const schema = useObjectSchema(objectSlug);
41
46
  const slug = encodeURIComponent(objectSlug);
42
47
  const records = usePaginated<ObjectRecord>(
@@ -68,6 +73,11 @@ export function ObjectTable({ objectSlug, pageSize = 50, onSelectRecord, maxColu
68
73
  return <ApiError error={schema.error ?? records.error} title="Could not load records" onRetry={() => { void schema.refetch(); void records.reload(); }} />;
69
74
  }
70
75
 
76
+ const cellText = (c: (typeof columns)[number], i: number, record: ObjectRecord): ReactNode => {
77
+ const text = formatValue(c, record.values?.[c.slug]) || (i === 0 ? recordTitle(schema.definition, schema.attributes, record) : "");
78
+ return i === 0 && onSelectRecord && text ? <RecordLink param={linkParam} id={record.id} onOpen={() => onSelectRecord(record)}>{text}</RecordLink> : text;
79
+ };
80
+
71
81
  return (
72
82
  <div className="grid gap-3">
73
83
  <div className="flex items-center justify-between gap-2">
@@ -92,9 +102,9 @@ export function ObjectTable({ objectSlug, pageSize = 50, onSelectRecord, maxColu
92
102
  </TableHeader>
93
103
  <TableBody>
94
104
  {rows.map((record) => (
95
- <TableRow key={record.id} className={onSelectRecord ? "cursor-pointer" : undefined} onClick={() => onSelectRecord?.(record)}>
105
+ <TableRow key={record.id} className={onSelectRecord ? "cursor-pointer" : undefined} {...(onSelectRecord ? rowActivationProps(() => onSelectRecord(record)) : {})}>
96
106
  {columns.map((c, i) => (
97
- <TableCell key={c.slug}>{formatValue(c, record.values?.[c.slug]) || (i === 0 ? recordTitle(schema.definition, schema.attributes, record) : "")}</TableCell>
107
+ <TableCell key={c.slug}>{cellText(c, i, record)}</TableCell>
98
108
  ))}
99
109
  <TableCell className="text-right" onClick={(e) => e.stopPropagation()}>
100
110
  <Button variant="ghost" size="sm" onClick={() => setForm({ record })}>Edit</Button>
@@ -0,0 +1,147 @@
1
+ "use client";
2
+
3
+ /**
4
+ * ObjectsScreen: the finished custom-objects workspace (HubSpot custom objects / Salesforce objects). Pick an object, work its
5
+ * records, open one for its values, owners, relationships and history, and define new objects and fields. No props are required.
6
+ *
7
+ * Composes: ScreenLayout, StatStrip, SplitView, FormSheet (screen kit) with ObjectTable, ObjectRecordDetail and
8
+ * ObjectSchemaEditor (this block). Standard objects (contacts, companies, deals) appear next to your own.
9
+ *
10
+ * Flows: switch between objects (deep link ?object=) - search the loaded rows, load more - create, edit and archive a record
11
+ * - open a record (deep link ?id=, full page with &view=full) to see values, owners, relationships and changes, edit, archive or restore it - define a
12
+ * new object, add, edit and archive fields, rename an object ("Define objects", needs an Admin key with objects:manage).
13
+ *
14
+ * SDK operations: none of its own. listObjects (picker, stats) is `read`; everything else runs inside the parts it composes,
15
+ * and every archive (record, field, object) goes through ConfirmSpendDialog. A 404 on the list call means the backend does not
16
+ * serve custom objects; the screen says so instead of showing an empty picker.
17
+ */
18
+
19
+ import { useState } from "react";
20
+ import type { ReactNode } from "react";
21
+ import { Button, Skeleton } from "@graph8/sdk/ui";
22
+ import { Box, Maximize2, Minimize2, Settings2 } from "lucide-react";
23
+ import { ApiError } from "@/g8/components/api-error";
24
+ import { EmptyState } from "@/g8/components/empty-state";
25
+ import { FormSheet } from "@/g8/components/screen/form-sheet";
26
+ import { ScreenLayout } from "@/g8/components/screen/screen-layout";
27
+ import { SplitView } from "@/g8/components/screen/split-view";
28
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
29
+ import { useUrlState } from "@/g8/hooks/use-url-state";
30
+ import { g8Get } from "@/g8/lib/g8-client";
31
+ import { useObjectSchema } from "../hooks/use-object-schema";
32
+ import { isObjectsApiMissing } from "../lib/object-types";
33
+ import type { ObjectDefinition } from "../lib/object-types";
34
+ import { ObjectNotFound } from "./object-not-found";
35
+ import { ObjectRecordDetail } from "./object-record-detail";
36
+ import { ObjectSchemaEditor } from "./object-schema-editor";
37
+ import { ObjectTable } from "./object-table";
38
+
39
+ export interface ObjectsScreenProps {
40
+ /** Object to open when the URL names none. Default: the first non-standard object, else the first one. */
41
+ initialObjectSlug?: string;
42
+ /** Show "Define objects" (the schema editor). Default true; it needs an Admin key behind the proxy. */
43
+ allowSchemaEditing?: boolean;
44
+ /** Extra buttons in the page header. */
45
+ toolbarActions?: ReactNode;
46
+ className?: string;
47
+ }
48
+
49
+ export function ObjectsScreen({ initialObjectSlug, allowSchemaEditing = true, toolbarActions, className }: ObjectsScreenProps) {
50
+ const [objectParam, setObjectParam] = useUrlState("object");
51
+ const [recordId, setRecordIdRaw] = useUrlState("id");
52
+ const [view, setView] = useUrlState("view");
53
+ const full = view === "full" && Boolean(recordId);
54
+ const setRecordId = (v: string | null): void => { if (v == null) setView(null); setRecordIdRaw(v); };
55
+ const [schemaOpen, setSchemaOpen] = useState(false);
56
+ const [tableKey, setTableKey] = useState(0);
57
+ const objects = useG8Query((signal) => g8Get<ObjectDefinition[]>("/objects", {}, { signal }).then((r) => r.data ?? []), []);
58
+ const all = (objects.data ?? []).filter((o) => !o.is_archived);
59
+ const slug = objectParam || initialObjectSlug || (all.find((o) => !o.is_system) ?? all[0])?.slug || "";
60
+ const current = all.find((o) => o.slug === slug);
61
+ const schema = useObjectSchema(slug || undefined);
62
+
63
+ const closeSchema = (open: boolean): void => {
64
+ setSchemaOpen(open);
65
+ if (open) return;
66
+ void objects.refetch();
67
+ void schema.refetch();
68
+ setTableKey((k) => k + 1);
69
+ };
70
+ const pick = (next: string) => { setObjectParam(next); setRecordId(null); };
71
+
72
+ if (isObjectsApiMissing(objects.error)) return <ObjectNotFound what="object" error={objects.error} />;
73
+
74
+ return (
75
+ <ScreenLayout
76
+ className={className}
77
+ title={current?.plural_noun ?? "Objects"}
78
+ description="Records of any type you define, next to your standard contacts, companies and deals."
79
+ icon={<Box className="size-5" aria-hidden />}
80
+ actions={
81
+ <>
82
+ {toolbarActions}
83
+ {allowSchemaEditing ? <Button variant="outline" size="sm" onClick={() => setSchemaOpen(true)}><Settings2 className="size-4" aria-hidden />Define objects</Button> : null}
84
+ </>
85
+ }
86
+ stats={[
87
+ { label: "Objects", value: objects.loading && !objects.data ? "-" : all.length, hint: `${all.filter((o) => !o.is_system).length} custom` },
88
+ { label: "Fields on this object", value: slug && !schema.loading ? schema.attributes.length : "-" },
89
+ ]}
90
+ >
91
+ {objects.error ? <ApiError error={objects.error} title="Could not load objects" onRetry={() => void objects.refetch()} /> : null}
92
+ {objects.loading && !objects.data ? <Skeleton className="h-9 w-full" /> : null}
93
+
94
+ {all.length > 0 ? (
95
+ <div className="mb-3 flex flex-wrap gap-2" role="group" aria-label="Objects">
96
+ {all.map((o) => (
97
+ <Button key={o.slug} variant={o.slug === slug ? "default" : "outline"} size="sm" aria-pressed={o.slug === slug} onClick={() => pick(o.slug)}>
98
+ {o.plural_noun}{o.is_system ? <span className="ml-1 text-xs opacity-70">standard</span> : null}
99
+ </Button>
100
+ ))}
101
+ </div>
102
+ ) : null}
103
+
104
+ {!objects.loading && !objects.error && all.length === 0 ? (
105
+ <EmptyState
106
+ title="No objects yet"
107
+ description="Define an object such as an invoice or a shipment, give it fields, then add records."
108
+ action={allowSchemaEditing ? <Button onClick={() => setSchemaOpen(true)}>Define your first object</Button> : undefined}
109
+ />
110
+ ) : null}
111
+
112
+ {slug ? (
113
+ <SplitView
114
+ detailOpen={Boolean(recordId)}
115
+ expanded={full}
116
+ onExpandedChange={(e) => setView(e ? "full" : null)}
117
+ onCloseDetail={() => setRecordId(null)}
118
+ detailWidth="lg"
119
+ list={<ObjectTable linkParam="id" key={`${slug}:${tableKey}`} objectSlug={slug} onSelectRecord={(r) => setRecordId(r.id)} />}
120
+ detail={recordId ? (
121
+ <div className="flex flex-col gap-2">
122
+ <div className="flex justify-end">
123
+ <Button variant="ghost" size="sm" onClick={() => setView(full ? null : "full")}>
124
+ {full ? <Minimize2 className="size-4" aria-hidden /> : <Maximize2 className="size-4" aria-hidden />}{full ? "Back to split view" : "Open full page"}
125
+ </Button>
126
+ </div>
127
+ <ObjectRecordDetail
128
+ key={`${slug}:${recordId}`}
129
+ objectSlug={slug}
130
+ recordId={recordId}
131
+ onBack={() => setRecordId(null)}
132
+ onArchived={() => { setRecordId(null); setTableKey((k) => k + 1); }}
133
+ />
134
+ </div>
135
+ ) : null}
136
+ />
137
+ ) : null}
138
+
139
+ <FormSheet open={schemaOpen} onOpenChange={closeSchema} title="Define objects" description="Create objects, add fields, rename or archive them. Needs an Admin key.">
140
+ <ObjectSchemaEditor
141
+ initialObjectSlug={slug || undefined}
142
+ onSelectObject={pick}
143
+ />
144
+ </FormSheet>
145
+ </ScreenLayout>
146
+ );
147
+ }
@@ -8,7 +8,8 @@
8
8
  * 2. assignRecordRole (POST /records/{entity_type}/{entity_id}/role-assignments) write
9
9
  * 3. clearRecordRole (DELETE /records/{entity_type}/{entity_id}/role-assignments/{role_slot}) destructive -> ConfirmSpendDialog
10
10
  * Only the standard objects (contacts, companies, deals) have role slots; custom objects do not.
11
- * The API takes a user id, not a name: pass `userOptions` to give people a picker.
11
+ * The API takes a user id, not a name, so nobody types one: each slot has a MemberPicker (search teammates by name;
12
+ * GET /team-members, read). Pass `userOptions` for your own fixed list; a raw id still works under "Use an id instead".
12
13
  */
13
14
 
14
15
  import { useEffect, useState } from "react";
@@ -18,6 +19,7 @@ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
18
19
  import { g8Delete, g8Get, g8Post } from "@/g8/lib/g8-client";
19
20
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
20
21
  import { useG8Query } from "@/g8/hooks/use-g8-query";
22
+ import { MemberPicker } from "@/g8/pickers/components/member-picker";
21
23
 
22
24
  type Slot = "sdr" | "ae" | "csm";
23
25
  const SLOTS: ReadonlyArray<{ slot: Slot; label: string }> = [
@@ -32,7 +34,7 @@ interface RoleSlots { sdr?: SlotInfo | null; ae?: SlotInfo | null; csm?: SlotInf
32
34
  export interface RecordRolesPanelProps {
33
35
  entityType: "contact" | "company" | "deal";
34
36
  entityId: string;
35
- /** Optional people to pick from. Without it the user id is typed. */
37
+ /** Optional people to pick from (your own list). Without it a MemberPicker searches the team. */
36
38
  userOptions?: ReadonlyArray<{ id: string; name: string }>;
37
39
  }
38
40
 
@@ -40,10 +42,11 @@ export function RecordRolesPanel({ entityType, entityId, userOptions }: RecordRo
40
42
  const base = `/records/${entityType}/${encodeURIComponent(entityId)}`;
41
43
  const slots = useG8Query((signal) => g8Get<RoleSlots>(`${base}/role-slots`, {}, { signal }).then((r) => r.data), [base]);
42
44
  const [draft, setDraft] = useState<Record<string, string>>({});
45
+ const [draftNames, setDraftNames] = useState<Record<string, string>>({});
43
46
  const [clearing, setClearing] = useState<Slot | null>(null);
44
47
  const [busySlot, setBusySlot] = useState<Slot | null>(null);
45
48
  const assign = useG8Mutation((vars: { slot: Slot; userId: string }) => g8Post(`${base}/role-assignments`, { role_slot: vars.slot, user_id: vars.userId }), {
46
- onSuccess: (_r, vars) => { setDraft((d) => ({ ...d, [vars.slot]: "" })); void slots.refetch(); },
49
+ onSuccess: (_r, vars) => { setDraft((d) => ({ ...d, [vars.slot]: "" })); setDraftNames((n) => ({ ...n, [vars.slot]: "" })); void slots.refetch(); },
47
50
  });
48
51
  const assignSlot = async (slot: Slot, userId: string): Promise<void> => {
49
52
  setBusySlot(slot);
@@ -53,6 +56,7 @@ export function RecordRolesPanel({ entityType, entityId, userOptions }: RecordRo
53
56
  // Typed ids and open dialogs belong to ONE record.
54
57
  useEffect(() => {
55
58
  setDraft({});
59
+ setDraftNames({});
56
60
  setClearing(null);
57
61
  assign.reset();
58
62
  clear.reset();
@@ -65,7 +69,7 @@ export function RecordRolesPanel({ entityType, entityId, userOptions }: RecordRo
65
69
  const canModify = slots.data != null && slots.data.can_modify !== false;
66
70
  return (
67
71
  <section aria-label="Owners" className="grid gap-2">
68
- <h3 className="text-sm font-semibold">Owners</h3>
72
+ <h3 className="text-sm font-medium">Owners</h3>
69
73
  {slots.loading && !slots.data ? <p className="text-muted-foreground text-sm">Loading owners...</p> : null}
70
74
  {SLOTS.map(({ slot, label }) => {
71
75
  const holder = slots.data?.[slot];
@@ -81,7 +85,13 @@ export function RecordRolesPanel({ entityType, entityId, userOptions }: RecordRo
81
85
  {userOptions.map((u) => <option key={u.id} value={u.id}>{u.name}</option>)}
82
86
  </select>
83
87
  ) : (
84
- <Input aria-label={`${label} user id`} placeholder="User id" className="h-8 w-40" value={draft[slot] ?? ""} onChange={(e) => setDraft((d) => ({ ...d, [slot]: e.target.value }))} />
88
+ <>
89
+ <MemberPicker label={`${label} person`} value={draft[slot] || null} selectedName={draftNames[slot]} placeholder="Choose a person" onChange={(m) => { setDraft((d) => ({ ...d, [slot]: m.id })); setDraftNames((n) => ({ ...n, [slot]: m.name })); }} />
90
+ <details className="text-xs">
91
+ <summary className="text-muted-foreground cursor-pointer">Use an id instead</summary>
92
+ <Input aria-label={`${label} user id`} placeholder="User id" className="mt-1 h-8 w-40" value={draft[slot] ?? ""} onChange={(e) => { setDraft((d) => ({ ...d, [slot]: e.target.value })); setDraftNames((n) => ({ ...n, [slot]: "" })); }} />
93
+ </details>
94
+ </>
85
95
  )}
86
96
  <Button size="sm" variant="outline" loading={busySlot === slot} disabled={!draft[slot] || busySlot !== null} onClick={() => void assignSlot(slot, draft[slot])}>Assign</Button>
87
97
  {holder ? <Button size="sm" variant="ghost" onClick={() => { clear.reset(); setClearing(slot); }}>Clear</Button> : null}
@@ -8,7 +8,7 @@
8
8
  * an attribute definition into the right input, display text and API value.
9
9
  */
10
10
 
11
- import { G8Error } from "@graph8/sdk";
11
+ import { G8Error } from "@graph8/sdk/errors";
12
12
 
13
13
  export type AttributeType =
14
14
  | "text" | "number" | "currency" | "date" | "timestamp" | "checkbox" | "select" | "status"
@@ -114,3 +114,17 @@ export default function Pages() { const router = useRouter(); return <PageList o
114
114
  - `recipes/edit-page-section.ts`: Change the text of one section of a landing page
115
115
  - `recipes/audit-site-seo.ts`: Run an SEO site audit and list what to fix first
116
116
  - `recipes/refresh-brand-kit.ts`: Re-scan the brand kit from the website
117
+
118
+ ## Depth added (C7c)
119
+
120
+ Landing pages now cover the whole life of a page, not only create and publish. Each open page has these tabs beside Edit and Details:
121
+
122
+ - **Publishing** (`PageDomains`): the URLs the page is live on, publish to a connected domain (domain, subdomain and path are sent together, behind a confirm), take one URL down (destructive confirm), download the HTML.
123
+ - **Tracking** (`PageTracking`): tracking mode, form label, chat widget, conversion goal (only changed fields are sent), a dedicated stream through Provision, reset consent (destructive confirm).
124
+ - **AI tools** (`PageAiTools`): edit by instruction, review (quick check is 0 credits), apply exact fixes for free, fix one finding with AI, write search metadata. Every AI pass is a billable confirm; the API quotes no price in advance, so the dialog says what runs.
125
+ - **Analytics** (`PageAnalytics`): visits, people, companies, sources and forms for 7/30/90 days and a traffic source; the top-level Analytics tab is portfolio-wide. People and companies page with Load more.
126
+ - "Clone a page" (`PageClone`, confirmed spend) rebuilds a URL or pasted HTML as a draft; `PageGenerationStatus` shows progress of an AI-built page.
127
+
128
+ The headline numbers read the API totals, and a page past the first 100 still opens with its real title.
129
+
130
+ Not covered (and why): HTML-level edits (`importLandingPageHtml`, `replaceLandingPageHtml`, `updateLandingPageHtml`, `setLandingPageText/Media`, element edits, anchors) need element ids a form cannot show; templates and components (`duplicateLandingPageTemplate`, `promoteLandingPageTemplate`, `ensureLandingPageBaseTemplate`, `extract components`) are template admin; `agent-sessions` and `getLandingPageConversation` are the hosted editor's chat; consent default (`/landing-page-consent/*`) is workspace admin; clone-homepage duplicates clone-url. Content calendar, programmatic pages and brand-kit template ops belong to later blocks.