@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.
- package/README.md +33 -10
- package/blocks/_foundation/README.md +78 -1
- package/blocks/_foundation/block.json +123 -6
- package/blocks/_foundation/components/api-error.tsx +9 -7
- package/blocks/_foundation/components/brand-provider.tsx +23 -0
- package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
- package/blocks/_foundation/components/confirmed-action.tsx +90 -0
- package/blocks/_foundation/components/connect-graph8.tsx +280 -0
- package/blocks/_foundation/components/empty-state.tsx +5 -2
- package/blocks/_foundation/components/list-select.tsx +112 -0
- package/blocks/_foundation/components/paging-notices.tsx +55 -0
- package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
- package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
- package/blocks/_foundation/components/screen/index.ts +2 -0
- package/blocks/_foundation/components/screen/record-link.tsx +76 -0
- package/blocks/_foundation/components/screen/split-view.tsx +1 -1
- package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
- package/blocks/_foundation/components/screen/time-series-chart.tsx +15 -97
- package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
- package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
- package/blocks/_foundation/hooks/use-brand.ts +52 -0
- package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
- package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
- package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
- package/blocks/_foundation/hooks/use-paginated.ts +120 -0
- package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
- package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
- package/blocks/_foundation/lib/g8-cache.ts +454 -0
- package/blocks/_foundation/lib/g8-client.ts +1 -1
- package/blocks/_foundation/lib/paging.ts +57 -0
- package/blocks/_foundation/lib/plural.ts +23 -0
- package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
- package/blocks/_foundation/server/connect-route.ts +15 -0
- package/blocks/_foundation/server/connect.ts +127 -0
- package/blocks/_foundation/server/local-key.ts +70 -0
- package/blocks/_foundation/server/proxy.ts +103 -3
- package/blocks/_foundation/server/route.ts +16 -4
- package/blocks/agents/README.md +93 -2
- package/blocks/agents/block.json +343 -2
- package/blocks/agents/components/agent-activity.tsx +112 -0
- package/blocks/agents/components/agent-approvals.tsx +135 -0
- package/blocks/agents/components/agent-builder.tsx +29 -10
- package/blocks/agents/components/agent-conversations.tsx +143 -0
- package/blocks/agents/components/agent-knowledge-panel.tsx +4 -3
- package/blocks/agents/components/agent-operators.tsx +164 -0
- package/blocks/agents/components/agent-run-detail.tsx +119 -0
- package/blocks/agents/components/agent-runs.tsx +19 -10
- package/blocks/agents/components/agent-skills-panel.tsx +7 -7
- package/blocks/agents/components/agent-test-history.tsx +121 -0
- package/blocks/agents/components/agents-screen.tsx +226 -0
- package/blocks/agents/components/assistant-sessions.tsx +150 -0
- package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
- package/blocks/agents/components/from-template-button.tsx +65 -0
- package/blocks/agents/components/knowledge-browser.tsx +107 -0
- package/blocks/agents/components/knowledge-detail.tsx +156 -0
- package/blocks/agents/components/knowledge-form.tsx +69 -0
- package/blocks/agents/components/name-dialog.tsx +32 -0
- package/blocks/agents/components/receptionist-button.tsx +46 -0
- package/blocks/agents/components/save-as-template-button.tsx +32 -0
- package/blocks/agents/components/skill-editor.tsx +147 -0
- package/blocks/agents/components/skill-library.tsx +111 -0
- package/blocks/agents/lib/agent-types.ts +7 -1
- package/blocks/agents/lib/facts.tsx +13 -0
- package/blocks/agents/lib/knowledge-utils.ts +49 -0
- package/blocks/agents/lib/load-more.tsx +141 -0
- package/blocks/agents/lib/skill-utils.ts +91 -0
- package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
- package/blocks/analytics/README.md +13 -0
- package/blocks/analytics/block.json +45 -1
- package/blocks/analytics/components/analytics-screen.tsx +222 -0
- package/blocks/analytics/components/kpi-tiles.tsx +1 -1
- package/blocks/analytics/components/leaderboard.tsx +14 -7
- package/blocks/analytics/components/report-builder.tsx +38 -11
- package/blocks/analytics/components/report-detail.tsx +84 -0
- package/blocks/analytics/components/trend-chart.tsx +62 -0
- package/blocks/appointments/README.md +6 -0
- package/blocks/appointments/block.json +33 -2
- package/blocks/appointments/components/availability-editor.tsx +17 -5
- package/blocks/appointments/components/booking-page.tsx +2 -2
- package/blocks/appointments/components/event-type-editor.tsx +2 -1
- package/blocks/appointments/components/event-type-related.tsx +4 -2
- package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
- package/blocks/appointments/components/meeting-detail.tsx +2 -2
- package/blocks/appointments/components/meeting-list.tsx +38 -18
- package/blocks/appointments/components/meetings-screen.tsx +7 -4
- package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
- package/blocks/appointments/components/scheduling-screen.tsx +2 -1
- package/blocks/block.schema.json +18 -0
- package/blocks/block.types.ts +17 -0
- package/blocks/campaigns/README.md +41 -11
- package/blocks/campaigns/block.json +105 -42
- package/blocks/campaigns/components/campaign-assets.tsx +17 -6
- package/blocks/campaigns/components/campaign-launch-step.tsx +114 -26
- package/blocks/campaigns/components/campaign-list.tsx +7 -3
- package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
- package/blocks/campaigns/components/campaign-wizard.tsx +52 -14
- package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
- package/blocks/campaigns/components/icp-detail.tsx +163 -0
- package/blocks/campaigns/components/icp-editor.tsx +22 -17
- package/blocks/campaigns/components/persona-detail.tsx +181 -0
- package/blocks/campaigns/components/persona-editor.tsx +19 -11
- package/blocks/campaigns/lib/campaigns-utils.ts +138 -5
- package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
- package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
- package/blocks/coach/README.md +10 -0
- package/blocks/coach/block.json +16 -1
- package/blocks/coach/components/call-scorecard.tsx +7 -7
- package/blocks/coach/components/coach-screen.tsx +222 -0
- package/blocks/company-intelligence/README.md +132 -0
- package/blocks/company-intelligence/block.json +318 -0
- package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
- package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
- package/blocks/company-intelligence/components/company-profile.tsx +68 -0
- package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
- package/blocks/company-intelligence/components/report-detail.tsx +189 -0
- package/blocks/company-intelligence/components/research-reports.tsx +136 -0
- package/blocks/company-intelligence/components/research-run.tsx +161 -0
- package/blocks/company-intelligence/components/source-detail.tsx +117 -0
- package/blocks/company-intelligence/components/source-view.tsx +274 -0
- package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
- package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
- package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
- package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
- package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
- package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
- package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
- package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
- package/blocks/copilot/README.md +13 -0
- package/blocks/copilot/block.json +14 -1
- package/blocks/copilot/components/copilot-memory.tsx +6 -2
- package/blocks/copilot/components/copilot-panel.tsx +14 -5
- package/blocks/copilot/components/copilot-screen.tsx +148 -0
- package/blocks/copilot/components/copilot-team-chat.tsx +13 -6
- package/blocks/crm/README.md +34 -0
- package/blocks/crm/block.json +107 -2
- package/blocks/crm/components/companies-screen.tsx +359 -0
- package/blocks/crm/components/company-detail.tsx +10 -8
- package/blocks/crm/components/company-table.tsx +11 -4
- package/blocks/crm/components/contact-detail.tsx +10 -7
- package/blocks/crm/components/contact-lookup-link.tsx +44 -0
- package/blocks/crm/components/contact-table.tsx +10 -3
- package/blocks/crm/components/contacts-screen.tsx +377 -0
- package/blocks/crm/components/dedupe-review.tsx +13 -6
- package/blocks/crm/components/import-wizard.tsx +7 -9
- package/blocks/crm/components/list-select.tsx +20 -0
- package/blocks/crm/components/notes-panel.tsx +4 -4
- package/blocks/crm/components/record-engagement-tab.tsx +103 -0
- package/blocks/crm/components/record-form.tsx +6 -4
- package/blocks/crm/components/task-list.tsx +4 -4
- package/blocks/crm/components/tasks-screen.tsx +399 -0
- package/blocks/crm/hooks/use-all-lists.ts +18 -0
- package/blocks/crm/hooks/use-custom-fields.ts +6 -1
- package/blocks/crm/hooks/use-page-query.ts +9 -1
- package/blocks/crm/lib/crm-types.ts +26 -2
- package/blocks/crm/lib/read-lists.ts +16 -0
- package/blocks/crm/lib/screen-parts.tsx +105 -0
- package/blocks/deals/README.md +24 -6
- package/blocks/deals/block.json +160 -5
- package/blocks/deals/components/close-deal-dialog.tsx +169 -0
- package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
- package/blocks/deals/components/deal-detail.tsx +25 -13
- package/blocks/deals/components/deal-filters.tsx +65 -0
- package/blocks/deals/components/deal-line-items.tsx +120 -0
- package/blocks/deals/components/deal-notes-tab.tsx +97 -0
- package/blocks/deals/components/deal-related.tsx +31 -10
- package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
- package/blocks/deals/components/deals-screen.tsx +66 -26
- package/blocks/deals/components/forecast-view.tsx +4 -4
- package/blocks/deals/components/pipeline-board.tsx +8 -6
- package/blocks/deals/components/pipeline-editor.tsx +132 -0
- package/blocks/deals/components/record-deals-tab.tsx +62 -0
- package/blocks/deals/components/stage-editor.tsx +5 -5
- package/blocks/deals/hooks/use-pipeline-deals.ts +21 -4
- package/blocks/deals/hooks/use-pipelines.ts +1 -1
- package/blocks/deals/lib/deal-extras.ts +156 -0
- package/blocks/dialer/README.md +45 -4
- package/blocks/dialer/block.json +84 -3
- package/blocks/dialer/components/call-contact-button.tsx +269 -0
- package/blocks/dialer/components/call-log-screen.tsx +208 -0
- package/blocks/dialer/components/call-log.tsx +23 -7
- package/blocks/dialer/components/call-queue.tsx +4 -4
- package/blocks/dialer/components/dialer-screen.tsx +361 -0
- package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
- package/blocks/dialer/components/disposition-panel.tsx +1 -1
- package/blocks/dialer/components/live-calls-table.tsx +7 -3
- package/blocks/dialer/components/record-calls-tab.tsx +101 -0
- package/blocks/dialer/components/session-start-form.tsx +61 -12
- package/blocks/dialer/components/softphone.tsx +4 -2
- package/blocks/enrichment/README.md +29 -7
- package/blocks/enrichment/block.json +49 -9
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +83 -29
- package/blocks/enrichment/components/email-verify-badge.tsx +31 -11
- package/blocks/enrichment/components/enrich-button.tsx +32 -9
- package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
- package/blocks/enrichment/components/enrichment-screen.tsx +341 -0
- package/blocks/enrichment/components/waterfall-config.tsx +66 -17
- package/blocks/enrichment/lib/enrichment-types.ts +39 -0
- package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
- package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
- package/blocks/forms/README.md +8 -0
- package/blocks/forms/block.json +139 -18
- package/blocks/forms/components/form-activity.tsx +71 -0
- package/blocks/forms/components/form-backfill.tsx +108 -0
- package/blocks/forms/components/form-builder.tsx +4 -2
- package/blocks/forms/components/form-embed.tsx +4 -2
- package/blocks/forms/components/form-groups.tsx +151 -0
- package/blocks/forms/components/form-mapping-editor.tsx +6 -9
- package/blocks/forms/components/form-route-history.tsx +72 -0
- package/blocks/forms/components/form-saved-views.tsx +81 -0
- package/blocks/forms/components/forms-screen.tsx +56 -19
- package/blocks/forms/components/more-rows.tsx +30 -0
- package/blocks/forms/components/submissions-export.tsx +67 -0
- package/blocks/forms/components/submissions-table.tsx +17 -6
- package/blocks/forms/lib/forms-extra.ts +67 -0
- package/blocks/inbox/README.md +32 -3
- package/blocks/inbox/block.json +29 -2
- package/blocks/inbox/components/inbox-list.tsx +7 -3
- package/blocks/inbox/components/inbox-screen.tsx +294 -27
- package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
- package/blocks/inbox/components/reply-composer.tsx +65 -9
- package/blocks/inbox/components/tag-and-assign.tsx +4 -2
- package/blocks/inbox/components/thread-badge.tsx +2 -2
- package/blocks/inbox/components/thread-view.tsx +1 -1
- package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
- package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
- package/blocks/inbox/lib/inbox-types.ts +47 -1
- package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
- package/blocks/integrations/README.md +63 -1
- package/blocks/integrations/block.json +234 -15
- package/blocks/integrations/components/ad-accounts.tsx +82 -0
- package/blocks/integrations/components/connection-health.tsx +59 -0
- package/blocks/integrations/components/crm-detail.tsx +119 -0
- package/blocks/integrations/components/crm-records.tsx +103 -0
- package/blocks/integrations/components/field-mappings.tsx +61 -0
- package/blocks/integrations/components/integration-list.tsx +13 -8
- package/blocks/integrations/components/integrations-screen.tsx +152 -0
- package/blocks/integrations/components/migration-panel.tsx +133 -0
- package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
- package/blocks/integrations/components/sync-status.tsx +11 -9
- package/blocks/integrations/components/sync-triggers.tsx +134 -0
- package/blocks/integrations/components/webhook-detail.tsx +179 -0
- package/blocks/integrations/components/webhook-manager.tsx +43 -25
- package/blocks/integrations/components/workspace-tools.tsx +127 -0
- package/blocks/integrations/lib/crm-utils.ts +39 -0
- package/blocks/integrations/lib/facts.tsx +13 -0
- package/blocks/integrations/lib/integrations-utils.ts +11 -1
- package/blocks/integrations/lib/load-more.tsx +141 -0
- package/blocks/intent/README.md +14 -0
- package/blocks/intent/block.json +27 -1
- package/blocks/intent/components/account-intent-score.tsx +5 -5
- package/blocks/intent/components/intent-feed.tsx +13 -10
- package/blocks/intent/components/intent-keyword-manager.tsx +16 -12
- package/blocks/intent/components/intent-screen.tsx +265 -0
- package/blocks/intent/components/keyword-detail.tsx +124 -0
- package/blocks/intent/lib/intent-utils.ts +4 -4
- package/blocks/lifecycle/block.json +13 -8
- package/blocks/lifecycle/components/account-related.tsx +8 -1
- package/blocks/lifecycle/components/churn-list.tsx +5 -2
- package/blocks/lifecycle/components/customer-table.tsx +1 -1
- package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
- package/blocks/lifecycle/components/lifecycle-screen.tsx +3 -1
- package/blocks/lifecycle/lib/lifecycle.ts +1 -1
- package/blocks/lists/README.md +20 -1
- package/blocks/lists/block.json +69 -3
- package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
- package/blocks/lists/components/add-to-list-button.tsx +20 -9
- package/blocks/lists/components/list-picker.tsx +17 -3
- package/blocks/lists/components/list-table.tsx +12 -6
- package/blocks/lists/components/lists-screen.tsx +346 -0
- package/blocks/lists/components/record-lists-tab.tsx +99 -0
- package/blocks/lists/components/suppression-manager.tsx +33 -8
- package/blocks/lists/components/suppressions-screen.tsx +264 -0
- package/blocks/mailboxes/README.md +17 -0
- package/blocks/mailboxes/block.json +69 -17
- package/blocks/mailboxes/components/connect-mailbox.tsx +32 -21
- package/blocks/mailboxes/components/domain-detail.tsx +148 -0
- package/blocks/mailboxes/components/domain-purchase.tsx +20 -14
- package/blocks/mailboxes/components/mailbox-detail.tsx +157 -0
- package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
- package/blocks/mailboxes/components/mailboxes-screen.tsx +228 -0
- package/blocks/mailboxes/components/warmup-health.tsx +24 -22
- package/blocks/mailboxes/lib/mailboxes-utils.ts +26 -5
- package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
- package/blocks/newsletter/README.md +12 -0
- package/blocks/newsletter/block.json +157 -24
- package/blocks/newsletter/components/issue-scheduler.tsx +4 -3
- package/blocks/newsletter/components/issue-versions.tsx +93 -0
- package/blocks/newsletter/components/more-rows.tsx +30 -0
- package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
- package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
- package/blocks/newsletter/components/newsletter-screen.tsx +39 -14
- package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
- package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
- package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
- package/blocks/newsletter/components/subscriber-table.tsx +5 -7
- package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
- package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
- package/blocks/nurture/block.json +28 -14
- package/blocks/nurture/components/nurture-builder.tsx +13 -3
- package/blocks/nurture/components/nurture-enroll.tsx +12 -10
- package/blocks/nurture/components/nurture-form.tsx +27 -10
- package/blocks/nurture/components/nurture-screen.tsx +26 -11
- package/blocks/nurture/components/nurture-stats.tsx +5 -4
- package/blocks/nurture/lib/nurture-types.ts +1 -1
- package/blocks/objects/README.md +18 -1
- package/blocks/objects/block.json +42 -21
- package/blocks/objects/components/attribute-field-input.tsx +14 -1
- package/blocks/objects/components/object-not-found.tsx +3 -1
- package/blocks/objects/components/object-record-detail.tsx +10 -2
- package/blocks/objects/components/object-schema-editor.tsx +4 -2
- package/blocks/objects/components/object-table.tsx +13 -3
- package/blocks/objects/components/objects-screen.tsx +147 -0
- package/blocks/objects/components/record-roles-panel.tsx +15 -5
- package/blocks/objects/lib/object-types.ts +1 -1
- package/blocks/pages/README.md +14 -0
- package/blocks/pages/block.json +140 -23
- package/blocks/pages/components/brand-kit-editor.tsx +5 -2
- package/blocks/pages/components/content-library.tsx +9 -4
- package/blocks/pages/components/more-rows.tsx +30 -0
- package/blocks/pages/components/page-ai-tools.tsx +196 -0
- package/blocks/pages/components/page-analytics.tsx +176 -0
- package/blocks/pages/components/page-clone.tsx +99 -0
- package/blocks/pages/components/page-domains.tsx +167 -0
- package/blocks/pages/components/page-editor.tsx +1 -1
- package/blocks/pages/components/page-generation-status.tsx +48 -0
- package/blocks/pages/components/page-list.tsx +2 -1
- package/blocks/pages/components/page-tracking.tsx +137 -0
- package/blocks/pages/components/pages-screen.tsx +57 -16
- package/blocks/pages/components/seo-report.tsx +6 -1
- package/blocks/pages/lib/studio.ts +7 -0
- package/blocks/phone-numbers/README.md +74 -0
- package/blocks/phone-numbers/block.json +262 -0
- package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
- package/blocks/phone-numbers/components/assign-number.tsx +104 -0
- package/blocks/phone-numbers/components/number-search.tsx +239 -0
- package/blocks/phone-numbers/components/number-settings.tsx +134 -0
- package/blocks/phone-numbers/components/number-usage.tsx +97 -0
- package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
- package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
- package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
- package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
- package/blocks/phone-numbers/components/release-number.tsx +126 -0
- package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
- package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
- package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
- package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
- package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
- package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
- package/blocks/pickers/README.md +23 -0
- package/blocks/pickers/block.json +103 -0
- package/blocks/pickers/components/company-picker.tsx +90 -0
- package/blocks/pickers/components/contact-picker.tsx +131 -0
- package/blocks/pickers/components/member-picker.tsx +99 -0
- package/blocks/pickers/components/search-picker.tsx +98 -0
- package/blocks/pickers/hooks/use-current-member.ts +104 -0
- package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
- package/blocks/pickers/lib/use-debounced.ts +13 -0
- package/blocks/quotes/README.md +13 -0
- package/blocks/quotes/block.json +65 -13
- package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
- package/blocks/quotes/components/product-picker.tsx +3 -1
- package/blocks/quotes/components/quote-builder.tsx +1 -1
- package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
- package/blocks/quotes/components/quote-list.tsx +5 -3
- package/blocks/quotes/components/quote-related.tsx +8 -4
- package/blocks/quotes/components/quote-viewer.tsx +7 -2
- package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
- package/blocks/radar/README.md +10 -0
- package/blocks/radar/block.json +41 -1
- package/blocks/radar/components/competitor-battle-card.tsx +1 -1
- package/blocks/radar/components/competitor-detail.tsx +117 -0
- package/blocks/radar/components/competitor-tracker.tsx +3 -1
- package/blocks/radar/components/radar-overview.tsx +4 -4
- package/blocks/radar/components/radar-report.tsx +4 -2
- package/blocks/radar/components/radar-screen.tsx +214 -0
- package/blocks/radar/components/report-detail.tsx +104 -0
- package/blocks/search/README.md +10 -0
- package/blocks/search/block.json +16 -1
- package/blocks/search/components/filter-builder.tsx +3 -3
- package/blocks/search/components/prospect-results.tsx +5 -3
- package/blocks/search/components/prospect-search-screen.tsx +235 -0
- package/blocks/search/components/saved-searches.tsx +4 -1
- package/blocks/search/hooks/use-filter-options.ts +20 -6
- package/blocks/search/lib/search-types.ts +2 -0
- package/blocks/sequencer/README.md +55 -9
- package/blocks/sequencer/block.json +126 -19
- package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
- package/blocks/sequencer/components/enroll-dialog.tsx +11 -5
- package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
- package/blocks/sequencer/components/sequence-builder.tsx +224 -60
- package/blocks/sequencer/components/sequence-detail.tsx +261 -0
- package/blocks/sequencer/components/sequence-enrollments.tsx +116 -0
- package/blocks/sequencer/components/sequence-list.tsx +34 -9
- package/blocks/sequencer/components/sequence-stats.tsx +11 -10
- package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
- package/blocks/sequencer/components/sequences-screen.tsx +162 -0
- package/blocks/sequencer/components/step-editor.tsx +14 -12
- package/blocks/sequencer/lib/sequencer-utils.ts +133 -10
- package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
- package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
- package/blocks/social-ads/README.md +19 -0
- package/blocks/social-ads/block.json +233 -1
- package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
- package/blocks/social-ads/components/ads-screen.tsx +152 -0
- package/blocks/social-ads/components/audience-sync-detail.tsx +194 -0
- package/blocks/social-ads/components/audience-sync.tsx +17 -9
- package/blocks/social-ads/components/campaign-ads.tsx +166 -0
- package/blocks/social-ads/components/campaign-detail.tsx +138 -0
- package/blocks/social-ads/components/campaign-editor.tsx +134 -0
- package/blocks/social-ads/components/creative-concepts.tsx +74 -0
- package/blocks/social-ads/components/creative-detail.tsx +155 -0
- package/blocks/social-ads/components/creative-gallery.tsx +35 -7
- package/blocks/social-ads/components/creative-revisions.tsx +57 -0
- package/blocks/social-ads/components/creative-tools.tsx +176 -0
- package/blocks/social-ads/components/post-composer.tsx +7 -3
- package/blocks/social-ads/components/post-detail.tsx +163 -0
- package/blocks/social-ads/components/social-ideas.tsx +64 -0
- package/blocks/social-ads/components/social-performance.tsx +80 -0
- package/blocks/social-ads/components/social-post-list.tsx +11 -3
- package/blocks/social-ads/components/social-screen.tsx +187 -0
- package/blocks/social-ads/lib/ads-extra.ts +67 -0
- package/blocks/surveys/README.md +91 -0
- package/blocks/surveys/block.json +299 -0
- package/blocks/surveys/components/account-nps.tsx +69 -0
- package/blocks/surveys/components/contact-survey-history.tsx +52 -0
- package/blocks/surveys/components/org-summary.tsx +82 -0
- package/blocks/surveys/components/sending-domains.tsx +113 -0
- package/blocks/surveys/components/survey-form.tsx +126 -0
- package/blocks/surveys/components/survey-responses.tsx +145 -0
- package/blocks/surveys/components/survey-results.tsx +72 -0
- package/blocks/surveys/components/survey-send-history.tsx +58 -0
- package/blocks/surveys/components/survey-send.tsx +162 -0
- package/blocks/surveys/components/survey-triggers.tsx +166 -0
- package/blocks/surveys/components/surveys-screen.tsx +234 -0
- package/blocks/surveys/lib/surveys-types.ts +228 -0
- package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
- package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
- package/blocks/surveys/recipes/survey-score-report.ts +27 -0
- package/blocks/visitors/README.md +10 -0
- package/blocks/visitors/block.json +25 -1
- package/blocks/visitors/components/snippet-install.tsx +21 -10
- package/blocks/visitors/components/traffic-chart.tsx +4 -4
- package/blocks/visitors/components/visitor-company-card.tsx +26 -16
- package/blocks/visitors/components/visitor-detail.tsx +105 -0
- package/blocks/visitors/components/visitor-feed.tsx +16 -7
- package/blocks/visitors/components/visitors-screen.tsx +166 -0
- package/blocks/webchat/README.md +10 -0
- package/blocks/webchat/block.json +21 -7
- package/blocks/webchat/components/chat-widget-config.tsx +8 -5
- package/blocks/webchat/components/conversation-list.tsx +8 -4
- package/blocks/webchat/components/webchat-screen.tsx +245 -0
- package/blocks/workbench/README.md +11 -0
- package/blocks/workbench/block.json +48 -1
- package/blocks/workbench/components/column-recipe-editor.tsx +5 -3
- package/blocks/workbench/components/run-progress.tsx +1 -1
- package/blocks/workbench/components/workbench-grid.tsx +10 -4
- package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
- package/blocks/workbench/components/workbench-screen.tsx +202 -0
- package/blocks/workbench/components/workbench-sources.tsx +194 -0
- package/blocks/workbench/components/workbench-table-list.tsx +14 -2
- package/blocks/workflows/README.md +13 -0
- package/blocks/workflows/block.json +28 -9
- package/blocks/workflows/components/run-history.tsx +31 -9
- package/blocks/workflows/components/trigger-config.tsx +3 -1
- package/blocks/workflows/components/workflow-editor.tsx +4 -2
- package/blocks/workflows/components/workflow-list.tsx +13 -6
- package/blocks/workflows/components/workflows-screen.tsx +193 -0
- package/dist/cli.js +3109 -1977
- package/dist/errors.d.mts +34 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.js +46 -0
- package/dist/errors.js.map +1 -0
- package/dist/errors.mjs +21 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.browser.js +11 -5
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +11 -5
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +164 -25
- package/dist/index.d.ts +164 -25
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -5
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +10 -4
- package/dist/react.browser.js.map +1 -1
- package/dist/react.browser.mjs +10 -4
- package/dist/react.browser.mjs.map +1 -1
- package/dist/react.d.mts +142 -5
- package/dist/react.d.ts +142 -5
- package/dist/react.js +10 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +10 -4
- package/dist/react.mjs.map +1 -1
- package/dist/types/blocks/add.d.ts +11 -4
- package/dist/types/blocks/brand.d.ts +122 -0
- package/dist/types/blocks/compose.d.ts +53 -0
- package/dist/types/blocks/create.d.ts +27 -20
- package/dist/types/blocks/doctor.d.ts +6 -1
- package/dist/types/blocks/flows.d.ts +7 -1
- package/dist/types/blocks/navfile.d.ts +35 -0
- package/dist/types/blocks/orgBrand.d.ts +34 -0
- package/dist/types/blocks/paths.d.ts +4 -0
- package/dist/types/blocks/prefixes.d.ts +37 -0
- package/dist/types/blocks/registry.d.ts +17 -0
- package/dist/types/blocks/screens.d.ts +53 -0
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/errors.d.ts +33 -0
- package/dist/types/http.d.ts +2 -18
- package/dist/types/version.d.ts +2 -2
- package/docs/agents.md +15 -4
- package/docs/api/README.md +2 -2
- package/docs/api/by-path.md +1 -0
- package/docs/api/desk.d.ts +98 -2
- package/docs/api/desk.md +123 -1
- package/docs/api/inbox.d.ts +10 -2
- package/docs/api/inbox.md +10 -2
- package/docs/api/seo.d.ts +19 -1
- package/docs/api/seo.md +24 -1
- package/package.json +16 -2
- package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
- package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
- package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
- package/stacks/6sense.json +55 -17
- package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
- package/stacks/apollo/pages/companies.tsx.tmpl +33 -0
- package/stacks/apollo/pages/contacts.tsx.tmpl +51 -0
- package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
- package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
- package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
- package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
- package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
- package/stacks/apollo.json +147 -37
- package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
- package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
- package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
- package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
- package/stacks/calendly.json +64 -0
- package/stacks/clay/pages/companies.tsx.tmpl +31 -0
- package/stacks/clay/pages/contacts.tsx.tmpl +31 -0
- package/stacks/clay/pages/lists.tsx.tmpl +12 -0
- package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
- package/stacks/clay.json +73 -27
- package/stacks/gong/pages/calls.tsx.tmpl +23 -0
- package/stacks/gong/pages/companies.tsx.tmpl +22 -0
- package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
- package/stacks/gong/pages/lists.tsx.tmpl +12 -0
- package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
- package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
- package/stacks/gong.json +98 -35
- package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
- package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
- package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
- package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
- package/stacks/hootsuite.json +104 -0
- package/stacks/hubspot/pages/companies.tsx.tmpl +26 -0
- package/stacks/hubspot/pages/contacts.tsx.tmpl +45 -0
- package/stacks/hubspot/pages/deals.tsx.tmpl +20 -0
- package/stacks/hubspot/pages/inbox.tsx.tmpl +103 -0
- package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
- package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
- package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
- package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
- package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
- package/stacks/hubspot.json +181 -64
- package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
- package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
- package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
- package/stacks/intercom.json +52 -20
- package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
- package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
- package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
- package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
- package/stacks/mailchimp.json +68 -34
- package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
- package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
- package/stacks/outreach/pages/contacts.tsx.tmpl +46 -0
- package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
- package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
- package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
- package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
- package/stacks/outreach.json +106 -25
- package/stacks/pipedrive/pages/companies.tsx.tmpl +23 -0
- package/stacks/pipedrive/pages/contacts.tsx.tmpl +27 -0
- package/stacks/pipedrive/pages/deals.tsx.tmpl +20 -0
- package/stacks/pipedrive/pages/inbox.tsx.tmpl +103 -0
- package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
- package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
- package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
- package/stacks/pipedrive.json +117 -0
- package/stacks/salesforce/pages/companies.tsx.tmpl +26 -0
- package/stacks/salesforce/pages/contacts.tsx.tmpl +32 -0
- package/stacks/salesforce/pages/deals.tsx.tmpl +20 -0
- package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
- package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
- package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
- package/stacks/salesforce.json +85 -31
- package/stacks/zoominfo/pages/companies.tsx.tmpl +31 -0
- package/stacks/zoominfo/pages/contacts.tsx.tmpl +31 -0
- package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
- package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
- package/stacks/zoominfo.json +115 -0
- package/styles/styles.css +1 -1
- package/templates/next-app/README.md.tmpl +40 -9
- package/templates/next-app/app/globals.css.tmpl +349 -64
- package/templates/next-app/app/layout.tsx.tmpl +7 -2
- package/templates/next-app/components/app-shell.tsx.tmpl +104 -88
- package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
- package/templates/next-app/dot-gitignore.tmpl +2 -0
- package/templates/next-app/package.json.tmpl +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RecordMeetingsTab: the meetings booked with one contact, past and upcoming, newest first, each opening in the meetings
|
|
5
|
+
* screen. Use it as an `extraDetailTabs` entry of ContactsScreen:
|
|
6
|
+
* { id: "meetings", label: "Meetings", render: (c) => <RecordMeetingsTab contactId={c.id} onOpenMeeting={(sel) => router.push(`/meetings?sel=${sel}`)} /> }
|
|
7
|
+
*
|
|
8
|
+
* SDK operations, in order:
|
|
9
|
+
* 1. getContactMeetings GET /contacts/{id}/meetings (read; booking events, the API answers the 50 most recent)
|
|
10
|
+
* Nothing here is billable, external or destructive. Book a new meeting from the scheduling screen.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Skeleton } from "@graph8/sdk/ui";
|
|
14
|
+
import { CalendarDays } from "lucide-react";
|
|
15
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
16
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
17
|
+
import { StatusPill } from "@/g8/components/screen/status-pill";
|
|
18
|
+
import type { Tone } from "@/g8/components/screen/status-pill";
|
|
19
|
+
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
20
|
+
import { g8Get } from "@/g8/lib/g8-client";
|
|
21
|
+
import { listOf } from "../lib/appointments-types";
|
|
22
|
+
|
|
23
|
+
/** One row of GET /contacts/{id}/meetings: a booking event. */
|
|
24
|
+
interface BookingEvent { event?: string | null; timestamp?: string | null; booking_id?: string | null; title?: string | null; start_time?: string | null; end_time?: string | null; attendees?: unknown }
|
|
25
|
+
|
|
26
|
+
/** The server returns at most this many events, newest first. */
|
|
27
|
+
export const MEETINGS_TAB_API_LIMIT = 50;
|
|
28
|
+
|
|
29
|
+
const EVENT: Record<string, { label: string; tone: Tone }> = {
|
|
30
|
+
"appointments.booking_created": { label: "Booked", tone: "info" },
|
|
31
|
+
"appointments.booking_rescheduled": { label: "Rescheduled", tone: "warning" },
|
|
32
|
+
"appointments.booking_cancelled": { label: "Cancelled", tone: "negative" },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function when(iso: string | null | undefined): string {
|
|
36
|
+
const d = iso ? new Date(iso) : null;
|
|
37
|
+
return d && !Number.isNaN(d.getTime()) ? d.toLocaleString(undefined, { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit" }) : "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function attendeeNames(a: unknown): string {
|
|
41
|
+
if (!Array.isArray(a)) return "";
|
|
42
|
+
return a.map((x) => (typeof x === "string" ? x : ((x as { name?: string; email?: string } | null)?.name || (x as { email?: string } | null)?.email || ""))).filter(Boolean).join(", ");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface RecordMeetingsTabProps {
|
|
46
|
+
contactId: number;
|
|
47
|
+
/** Route to the booking in your meetings screen; receives the `sel` value ("booking:<id>"). Without it the rows link to `${meetingsPath}?sel=booking:<id>`. */
|
|
48
|
+
onOpenMeeting?: (selection: string) => void;
|
|
49
|
+
/** Path of your meetings screen for the plain links. Default "/meetings". */
|
|
50
|
+
meetingsPath?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function RecordMeetingsTab({ contactId, onOpenMeeting, meetingsPath = "/meetings" }: RecordMeetingsTabProps) {
|
|
54
|
+
const q = useG8Query(async (signal) => listOf<BookingEvent>(await g8Get(`/contacts/${contactId}/meetings`, undefined, { signal }), "items"), [contactId]);
|
|
55
|
+
if (q.error) return <ApiError error={q.error} title="Could not load meetings" onRetry={() => void q.refetch()} />;
|
|
56
|
+
if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading meetings" />;
|
|
57
|
+
if (q.data.length === 0) return <EmptyState compact icon={<CalendarDays className="size-8" />} title="No meetings yet" description="Meetings booked with this contact show up here." />;
|
|
58
|
+
return (
|
|
59
|
+
<div className="space-y-2">
|
|
60
|
+
<ul className="divide-y rounded-md border" aria-label="Meetings">
|
|
61
|
+
{q.data.map((m, i) => {
|
|
62
|
+
const kind = EVENT[m.event ?? ""] ?? { label: "Booking", tone: "neutral" as Tone };
|
|
63
|
+
const selection = m.booking_id ? `booking:${m.booking_id}` : null;
|
|
64
|
+
const title = m.title || "Meeting";
|
|
65
|
+
const detail = [when(m.start_time ?? m.timestamp), attendeeNames(m.attendees)].filter(Boolean).join(" · ");
|
|
66
|
+
return (
|
|
67
|
+
<li key={`${m.booking_id ?? "event"}-${m.event}-${i}`} className="flex items-center justify-between gap-3 p-3 text-sm">
|
|
68
|
+
<div className="min-w-0">
|
|
69
|
+
{selection ? (
|
|
70
|
+
<a
|
|
71
|
+
href={`${meetingsPath}?sel=${encodeURIComponent(selection)}`}
|
|
72
|
+
className="block truncate font-medium hover:underline"
|
|
73
|
+
onClick={(e) => {
|
|
74
|
+
if (!onOpenMeeting || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
onOpenMeeting(selection);
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{title}
|
|
80
|
+
</a>
|
|
81
|
+
) : <span className="font-medium">{title}</span>}
|
|
82
|
+
{detail ? <p className="truncate text-xs text-muted-foreground">{detail}</p> : null}
|
|
83
|
+
</div>
|
|
84
|
+
<StatusPill tone={kind.tone} dot>{kind.label}</StatusPill>
|
|
85
|
+
</li>
|
|
86
|
+
);
|
|
87
|
+
})}
|
|
88
|
+
</ul>
|
|
89
|
+
{q.data.length >= MEETINGS_TAB_API_LIMIT ? <p role="note" className="text-xs text-muted-foreground">Showing the {MEETINGS_TAB_API_LIMIT} most recent booking events. Older ones are in the meetings screen.</p> : null}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -37,6 +37,7 @@ import { AvailabilityEditor } from "./availability-editor";
|
|
|
37
37
|
import { BookingPage } from "./booking-page";
|
|
38
38
|
import { EventTypeEditor } from "./event-type-editor";
|
|
39
39
|
import { EventTypeBookings, EventTypeOverview } from "./event-type-related";
|
|
40
|
+
import { rowActivationProps } from "@/g8/hooks/use-row-activation";
|
|
40
41
|
|
|
41
42
|
export interface SchedulingScreenProps {
|
|
42
43
|
/** Where a person connects a calendar (graph8 app). */
|
|
@@ -88,7 +89,7 @@ export function SchedulingScreen({ connectCalendarUrl, renderRowActions, renderD
|
|
|
88
89
|
<TableHeader><TableRow><TableHead>Event type</TableHead><TableHead className="text-right">Length</TableHead><TableHead>Rules</TableHead>{renderRowActions ? <TableHead /> : null}</TableRow></TableHeader>
|
|
89
90
|
<TableBody>
|
|
90
91
|
{types.eventTypes.map((t) => (
|
|
91
|
-
<TableRow key={t.id} className="cursor-pointer" data-state={String(t.id) === idParam ? "selected" : undefined}
|
|
92
|
+
<TableRow key={t.id} className="cursor-pointer" data-state={String(t.id) === idParam ? "selected" : undefined} {...rowActivationProps(() => setId(String(t.id)))}>
|
|
92
93
|
<TableCell><button type="button" className="font-medium hover:underline" onClick={(e) => { e.stopPropagation(); setId(String(t.id)); }}>{t.title}</button><div className="text-xs text-muted-foreground">/{t.slug}</div></TableCell>
|
|
93
94
|
<TableCell className="text-right tabular-nums">{t.length} min</TableCell>
|
|
94
95
|
<TableCell className="space-x-1">
|
package/blocks/block.schema.json
CHANGED
|
@@ -36,6 +36,24 @@
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
+
"screens": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"description": "Optional. How the block's finished screens become routes and sidebar entries in an app made with `create --blocks`. Absent: derived from the components named `*Screen` (ContactsScreen -> /contacts, label Contacts, group = the block title, icon from a built-in per-block map).",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"required": ["component", "path", "label"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"component": { "type": "string", "pattern": "^[A-Z][A-Za-z0-9]*$", "description": "A component of this block." },
|
|
48
|
+
"path": { "type": "string", "pattern": "^/[a-z0-9]+(-[a-z0-9]+)*(/[a-z0-9]+(-[a-z0-9]+)*)*$", "description": "Static route path. On a collision in a generated app it is prefixed with the block id." },
|
|
49
|
+
"label": { "type": "string", "minLength": 1 },
|
|
50
|
+
"icon": { "type": "string", "pattern": "^[A-Z][A-Za-z0-9]*$", "description": "A lucide-react export name." },
|
|
51
|
+
"group": { "type": "string", "minLength": 1, "description": "Sidebar section (default: the block title)." },
|
|
52
|
+
"description": { "type": "string", "minLength": 1 },
|
|
53
|
+
"order": { "type": "number", "description": "Sort position inside the block; lower first." }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
39
57
|
"hooks": {
|
|
40
58
|
"type": "array",
|
|
41
59
|
"items": {
|
package/blocks/block.types.ts
CHANGED
|
@@ -59,6 +59,21 @@ export interface BlockDefault {
|
|
|
59
59
|
askHuman: boolean;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/** Optional route metadata for a finished screen (`create --blocks`, `add` inside a created app). */
|
|
63
|
+
export interface BlockScreen {
|
|
64
|
+
/** A component of this block. */
|
|
65
|
+
component: string;
|
|
66
|
+
/** Static route path; prefixed with the block id when it collides in a generated app. */
|
|
67
|
+
path: string;
|
|
68
|
+
label: string;
|
|
69
|
+
/** lucide-react export name. */
|
|
70
|
+
icon?: string;
|
|
71
|
+
/** Sidebar section (default: the block title). */
|
|
72
|
+
group?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
order?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
export interface BlockManifest {
|
|
63
78
|
$schema?: string;
|
|
64
79
|
id: string;
|
|
@@ -71,6 +86,8 @@ export interface BlockManifest {
|
|
|
71
86
|
/** Contract operationIds (keys of API_OPERATIONS). */
|
|
72
87
|
operations: string[];
|
|
73
88
|
components: BlockComponent[];
|
|
89
|
+
/** Optional. Absent: derived from the components named `*Screen`. */
|
|
90
|
+
screens?: BlockScreen[];
|
|
74
91
|
hooks: BlockHook[];
|
|
75
92
|
lib?: BlockFile[];
|
|
76
93
|
server?: BlockServerFile[];
|
|
@@ -7,6 +7,26 @@ Apollo / HubSpot "campaigns" screen, with generation behind credit confirmations
|
|
|
7
7
|
Depends on: `lists` (the audience picker), `mailboxes` (senders and the connect-a-mailbox link). Install:
|
|
8
8
|
`npx @graph8/sdk add campaigns`.
|
|
9
9
|
|
|
10
|
+
## Screens
|
|
11
|
+
|
|
12
|
+
`CampaignsScreen` is the finished page: mount it as the route. Every record type has a complete detail view,
|
|
13
|
+
deep-linkable (`?tab=campaigns|profile|personas`, `?id=` the record, `?dtab=` the tab, `?doc=` a document).
|
|
14
|
+
|
|
15
|
+
- **Campaigns tab**: list (delete confirmed) beside a detail panel with Details (every field, audience, edit name,
|
|
16
|
+
goal and brief, confirmed), Metrics, Documents (each document opens with its fields and editor), Audience,
|
|
17
|
+
Sequence and Launch. "New campaign" runs the five-step wizard in a sheet.
|
|
18
|
+
- **Company profile tab**: the profile plus ICPs; `IcpDetail` shows every field and edits, pins or archives.
|
|
19
|
+
- **Personas tab**: `PersonaDetail` shows every field, the campaigns that target it, and edits, pins or archives.
|
|
20
|
+
|
|
21
|
+
Slot props: `toolbarActions`, `renderDetailActions(campaign)`, `extraDetailTabs`, `websiteUrl`. Prerequisites: a
|
|
22
|
+
company profile with the real website, an active mailbox, a contact list, and credits for AI generation.
|
|
23
|
+
`IcpEditor` and `PersonaEditor` gained optional `onOpen` and `refreshKey`; `CampaignAssets` optional
|
|
24
|
+
`documentId` and `onDocumentChange`.
|
|
25
|
+
|
|
26
|
+
Known limits: there is no single-ICP read, so `IcpDetail` finds the ICP in the first 200 (the API maximum).
|
|
27
|
+
`PersonaDetail` lists the campaigns whose `target_persona` matches the persona title among the first 100
|
|
28
|
+
campaigns; the API does not link campaigns to ICPs, so ICPs show none.
|
|
29
|
+
|
|
10
30
|
## Components
|
|
11
31
|
|
|
12
32
|
| Component | What it does | Tiers |
|
|
@@ -16,7 +36,7 @@ Depends on: `lists` (the audience picker), `mailboxes` (senders and the connect-
|
|
|
16
36
|
| `CampaignWizard` | A page. Idea (AI, polled) -> create -> audience -> steps -> launch. Also exports the three step components below. | all |
|
|
17
37
|
| `CampaignAudienceStep` | Attach or detach a contact list (uses the lists block `ListPicker`; reads `getCampaignFull`, since `getCampaign` omits the audience). | read, write, external |
|
|
18
38
|
| `CampaignSequenceStep` | Add, edit, remove and AI-write sequence steps. | read, billable, external, destructive |
|
|
19
|
-
| `CampaignLaunchStep` | Readiness checklist,
|
|
39
|
+
| `CampaignLaunchStep` | Readiness checklist (audience, steps, written copy, sender), deliverability and warm-up hold warnings, writes missing email copy with AI, confirmed launch. | read, billable, external |
|
|
20
40
|
| `CampaignList` | A page. All campaigns; open one for metrics and documents; delete. | read, billable, external, destructive |
|
|
21
41
|
| `CampaignAssets` | Campaign documents: read, edit, rewrite with AI, add, delete. Standalone: shows a campaign picker. | read, billable, external, destructive |
|
|
22
42
|
| `CampaignMetrics` | A page. Metrics for 7, 30, 90 or 365 days. Standalone: shows a campaign picker. | read |
|
|
@@ -40,15 +60,24 @@ read, so credit numbers are estimates: edit `CREDIT_ESTIMATES` in `lib/campaigns
|
|
|
40
60
|
3. **Ideas.** `generateCampaignIdeasAsync` (billable) -> `task_id` -> poll `getCampaignIdeasTaskStatus` every
|
|
41
61
|
2-3 seconds until `completed`; `ideas[]` carry `campaign_name`, `core_concept`, `primary_hook`.
|
|
42
62
|
4. **Create.** `createCampaign` -> `id`. Store it at once; later steps reuse it and "Create" becomes
|
|
43
|
-
"Save changes" (`updateCampaign`). With `auto_generate_documents`
|
|
44
|
-
background: poll `getCampaignGenerationStatus
|
|
63
|
+
"Save changes" (`updateCampaign`). With `auto_generate_documents` (on by default in the wizard and the
|
|
64
|
+
recipe) the documents generate in the background: poll `getCampaignGenerationStatus`; a `generation_error`
|
|
65
|
+
there, or `generation_status: "failed_to_dispatch"` on create, means the writing never started.
|
|
45
66
|
5. **Audience.** `getCampaignFull` shows what is attached (`audience_list_id`, `audience`). `ListPicker` (lists block) -> a list `id` -> `attachAudienceToCampaign` with `list_id` as a
|
|
46
67
|
string. Build the list in the search/lists blocks first.
|
|
47
68
|
6. **Steps and copy.** `getCampaignSequence` -> `createCampaignStep` (channel -> mode matrix: email -> email,
|
|
48
69
|
phone -> call|voicemail, sms -> sms, social -> connect_note|dm|comment|reply) -> `generateCampaignStepContent`
|
|
49
|
-
(billable) per step. `CampaignAssets` edits the documents behind them.
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
(billable) per step. `CampaignAssets` edits the documents behind them. Writing an EMAIL step needs the
|
|
71
|
+
campaign's `emails` document to exist already (created by `auto_generate_documents`); without it the step
|
|
72
|
+
write answers 404.
|
|
73
|
+
7. **Launch.** `CampaignLaunchStep` -> `getCampaignFull` (audience attached with its size, >= 1 step, written copy,
|
|
74
|
+
`is_launched`), `listMailboxes` (active ones), `getSafeToSend` (`safe`, a warning) -> `launchCampaign` with
|
|
75
|
+
`sender_mailbox_ids`. The launch is refused (400 "no completed copy documents") unless a document other than
|
|
76
|
+
`sequence` and `step_catalog` has `status: "completed"`, so Launch stays disabled until one does; "Write the
|
|
77
|
+
email copy with AI" creates the `emails` document if missing (`createCampaignDocument`, once) and has AI write it
|
|
78
|
+
(`regenerateCampaignDocument`, billable). `is_launched` is true only for a live launch, so a failed launch can be
|
|
79
|
+
retried. A mailbox added less than 14 days ago is in its warm-up hold: the campaign goes live but sends nothing
|
|
80
|
+
from it until the hold ends (the step warns).
|
|
52
81
|
The one-step launch executes email only; configure other channels in the sequencer block.
|
|
53
82
|
8. **Measure.** `CampaignMetrics` -> `getCampaignMetrics` with `days`.
|
|
54
83
|
|
|
@@ -62,8 +91,9 @@ read, so credit numbers are estimates: edit `CREDIT_ESTIMATES` in `lib/campaigns
|
|
|
62
91
|
|
|
63
92
|
## Defaults (do not ask the human)
|
|
64
93
|
|
|
65
|
-
- 5 ideas, goal `awareness`, channel `email`,
|
|
66
|
-
|
|
94
|
+
- 5 ideas, goal `awareness`, channel `email`, AI-written documents ON (a campaign without written copy cannot
|
|
95
|
+
launch; the create confirmation shows the spend), personas picked by AI when none are chosen
|
|
96
|
+
(`persona_mode: "ai_pick"`), sender = the first active mailbox.
|
|
67
97
|
- **Ask the human**: whether to launch and to which list (state the recipient count), and whether to approve
|
|
68
98
|
(lock) the company profile.
|
|
69
99
|
|
|
@@ -95,7 +125,7 @@ read, so credit numbers are estimates: edit `CREDIT_ESTIMATES` in `lib/campaigns
|
|
|
95
125
|
| `get_campaign_ideas_task_status_campaigns_ideas_status__task_id__get` | GET /campaigns/ideas/status/{task_id} | read | CampaignWizard |
|
|
96
126
|
| `create_campaign_campaigns_post` | POST /campaigns | external | CampaignWizard |
|
|
97
127
|
| `update_campaign_campaigns__campaign_id__patch` | PATCH /campaigns/{campaign_id} | external | CampaignWizard |
|
|
98
|
-
| `get_campaign_generation_status_campaigns__campaign_id__status_get` | GET /campaigns/{campaign_id}/status | read | CampaignWizard |
|
|
128
|
+
| `get_campaign_generation_status_campaigns__campaign_id__status_get` | GET /campaigns/{campaign_id}/status | read | CampaignWizard, CampaignLaunchStep |
|
|
99
129
|
| `get_campaign_full_campaigns__campaign_id__full_get` | GET /campaigns/{campaign_id}/full | read | CampaignWizard, CampaignAudienceStep, CampaignLaunchStep |
|
|
100
130
|
| `attach_audience_to_campaign_campaigns__campaign_id__audience_put` | PUT /campaigns/{campaign_id}/audience | external | CampaignWizard, CampaignAudienceStep |
|
|
101
131
|
| `get_campaign_sequence_campaigns__campaign_id__sequence_get` | GET /campaigns/{campaign_id}/sequence | read | CampaignWizard, CampaignSequenceStep |
|
|
@@ -112,7 +142,7 @@ read, so credit numbers are estimates: edit `CREDIT_ESTIMATES` in `lib/campaigns
|
|
|
112
142
|
| `get_campaign_metrics_campaigns__campaign_id__metrics_get` | GET /campaigns/{campaign_id}/metrics | read | CampaignList, CampaignMetrics |
|
|
113
143
|
| `list_campaign_documents_campaigns__campaign_id__documents_get` | GET /campaigns/{campaign_id}/documents | read | CampaignList, CampaignAssets |
|
|
114
144
|
| `get_campaign_document_campaigns__campaign_id__documents__document_id__get` | GET /campaigns/{campaign_id}/documents/{document_id} | read | CampaignList, CampaignAssets |
|
|
115
|
-
| `create_campaign_document_campaigns__campaign_id__documents_post` | POST /campaigns/{campaign_id}/documents | external | CampaignList, CampaignAssets |
|
|
145
|
+
| `create_campaign_document_campaigns__campaign_id__documents_post` | POST /campaigns/{campaign_id}/documents | external | CampaignWizard, CampaignLaunchStep, CampaignList, CampaignAssets |
|
|
116
146
|
| `update_campaign_document_campaigns__campaign_id__documents__document_id__put` | PUT /campaigns/{campaign_id}/documents/{document_id} | external | CampaignList, CampaignAssets |
|
|
117
147
|
| `delete_campaign_document_campaigns__campaign_id__documents__document_id__delete` | DELETE /campaigns/{campaign_id}/documents/{document_id} | destructive | CampaignList, CampaignAssets |
|
|
118
|
-
| `regenerate_campaign_document_campaigns__campaign_id__documents__document_id__regenerate_post` | POST /campaigns/{campaign_id}/documents/{document_id}/regenerate | billable | CampaignList, CampaignAssets |
|
|
148
|
+
| `regenerate_campaign_document_campaigns__campaign_id__documents__document_id__regenerate_post` | POST /campaigns/{campaign_id}/documents/{document_id}/regenerate | billable | CampaignWizard, CampaignLaunchStep, CampaignList, CampaignAssets |
|
|
@@ -15,45 +15,51 @@
|
|
|
15
15
|
"mailboxes"
|
|
16
16
|
],
|
|
17
17
|
"operations": [
|
|
18
|
-
"get_company_profile_company_profile_get",
|
|
19
|
-
"update_company_profile_company_profile_put",
|
|
20
|
-
"generate_company_profile_company_profile_generate_post",
|
|
21
|
-
"get_company_profile_workflow_status_company_profile_workflow_status_get",
|
|
22
18
|
"approve_company_profile_company_profile_approve_post",
|
|
23
|
-
"list_icps_icps_get",
|
|
24
|
-
"create_icp_icps_post",
|
|
25
|
-
"update_icp_icps__icp_id__patch",
|
|
26
19
|
"archive_icp_icps__icp_id__archive_post",
|
|
27
|
-
"list_personas_personas_get",
|
|
28
|
-
"create_persona_personas_post",
|
|
29
|
-
"update_persona_personas__persona_id__patch",
|
|
30
20
|
"archive_persona_personas__persona_id__archive_post",
|
|
31
|
-
"recommend_campaign_personas_campaigns_recommend_personas_post",
|
|
32
|
-
"generate_campaign_ideas_async_campaigns_generate_ideas_async_post",
|
|
33
|
-
"get_campaign_ideas_task_status_campaigns_ideas_status__task_id__get",
|
|
34
|
-
"create_campaign_campaigns_post",
|
|
35
|
-
"update_campaign_campaigns__campaign_id__patch",
|
|
36
|
-
"get_campaign_generation_status_campaigns__campaign_id__status_get",
|
|
37
|
-
"get_campaign_full_campaigns__campaign_id__full_get",
|
|
38
21
|
"attach_audience_to_campaign_campaigns__campaign_id__audience_put",
|
|
39
|
-
"
|
|
22
|
+
"create_campaign_campaigns_post",
|
|
23
|
+
"create_campaign_document_campaigns__campaign_id__documents_post",
|
|
40
24
|
"create_campaign_step_campaigns__campaign_id__sequence_steps_post",
|
|
41
|
-
"
|
|
25
|
+
"create_icp_icps_post",
|
|
26
|
+
"create_persona_personas_post",
|
|
27
|
+
"delete_campaign_campaigns__campaign_id__delete",
|
|
28
|
+
"delete_campaign_document_campaigns__campaign_id__documents__document_id__delete",
|
|
42
29
|
"delete_campaign_step_campaigns__campaign_id__sequence_steps__step_id__delete",
|
|
30
|
+
"generate_campaign_ideas_async_campaigns_generate_ideas_async_post",
|
|
43
31
|
"generate_campaign_step_content_campaigns__campaign_id__sequence_steps__step_id__generate_post",
|
|
44
|
-
"
|
|
32
|
+
"generate_company_profile_company_profile_generate_post",
|
|
33
|
+
"get_campaign_campaigns__campaign_id__get",
|
|
34
|
+
"get_campaign_document_campaigns__campaign_id__documents__document_id__get",
|
|
35
|
+
"get_campaign_full_campaigns__campaign_id__full_get",
|
|
36
|
+
"get_campaign_generation_status_campaigns__campaign_id__status_get",
|
|
37
|
+
"get_campaign_ideas_task_status_campaigns_ideas_status__task_id__get",
|
|
38
|
+
"get_campaign_metrics_campaigns__campaign_id__metrics_get",
|
|
39
|
+
"get_campaign_sequence_campaigns__campaign_id__sequence_get",
|
|
40
|
+
"get_company_profile_company_profile_get",
|
|
41
|
+
"get_company_profile_workflow_status_company_profile_workflow_status_get",
|
|
42
|
+
"get_persona_personas__persona_id__get",
|
|
45
43
|
"get_safe_to_send_deliverability_safe_to_send_get",
|
|
46
44
|
"launch_campaign_campaigns__campaign_id__launch_post",
|
|
45
|
+
"list_campaign_documents_campaigns__campaign_id__documents_get",
|
|
47
46
|
"list_campaigns_campaigns_get",
|
|
48
|
-
"delete_campaign_campaigns__campaign_id__delete",
|
|
49
47
|
"list_campaigns_for_picker_campaigns_picker_get",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
48
|
+
"list_icps_icps_get",
|
|
49
|
+
"list_mailboxes_mailboxes_get",
|
|
50
|
+
"list_personas_personas_get",
|
|
51
|
+
"pin_icp_icps__icp_id__pin_post",
|
|
52
|
+
"pin_persona_personas__persona_id__pin_post",
|
|
53
|
+
"recommend_campaign_personas_campaigns_recommend_personas_post",
|
|
54
|
+
"regenerate_campaign_document_campaigns__campaign_id__documents__document_id__regenerate_post",
|
|
55
|
+
"unpin_icp_icps__icp_id__unpin_post",
|
|
56
|
+
"unpin_persona_personas__persona_id__unpin_post",
|
|
57
|
+
"update_campaign_campaigns__campaign_id__patch",
|
|
54
58
|
"update_campaign_document_campaigns__campaign_id__documents__document_id__put",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
59
|
+
"update_campaign_step_campaigns__campaign_id__sequence_steps__step_id__put",
|
|
60
|
+
"update_company_profile_company_profile_put",
|
|
61
|
+
"update_icp_icps__icp_id__patch",
|
|
62
|
+
"update_persona_personas__persona_id__patch"
|
|
57
63
|
],
|
|
58
64
|
"components": [
|
|
59
65
|
{
|
|
@@ -102,23 +108,26 @@
|
|
|
102
108
|
"file": "components/campaign-wizard.tsx",
|
|
103
109
|
"purpose": "Idea -> create campaign -> attach audience -> steps and copy -> launch. Every billable or external step is confirmed; ideas are polled.",
|
|
104
110
|
"operations": [
|
|
105
|
-
"get_company_profile_company_profile_get",
|
|
106
|
-
"list_personas_personas_get",
|
|
107
|
-
"generate_campaign_ideas_async_campaigns_generate_ideas_async_post",
|
|
108
|
-
"get_campaign_ideas_task_status_campaigns_ideas_status__task_id__get",
|
|
109
|
-
"create_campaign_campaigns_post",
|
|
110
|
-
"update_campaign_campaigns__campaign_id__patch",
|
|
111
|
-
"get_campaign_generation_status_campaigns__campaign_id__status_get",
|
|
112
|
-
"get_campaign_full_campaigns__campaign_id__full_get",
|
|
113
111
|
"attach_audience_to_campaign_campaigns__campaign_id__audience_put",
|
|
114
|
-
"
|
|
112
|
+
"create_campaign_campaigns_post",
|
|
113
|
+
"create_campaign_document_campaigns__campaign_id__documents_post",
|
|
115
114
|
"create_campaign_step_campaigns__campaign_id__sequence_steps_post",
|
|
116
|
-
"update_campaign_step_campaigns__campaign_id__sequence_steps__step_id__put",
|
|
117
115
|
"delete_campaign_step_campaigns__campaign_id__sequence_steps__step_id__delete",
|
|
116
|
+
"generate_campaign_ideas_async_campaigns_generate_ideas_async_post",
|
|
118
117
|
"generate_campaign_step_content_campaigns__campaign_id__sequence_steps__step_id__generate_post",
|
|
119
|
-
"
|
|
118
|
+
"get_campaign_campaigns__campaign_id__get",
|
|
119
|
+
"get_campaign_full_campaigns__campaign_id__full_get",
|
|
120
|
+
"get_campaign_generation_status_campaigns__campaign_id__status_get",
|
|
121
|
+
"get_campaign_ideas_task_status_campaigns_ideas_status__task_id__get",
|
|
122
|
+
"get_campaign_sequence_campaigns__campaign_id__sequence_get",
|
|
123
|
+
"get_company_profile_company_profile_get",
|
|
120
124
|
"get_safe_to_send_deliverability_safe_to_send_get",
|
|
121
|
-
"launch_campaign_campaigns__campaign_id__launch_post"
|
|
125
|
+
"launch_campaign_campaigns__campaign_id__launch_post",
|
|
126
|
+
"list_mailboxes_mailboxes_get",
|
|
127
|
+
"list_personas_personas_get",
|
|
128
|
+
"regenerate_campaign_document_campaigns__campaign_id__documents__document_id__regenerate_post",
|
|
129
|
+
"update_campaign_campaigns__campaign_id__patch",
|
|
130
|
+
"update_campaign_step_campaigns__campaign_id__sequence_steps__step_id__put"
|
|
122
131
|
],
|
|
123
132
|
"tiers": [
|
|
124
133
|
"read",
|
|
@@ -161,15 +170,19 @@
|
|
|
161
170
|
{
|
|
162
171
|
"name": "CampaignLaunchStep",
|
|
163
172
|
"file": "components/campaign-launch-step.tsx",
|
|
164
|
-
"purpose": "Readiness checklist, sender
|
|
173
|
+
"purpose": "Readiness checklist (audience, steps, written copy, sender), deliverability and warm-up hold warnings, writes missing email copy with AI, then a confirmed launch.",
|
|
165
174
|
"operations": [
|
|
166
175
|
"get_campaign_full_campaigns__campaign_id__full_get",
|
|
167
176
|
"list_mailboxes_mailboxes_get",
|
|
168
177
|
"get_safe_to_send_deliverability_safe_to_send_get",
|
|
169
|
-
"launch_campaign_campaigns__campaign_id__launch_post"
|
|
178
|
+
"launch_campaign_campaigns__campaign_id__launch_post",
|
|
179
|
+
"get_campaign_generation_status_campaigns__campaign_id__status_get",
|
|
180
|
+
"create_campaign_document_campaigns__campaign_id__documents_post",
|
|
181
|
+
"regenerate_campaign_document_campaigns__campaign_id__documents__document_id__regenerate_post"
|
|
170
182
|
],
|
|
171
183
|
"tiers": [
|
|
172
184
|
"read",
|
|
185
|
+
"billable",
|
|
173
186
|
"external"
|
|
174
187
|
]
|
|
175
188
|
},
|
|
@@ -245,6 +258,55 @@
|
|
|
245
258
|
"purpose": "A label around a control; the layout piece the editors share.",
|
|
246
259
|
"operations": [],
|
|
247
260
|
"tiers": []
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "CampaignsScreen",
|
|
264
|
+
"file": "components/campaigns-screen.tsx",
|
|
265
|
+
"purpose": "Finished campaigns page: list, wizard sheet, detail tabs (details, metrics, documents, audience, sequence, launch), company profile and personas.",
|
|
266
|
+
"operations": [
|
|
267
|
+
"get_campaign_campaigns__campaign_id__get",
|
|
268
|
+
"get_campaign_full_campaigns__campaign_id__full_get",
|
|
269
|
+
"update_campaign_campaigns__campaign_id__patch"
|
|
270
|
+
],
|
|
271
|
+
"tiers": [
|
|
272
|
+
"read",
|
|
273
|
+
"external"
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"name": "IcpDetail",
|
|
278
|
+
"file": "components/icp-detail.tsx",
|
|
279
|
+
"purpose": "One ideal customer profile, complete: every field; edit, pin or unpin, archive (confirmed).",
|
|
280
|
+
"operations": [
|
|
281
|
+
"list_icps_icps_get",
|
|
282
|
+
"update_icp_icps__icp_id__patch",
|
|
283
|
+
"pin_icp_icps__icp_id__pin_post",
|
|
284
|
+
"unpin_icp_icps__icp_id__unpin_post",
|
|
285
|
+
"archive_icp_icps__icp_id__archive_post"
|
|
286
|
+
],
|
|
287
|
+
"tiers": [
|
|
288
|
+
"read",
|
|
289
|
+
"write",
|
|
290
|
+
"destructive"
|
|
291
|
+
]
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"name": "PersonaDetail",
|
|
295
|
+
"file": "components/persona-detail.tsx",
|
|
296
|
+
"purpose": "One buyer persona, complete: every field and the campaigns targeting it; edit, pin or unpin, archive (confirmed).",
|
|
297
|
+
"operations": [
|
|
298
|
+
"get_persona_personas__persona_id__get",
|
|
299
|
+
"list_campaigns_campaigns_get",
|
|
300
|
+
"update_persona_personas__persona_id__patch",
|
|
301
|
+
"pin_persona_personas__persona_id__pin_post",
|
|
302
|
+
"unpin_persona_personas__persona_id__unpin_post",
|
|
303
|
+
"archive_persona_personas__persona_id__archive_post"
|
|
304
|
+
],
|
|
305
|
+
"tiers": [
|
|
306
|
+
"read",
|
|
307
|
+
"write",
|
|
308
|
+
"destructive"
|
|
309
|
+
]
|
|
248
310
|
}
|
|
249
311
|
],
|
|
250
312
|
"hooks": [
|
|
@@ -356,6 +418,7 @@
|
|
|
356
418
|
}
|
|
357
419
|
],
|
|
358
420
|
"peerDependencies": {
|
|
359
|
-
"react": ">=18"
|
|
421
|
+
"react": ">=18",
|
|
422
|
+
"lucide-react": ">=0.400.0"
|
|
360
423
|
}
|
|
361
424
|
}
|
|
@@ -27,13 +27,17 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
|
27
27
|
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
28
28
|
import { useJobPoll } from "@/g8/hooks/use-job-poll";
|
|
29
29
|
import { g8Delete, g8Get, g8Post, g8Put } from "@/g8/lib/g8-client";
|
|
30
|
-
import {
|
|
30
|
+
import { KeyValueList } from "@/g8/components/screen/key-value-list";
|
|
31
|
+
import { CREDIT_ESTIMATES, MAX_BODY_BYTES, byteLength, fieldItems, listOf, objectOf, statusClass, text } from "../lib/campaigns-utils";
|
|
31
32
|
import type { CampaignDocRow } from "../lib/campaigns-utils";
|
|
32
33
|
import { CampaignSelect } from "./campaign-select";
|
|
33
34
|
import { FormField } from "./form-field";
|
|
34
35
|
|
|
35
36
|
export interface CampaignAssetsProps {
|
|
36
37
|
campaignId?: string;
|
|
38
|
+
/** Controlled open document (a screen deep-links it). Omit to let the component keep it. */
|
|
39
|
+
documentId?: string | null;
|
|
40
|
+
onDocumentChange?: (documentId: string | null) => void;
|
|
37
41
|
className?: string;
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -74,6 +78,7 @@ function Editor({ campaignId, doc, onChanged, onClosed }: { campaignId: string;
|
|
|
74
78
|
<h4 className="font-medium">{doc.display_name}</h4>
|
|
75
79
|
<Button size="sm" variant="outline" onClick={onClosed}>Close</Button>
|
|
76
80
|
</div>
|
|
81
|
+
<KeyValueList columns={2} items={fieldItems({ type: doc.file_type, status: text(objectOf(full.data).status) || doc.status, folder: doc.folder_path, updated_at: doc.updated_at, size: `${byteLength(text(objectOf(full.data).content)).toLocaleString()} bytes` })} />
|
|
77
82
|
{full.error ? <ApiError error={full.error} title="Could not load the document" onRetry={() => void full.refetch()} /> : null}
|
|
78
83
|
{full.loading && !full.data ? <Skeleton className="h-32 w-full" /> : null}
|
|
79
84
|
{run.error ? <ApiError error={run.error} /> : null}
|
|
@@ -81,7 +86,7 @@ function Editor({ campaignId, doc, onChanged, onClosed }: { campaignId: string;
|
|
|
81
86
|
{full.data ? (
|
|
82
87
|
<>
|
|
83
88
|
<Textarea aria-label="Document content" className="min-h-48 font-mono text-sm" value={body} onChange={(e) => setContent(e.target.value)} />
|
|
84
|
-
{tooBig ? <p role="alert" className="text-sm text-
|
|
89
|
+
{tooBig ? <p role="alert" className="text-sm text-error-foreground">This document is over 5 MiB and cannot be saved through the proxy.</p> : null}
|
|
85
90
|
<div className="flex gap-2">
|
|
86
91
|
<Button disabled={content === null || tooBig || poll.polling} onClick={() => setPending({ kind: "save" })}>Save changes</Button>
|
|
87
92
|
<Button variant="outline" disabled={poll.polling} onClick={() => setPending({ kind: "regenerate" })}>Rewrite with AI</Button>
|
|
@@ -105,9 +110,15 @@ function Editor({ campaignId, doc, onChanged, onClosed }: { campaignId: string;
|
|
|
105
110
|
);
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
function Documents({ campaignId, className }: { campaignId: string; className?: string }) {
|
|
113
|
+
function Documents({ campaignId, documentId, onDocumentChange, className }: { campaignId: string; documentId?: string | null; onDocumentChange?: (id: string | null) => void; className?: string }) {
|
|
109
114
|
const docs = useG8Query((signal) => g8Get(`/campaigns/${campaignId}/documents`, undefined, { signal }), [campaignId]);
|
|
110
|
-
const [
|
|
115
|
+
const [localId, setLocalId] = useState<string | null>(null);
|
|
116
|
+
const openId = documentId !== undefined ? documentId : localId;
|
|
117
|
+
const setOpenId = (next: string | null | ((cur: string | null) => string | null)) => {
|
|
118
|
+
const value = typeof next === "function" ? next(openId) : next;
|
|
119
|
+
setLocalId(value);
|
|
120
|
+
onDocumentChange?.(value);
|
|
121
|
+
};
|
|
111
122
|
const [creating, setCreating] = useState(false);
|
|
112
123
|
const [form, setForm] = useState({ display_name: "", file_type: FILE_TYPES[0], content: "" });
|
|
113
124
|
const [confirmCreate, setConfirmCreate] = useState(false);
|
|
@@ -168,13 +179,13 @@ function Documents({ campaignId, className }: { campaignId: string; className?:
|
|
|
168
179
|
);
|
|
169
180
|
}
|
|
170
181
|
|
|
171
|
-
export function CampaignAssets({ campaignId, className }: CampaignAssetsProps) {
|
|
182
|
+
export function CampaignAssets({ campaignId, documentId, onDocumentChange, className }: CampaignAssetsProps) {
|
|
172
183
|
const [picked, setPicked] = useState<string | null>(null);
|
|
173
184
|
const id = campaignId ?? picked;
|
|
174
185
|
return (
|
|
175
186
|
<div className={`space-y-3 ${className ?? ""}`}>
|
|
176
187
|
{campaignId ? null : <CampaignSelect value={picked} onChange={setPicked} />}
|
|
177
|
-
{id ? <Documents key={id} campaignId={id} /> : null}
|
|
188
|
+
{id ? <Documents key={id} campaignId={id} documentId={documentId} onDocumentChange={onDocumentChange} /> : null}
|
|
178
189
|
</div>
|
|
179
190
|
);
|
|
180
191
|
}
|