@graph8/sdk 0.370.0 → 0.374.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -3
- package/blocks/_foundation/README.md +61 -0
- package/blocks/_foundation/block.json +57 -0
- package/blocks/_foundation/components/auth-gate.tsx +363 -0
- package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
- 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 +1 -0
- package/blocks/_foundation/components/screen/split-view.tsx +1 -1
- package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
- package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
- package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
- package/blocks/_foundation/lib/paging.ts +57 -0
- package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
- package/blocks/_foundation/server/auth.ts +815 -0
- package/blocks/agents/README.md +81 -4
- package/blocks/agents/block.json +311 -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 +17 -3
- package/blocks/agents/components/agent-conversations.tsx +143 -0
- package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
- package/blocks/agents/components/agent-operators.tsx +164 -0
- package/blocks/agents/components/agent-run-detail.tsx +20 -3
- package/blocks/agents/components/agent-runs.tsx +3 -3
- 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 +53 -11
- 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/components/analytics-screen.tsx +16 -3
- package/blocks/analytics/components/kpi-tiles.tsx +1 -1
- package/blocks/analytics/components/report-builder.tsx +15 -2
- package/blocks/appointments/README.md +6 -0
- package/blocks/appointments/block.json +27 -0
- package/blocks/appointments/components/booking-page.tsx +2 -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 +29 -13
- package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
- package/blocks/campaigns/README.md +1 -1
- package/blocks/campaigns/block.json +34 -1
- package/blocks/campaigns/components/campaign-assets.tsx +1 -1
- package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
- package/blocks/campaigns/components/campaign-links.tsx +180 -0
- package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
- package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
- package/blocks/campaigns/components/icp-detail.tsx +11 -2
- package/blocks/campaigns/components/icp-editor.tsx +8 -3
- package/blocks/campaigns/components/persona-detail.tsx +11 -2
- package/blocks/campaigns/components/persona-editor.tsx +6 -1
- package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
- package/blocks/coach/components/call-scorecard.tsx +7 -7
- 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 +165 -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/components/copilot-memory.tsx +6 -2
- package/blocks/copilot/components/copilot-panel.tsx +1 -1
- package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
- package/blocks/crm/README.md +25 -0
- package/blocks/crm/block.json +257 -9
- package/blocks/crm/components/bulk-update.tsx +119 -0
- package/blocks/crm/components/companies-screen.tsx +23 -9
- package/blocks/crm/components/company-detail.tsx +2 -2
- package/blocks/crm/components/contact-detail.tsx +4 -4
- package/blocks/crm/components/contact-documents-tab.tsx +144 -0
- package/blocks/crm/components/contact-insights-tab.tsx +118 -0
- package/blocks/crm/components/contact-lookup-link.tsx +85 -0
- package/blocks/crm/components/contact-row-actions.tsx +43 -0
- package/blocks/crm/components/contact-tag-chips.tsx +113 -0
- package/blocks/crm/components/contacts-screen.tsx +77 -18
- package/blocks/crm/components/dedupe-review.tsx +6 -1
- package/blocks/crm/components/import-wizard.tsx +3 -7
- package/blocks/crm/components/list-select.tsx +20 -0
- package/blocks/crm/components/owner-assign.tsx +132 -0
- package/blocks/crm/components/record-engagement-tab.tsx +103 -0
- package/blocks/crm/components/record-form.tsx +1 -1
- package/blocks/crm/components/record-open-links.tsx +79 -0
- package/blocks/crm/components/tag-assign.tsx +112 -0
- package/blocks/crm/components/tag-manager.tsx +178 -0
- package/blocks/crm/components/tasks-screen.tsx +35 -15
- package/blocks/crm/components/unlock-info-button.tsx +82 -0
- package/blocks/crm/components/verify-email-button.tsx +93 -0
- package/blocks/crm/hooks/use-all-lists.ts +18 -0
- package/blocks/crm/hooks/use-contact-tags.ts +29 -0
- package/blocks/crm/hooks/use-page-query.ts +3 -1
- package/blocks/crm/lib/crm-types.ts +14 -0
- package/blocks/crm/lib/read-lists.ts +16 -0
- package/blocks/crm/lib/record-actions.ts +102 -0
- package/blocks/deals/README.md +25 -6
- package/blocks/deals/block.json +168 -2
- package/blocks/deals/components/close-deal-dialog.tsx +169 -0
- package/blocks/deals/components/create-deal-button.tsx +109 -0
- package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
- package/blocks/deals/components/deal-detail.tsx +7 -5
- 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 +30 -9
- package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
- package/blocks/deals/components/deals-screen.tsx +57 -22
- 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 +1 -1
- package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
- package/blocks/deals/lib/deal-extras.ts +156 -0
- package/blocks/dialer/README.md +14 -5
- package/blocks/dialer/block.json +17 -2
- package/blocks/dialer/components/call-contact-button.tsx +26 -9
- package/blocks/dialer/components/call-log-screen.tsx +10 -2
- package/blocks/dialer/components/call-log.tsx +16 -4
- package/blocks/dialer/components/call-queue.tsx +3 -3
- package/blocks/dialer/components/dialer-screen.tsx +69 -27
- package/blocks/dialer/components/disposition-panel.tsx +1 -1
- package/blocks/dialer/components/record-calls-tab.tsx +101 -0
- package/blocks/dialer/components/session-start-form.tsx +58 -11
- package/blocks/dialer/components/softphone.tsx +1 -1
- package/blocks/documents/README.md +86 -0
- package/blocks/documents/block.json +378 -0
- package/blocks/documents/components/account-bar.tsx +48 -0
- package/blocks/documents/components/correspondence-form.tsx +95 -0
- package/blocks/documents/components/correspondence-import.tsx +106 -0
- package/blocks/documents/components/correspondence-panel.tsx +191 -0
- package/blocks/documents/components/documents-screen.tsx +76 -0
- package/blocks/documents/components/library-document-detail.tsx +175 -0
- package/blocks/documents/components/library-document-form.tsx +105 -0
- package/blocks/documents/components/library-folder-form.tsx +74 -0
- package/blocks/documents/components/library-panel.tsx +235 -0
- package/blocks/documents/components/library-upload.tsx +160 -0
- package/blocks/documents/components/question-row.tsx +130 -0
- package/blocks/documents/components/questionnaire-detail.tsx +197 -0
- package/blocks/documents/components/questionnaires-panel.tsx +173 -0
- package/blocks/documents/components/record-documents-tab.tsx +109 -0
- package/blocks/documents/lib/documents-types.ts +252 -0
- package/blocks/documents/lib/download.ts +64 -0
- package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
- package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
- package/blocks/documents/recipes/organize-the-library.ts +25 -0
- package/blocks/email-signatures/README.md +123 -0
- package/blocks/email-signatures/block.json +341 -0
- package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
- package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
- package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
- package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
- package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
- package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
- package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
- package/blocks/email-signatures/components/signature-policy.tsx +130 -0
- package/blocks/email-signatures/components/signature-preview.tsx +119 -0
- package/blocks/email-signatures/components/signature-report.tsx +77 -0
- package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
- package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
- package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
- package/blocks/email-signatures/lib/signatures-types.ts +392 -0
- package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
- package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
- package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
- package/blocks/enrichment/README.md +38 -10
- package/blocks/enrichment/block.json +164 -12
- package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
- package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +79 -30
- package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
- package/blocks/enrichment/components/enrich-button.tsx +1 -1
- package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
- package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
- package/blocks/enrichment/components/enrichment-screen.tsx +103 -19
- package/blocks/enrichment/components/list-pipelines.tsx +97 -0
- package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
- package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
- package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
- package/blocks/enrichment/components/waterfall-config.tsx +27 -7
- package/blocks/enrichment/lib/enrichment-types.ts +51 -0
- package/blocks/enrichment/lib/pipeline-types.ts +234 -0
- package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
- package/blocks/forms/README.md +10 -0
- package/blocks/forms/block.json +107 -0
- 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 +1 -1
- package/blocks/forms/components/form-embed.tsx +1 -1
- 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 +57 -20
- 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 +15 -6
- package/blocks/forms/lib/forms-extra.ts +67 -0
- package/blocks/inbox/README.md +45 -4
- package/blocks/inbox/block.json +145 -8
- package/blocks/inbox/components/blocked-senders.tsx +103 -0
- package/blocks/inbox/components/compose-message.tsx +193 -0
- package/blocks/inbox/components/inbox-screen.tsx +54 -6
- package/blocks/inbox/components/inbox-search.tsx +106 -0
- package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
- package/blocks/inbox/components/tag-manager.tsx +136 -0
- package/blocks/inbox/components/thread-actions.tsx +133 -0
- package/blocks/inbox/components/thread-badge.tsx +2 -2
- package/blocks/inbox/components/thread-view.tsx +3 -1
- package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
- package/blocks/inbox/lib/inbox-manage.ts +227 -0
- package/blocks/integrations/README.md +54 -1
- package/blocks/integrations/block.json +189 -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 +20 -1
- 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 +4 -4
- package/blocks/integrations/components/integrations-screen.tsx +20 -3
- 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 +6 -6
- package/blocks/integrations/components/sync-triggers.tsx +134 -0
- package/blocks/integrations/components/webhook-detail.tsx +26 -7
- package/blocks/integrations/components/webhook-manager.tsx +30 -17
- 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 +5 -0
- package/blocks/integrations/lib/load-more.tsx +141 -0
- package/blocks/intent/components/account-intent-score.tsx +5 -5
- package/blocks/intent/components/intent-feed.tsx +8 -8
- package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
- package/blocks/intent/lib/intent-utils.ts +4 -4
- 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/lists/README.md +7 -1
- package/blocks/lists/block.json +31 -3
- package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
- package/blocks/lists/components/add-to-list-button.tsx +17 -7
- package/blocks/lists/components/list-picker.tsx +13 -3
- package/blocks/lists/components/list-table.tsx +1 -1
- package/blocks/lists/components/lists-screen.tsx +4 -2
- package/blocks/lists/components/record-lists-tab.tsx +99 -0
- package/blocks/lists/components/suppression-manager.tsx +27 -6
- package/blocks/lists/components/suppressions-screen.tsx +21 -12
- package/blocks/mailboxes/block.json +4 -0
- package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
- package/blocks/mailboxes/components/domain-detail.tsx +10 -4
- package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
- package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
- package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
- package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
- package/blocks/mailboxes/components/warmup-health.tsx +20 -20
- package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
- package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
- package/blocks/newsletter/README.md +12 -0
- package/blocks/newsletter/block.json +117 -0
- package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
- 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 +38 -13
- 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/components/nurture-builder.tsx +10 -2
- package/blocks/nurture/components/nurture-enroll.tsx +6 -7
- package/blocks/nurture/components/nurture-form.tsx +5 -8
- package/blocks/nurture/components/nurture-screen.tsx +16 -5
- package/blocks/nurture/components/nurture-stats.tsx +5 -4
- package/blocks/objects/README.md +1 -1
- package/blocks/objects/block.json +13 -5
- package/blocks/objects/components/attribute-field-input.tsx +14 -1
- package/blocks/objects/components/object-record-detail.tsx +8 -1
- package/blocks/objects/components/object-schema-editor.tsx +1 -1
- package/blocks/objects/components/record-roles-panel.tsx +15 -5
- package/blocks/pages/README.md +14 -0
- package/blocks/pages/block.json +123 -2
- 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-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/hooks/use-landing-page-paths.ts +60 -0
- 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/programmatic-pages/README.md +82 -0
- package/blocks/programmatic-pages/block.json +292 -0
- package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
- package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
- package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
- package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
- package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
- package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
- package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
- package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
- package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
- package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
- package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
- package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
- package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
- package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
- package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
- package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
- package/blocks/quotes/README.md +38 -1
- package/blocks/quotes/block.json +170 -6
- package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
- package/blocks/quotes/components/manual-products.tsx +144 -0
- package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
- package/blocks/quotes/components/quote-builder.tsx +1 -1
- package/blocks/quotes/components/quote-documents.tsx +94 -0
- package/blocks/quotes/components/quote-evidence.tsx +95 -0
- package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
- package/blocks/quotes/components/quote-list.tsx +28 -8
- package/blocks/quotes/components/quote-related.tsx +3 -2
- package/blocks/quotes/components/quote-settings.tsx +184 -0
- package/blocks/quotes/components/quote-templates.tsx +189 -0
- package/blocks/quotes/components/quote-viewer.tsx +23 -5
- package/blocks/quotes/components/quotes-screen.tsx +15 -5
- package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
- package/blocks/quotes/lib/quote-admin.ts +264 -0
- package/blocks/radar/components/competitor-battle-card.tsx +1 -1
- package/blocks/radar/components/radar-overview.tsx +4 -4
- package/blocks/radar/components/radar-screen.tsx +2 -2
- package/blocks/sequencer/README.md +61 -0
- package/blocks/sequencer/block.json +200 -6
- package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
- package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
- package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
- package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
- package/blocks/sequencer/components/sequence-builder.tsx +32 -23
- package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
- package/blocks/sequencer/components/sequence-detail.tsx +10 -2
- package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
- package/blocks/sequencer/components/sequence-enrollments.tsx +5 -2
- package/blocks/sequencer/components/sequence-list.tsx +1 -1
- package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
- package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
- package/blocks/sequencer/components/sequence-stats.tsx +8 -8
- package/blocks/sequencer/components/sequence-templates.tsx +160 -0
- package/blocks/sequencer/components/sequences-screen.tsx +39 -6
- package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
- package/blocks/sequencer/components/step-editor.tsx +21 -12
- package/blocks/sequencer/components/step-templates.tsx +224 -0
- package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
- package/blocks/sequencer/lib/sequencer-utils.ts +9 -5
- package/blocks/signals/README.md +39 -0
- package/blocks/signals/block.json +287 -0
- package/blocks/signals/components/auto-seed-panel.tsx +111 -0
- package/blocks/signals/components/listener-form.tsx +184 -0
- package/blocks/signals/components/matched-posts.tsx +195 -0
- package/blocks/signals/components/post-engagements.tsx +114 -0
- package/blocks/signals/components/record-social-tab.tsx +49 -0
- package/blocks/signals/components/social-listeners-screen.tsx +250 -0
- package/blocks/signals/components/social-settings.tsx +128 -0
- package/blocks/signals/components/source-lists.tsx +83 -0
- package/blocks/signals/components/top-engagers.tsx +68 -0
- package/blocks/signals/components/x-integration.tsx +129 -0
- package/blocks/signals/lib/signals-types.ts +228 -0
- package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
- package/blocks/signals/recipes/spend-against-caps.ts +14 -0
- package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
- package/blocks/social-ads/README.md +8 -0
- package/blocks/social-ads/block.json +137 -4
- package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
- package/blocks/social-ads/components/audience-sync.tsx +6 -2
- package/blocks/social-ads/components/campaign-ads.tsx +166 -0
- package/blocks/social-ads/components/campaign-detail.tsx +6 -1
- 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 +6 -1
- package/blocks/social-ads/components/creative-gallery.tsx +20 -2
- 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 +4 -2
- package/blocks/social-ads/components/post-detail.tsx +3 -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-screen.tsx +33 -6
- 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/components/snippet-install.tsx +18 -9
- package/blocks/visitors/components/traffic-chart.tsx +7 -6
- package/blocks/visitors/components/visitor-company-card.tsx +24 -14
- package/blocks/visitors/components/visitor-feed.tsx +5 -5
- package/blocks/web-analytics/README.md +102 -0
- package/blocks/web-analytics/block.json +292 -0
- package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
- package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
- package/blocks/web-analytics/components/audience-tab.tsx +135 -0
- package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
- package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
- package/blocks/web-analytics/components/overview-tab.tsx +102 -0
- package/blocks/web-analytics/components/pages-tab.tsx +148 -0
- package/blocks/web-analytics/components/ranked-table.tsx +103 -0
- package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
- package/blocks/web-analytics/components/report-card.tsx +71 -0
- package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
- package/blocks/web-analytics/hooks/use-report.ts +48 -0
- package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
- package/blocks/web-analytics/lib/page-path.ts +14 -0
- package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
- package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
- package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
- package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
- package/blocks/webchat/components/chat-widget-config.tsx +1 -1
- package/blocks/webchat/components/conversation-list.tsx +1 -1
- package/blocks/workbench/README.md +54 -2
- package/blocks/workbench/block.json +234 -5
- package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
- package/blocks/workbench/components/run-progress.tsx +1 -1
- package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
- package/blocks/workbench/components/workbench-automation.tsx +36 -0
- package/blocks/workbench/components/workbench-connections.tsx +136 -0
- package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
- package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
- package/blocks/workbench/components/workbench-grid.tsx +71 -8
- package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
- package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
- package/blocks/workbench/components/workbench-schedule.tsx +191 -0
- package/blocks/workbench/components/workbench-screen.tsx +65 -22
- package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
- package/blocks/workbench/components/workbench-table-list.tsx +14 -2
- package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
- package/blocks/workbench/components/workbench-triggers.tsx +178 -0
- package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
- package/blocks/workbench/lib/api-status.ts +8 -0
- package/blocks/workbench/lib/automation-types.ts +257 -0
- package/blocks/workflows/components/workflow-editor.tsx +1 -1
- package/dist/cli.js +744 -394
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +1 -1
- package/dist/react.browser.js.map +1 -1
- package/dist/react.browser.mjs +1 -1
- package/dist/react.browser.mjs.map +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/types/blocks/auth.d.ts +45 -0
- package/dist/types/blocks/brand.d.ts +11 -2
- package/dist/types/blocks/create.d.ts +11 -4
- package/dist/types/blocks/doctor.d.ts +1 -0
- package/dist/types/blocks/flows.d.ts +7 -1
- package/dist/types/blocks/orgBrand.d.ts +34 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/version.d.ts +2 -2
- package/docs/api/desk.d.ts +2 -2
- package/docs/api/desk.md +3 -3
- package/docs/api/forms.md +1 -1
- package/package.json +1 -1
- package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
- package/stacks/6sense/pages/contacts.tsx.tmpl +21 -0
- package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
- package/stacks/6sense.json +37 -4
- package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
- package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
- package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
- package/stacks/apollo/pages/contacts.tsx.tmpl +35 -95
- 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 +24 -0
- package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
- package/stacks/apollo/pages/sequences.tsx.tmpl +18 -0
- package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
- package/stacks/apollo.json +68 -9
- 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 +8 -4
- package/stacks/clay/pages/companies.tsx.tmpl +17 -1
- package/stacks/clay/pages/contacts.tsx.tmpl +14 -0
- package/stacks/clay/pages/lists.tsx.tmpl +24 -0
- package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
- package/stacks/clay.json +16 -3
- 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 +34 -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 +23 -6
- package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
- package/stacks/hootsuite/pages/contacts.tsx.tmpl +30 -0
- package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
- package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
- package/stacks/hootsuite.json +19 -4
- package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
- package/stacks/hubspot/pages/companies.tsx.tmpl +16 -35
- package/stacks/hubspot/pages/contacts.tsx.tmpl +35 -95
- package/stacks/hubspot/pages/deals.tsx.tmpl +14 -1
- package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
- package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
- 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/sequences.tsx.tmpl +18 -0
- package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
- package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
- package/stacks/hubspot.json +66 -7
- 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 +18 -4
- package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
- package/stacks/mailchimp/pages/contacts.tsx.tmpl +30 -0
- package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
- package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
- package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
- package/stacks/mailchimp.json +22 -6
- package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
- package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
- package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
- package/stacks/outreach/pages/contacts.tsx.tmpl +29 -66
- 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 +18 -0
- package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
- package/stacks/outreach.json +38 -9
- package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
- package/stacks/pipedrive/pages/contacts.tsx.tmpl +17 -35
- package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
- package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
- package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
- package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
- package/stacks/pipedrive.json +14 -2
- package/stacks/salesforce/pages/companies.tsx.tmpl +16 -35
- package/stacks/salesforce/pages/contacts.tsx.tmpl +26 -35
- package/stacks/salesforce/pages/deals.tsx.tmpl +14 -1
- package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
- package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
- package/stacks/salesforce.json +17 -3
- package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
- package/stacks/zoominfo/pages/contacts.tsx.tmpl +16 -0
- package/stacks/zoominfo/pages/lists.tsx.tmpl +24 -0
- package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
- package/stacks/zoominfo.json +36 -3
- package/styles/styles.css +1 -1
- package/templates/auth/auth-route.ts.tmpl +16 -0
- package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
- package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
- package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
- package/templates/auth/g8.auth.password.ts.tmpl +18 -0
- package/templates/auth/g8.auth.session.ts.tmpl +23 -0
- package/templates/auth/middleware.ts.tmpl +14 -0
- package/templates/next-app/README.md.tmpl +5 -0
- package/templates/next-app/app/globals.css.tmpl +349 -64
- package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
- package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
"$schema": "../block.schema.json",
|
|
3
3
|
"id": "sequencer",
|
|
4
4
|
"title": "Sequencer",
|
|
5
|
-
"summary": "Build multi-channel outreach sequences
|
|
5
|
+
"summary": "Build multi-channel outreach sequences, launch, pause, rename and delete them, start from templates, get AI help with copy, enroll and withdraw contacts, and read performance.",
|
|
6
6
|
"features": [
|
|
7
7
|
"multi-channel step builder with send-time rule checks",
|
|
8
8
|
"launch, pause, resume, duplicate and archive with recipient-count confirmation",
|
|
9
9
|
"enroll contacts with deliverability warning",
|
|
10
|
-
"per-step stats, skip reasons and per-contact control"
|
|
10
|
+
"per-step stats, skip reasons and per-contact control",
|
|
11
|
+
"rename, edit, sync and delete a sequence (confirmed)",
|
|
12
|
+
"sequence and step templates, and AI-drafted step templates (billable, confirmed)",
|
|
13
|
+
"AI help on a step: improve instructions, preview a draft for a contact (billable, confirmed)",
|
|
14
|
+
"remove a sender from a sequence, withdraw one contact"
|
|
11
15
|
],
|
|
12
16
|
"dependsOn": [
|
|
13
17
|
"lists",
|
|
@@ -18,14 +22,28 @@
|
|
|
18
22
|
"add_sequence_channels_sequences__sequence_id__channels_post",
|
|
19
23
|
"add_sequence_steps_sequences__sequence_id__steps_post",
|
|
20
24
|
"archive_sequence_sequences__sequence_id__archive_post",
|
|
25
|
+
"archive_sequence_template_sequencer_templates_sequences__sequence_template_id__archive_patch",
|
|
26
|
+
"archive_step_template_sequencer_templates_steps__step_template_id__archive_patch",
|
|
21
27
|
"create_sequence_sequences_post",
|
|
28
|
+
"create_sequence_template_sequencer_templates_sequences_post",
|
|
29
|
+
"create_step_template_sequencer_templates_steps_post",
|
|
30
|
+
"delete_sequence_sequences__sequence_id__delete",
|
|
22
31
|
"delete_sequence_step_sequences__sequence_id__steps__step_id__delete",
|
|
32
|
+
"draft_email_sequencer_content_email_generate_post",
|
|
33
|
+
"draft_linkedin_message_sequencer_content_linkedin_generate_post",
|
|
23
34
|
"duplicate_sequence_sequences__sequence_id__duplicate_post",
|
|
35
|
+
"estimate_email_draft_sequencer_content_email_estimate_post",
|
|
36
|
+
"generate_step_templates_sequencer_templates_steps_generate_post",
|
|
37
|
+
"get_campaign_campaigns__campaign_id__get",
|
|
38
|
+
"get_contact_sequences_contacts__contact_id__sequences_get",
|
|
24
39
|
"get_outreach_connection_integrations_linkedin_outreach_connection_get",
|
|
25
40
|
"get_safe_to_send_deliverability_safe_to_send_get",
|
|
26
41
|
"get_sequence_analytics_sequences__sequence_id__analytics_get",
|
|
27
42
|
"get_sequence_sequences__sequence_id__get",
|
|
28
43
|
"get_sequence_stats_sequences__sequence_id__stats_get",
|
|
44
|
+
"improve_step_instruction_sequencer_content_instructions_improve_post",
|
|
45
|
+
"link_sequence_campaign_sequences__sequence_id__campaign_put",
|
|
46
|
+
"list_campaigns_for_picker_campaigns_picker_get",
|
|
29
47
|
"list_contacts_contacts_get",
|
|
30
48
|
"list_linkedin_accounts_sequencer_channels_linkedin_accounts_get",
|
|
31
49
|
"list_lists_lists_get",
|
|
@@ -33,15 +51,25 @@
|
|
|
33
51
|
"list_sequence_channels_sequences__sequence_id__channels_get",
|
|
34
52
|
"list_sequence_contacts_sequences__sequence_id__contacts_get",
|
|
35
53
|
"list_sequence_steps_sequences__sequence_id__steps_get",
|
|
54
|
+
"list_sequence_templates_sequencer_templates_sequences_get",
|
|
36
55
|
"list_sequencer_phone_numbers_sequencer_channels_phone_numbers_get",
|
|
56
|
+
"list_sequencer_textual_agents_sequencer_agents_textual_get",
|
|
37
57
|
"list_sequences_sequences_get",
|
|
58
|
+
"list_step_templates_sequencer_templates_steps_get",
|
|
38
59
|
"pause_sequence_contact_sequences__sequence_id__contacts__contact_id__pause_post",
|
|
39
60
|
"pause_sequence_sequences__sequence_id__pause_post",
|
|
40
61
|
"preview_sequence_sequences__sequence_id__preview_get",
|
|
62
|
+
"remove_sequence_channel_sequences__sequence_id__channels__sequence_channel_id__delete",
|
|
41
63
|
"resume_sequence_contact_sequences__sequence_id__contacts__contact_id__resume_post",
|
|
42
64
|
"resume_sequence_sequences__sequence_id__resume_post",
|
|
43
65
|
"run_sequence_sequences__sequence_id__run_post",
|
|
44
|
-
"
|
|
66
|
+
"sync_sequence_sequences__sequence_id__sync_post",
|
|
67
|
+
"unlink_sequence_campaign_sequences__sequence_id__campaign_delete",
|
|
68
|
+
"update_sequence_sequences__sequence_id__patch",
|
|
69
|
+
"update_sequence_step_sequences__sequence_id__steps__step_id__patch",
|
|
70
|
+
"update_sequence_template_sequencer_templates_sequences__sequence_template_id__patch",
|
|
71
|
+
"update_step_template_sequencer_templates_steps__step_template_id__patch",
|
|
72
|
+
"withdraw_contacts_from_sequences_contacts_withdraw_from_sequences_post"
|
|
45
73
|
],
|
|
46
74
|
"components": [
|
|
47
75
|
{
|
|
@@ -92,7 +120,7 @@
|
|
|
92
120
|
{
|
|
93
121
|
"name": "StepEditor",
|
|
94
122
|
"file": "components/step-editor.tsx",
|
|
95
|
-
"purpose": "Controlled form for one step with the rules the API only enforces at send time; shows LinkedIn sender readiness.",
|
|
123
|
+
"purpose": "Controlled form for one step with the rules the API only enforces at send time; shows LinkedIn sender readiness. Optional renderAssist slot for AI help.",
|
|
96
124
|
"operations": [
|
|
97
125
|
"get_outreach_connection_integrations_linkedin_outreach_connection_get"
|
|
98
126
|
],
|
|
@@ -131,10 +159,116 @@
|
|
|
131
159
|
"external"
|
|
132
160
|
]
|
|
133
161
|
},
|
|
162
|
+
{
|
|
163
|
+
"name": "SequenceEditForm",
|
|
164
|
+
"file": "components/sequence-edit-form.tsx",
|
|
165
|
+
"purpose": "Rename a sequence and change its settings (description, stop on reply, same thread, new contacts). Loads the record first, saves only the changed fields, and confirms before changing a sequence that may be live.",
|
|
166
|
+
"operations": [
|
|
167
|
+
"get_sequence_sequences__sequence_id__get",
|
|
168
|
+
"update_sequence_sequences__sequence_id__patch"
|
|
169
|
+
],
|
|
170
|
+
"tiers": [
|
|
171
|
+
"read",
|
|
172
|
+
"external"
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": "SequenceManageActions",
|
|
177
|
+
"file": "components/sequence-manage-actions.tsx",
|
|
178
|
+
"purpose": "Edit, sync the audience list (confirmed), save as template and delete (confirmed) for one sequence.",
|
|
179
|
+
"operations": [
|
|
180
|
+
"create_sequence_template_sequencer_templates_sequences_post",
|
|
181
|
+
"delete_sequence_sequences__sequence_id__delete",
|
|
182
|
+
"list_sequence_steps_sequences__sequence_id__steps_get",
|
|
183
|
+
"sync_sequence_sequences__sequence_id__sync_post"
|
|
184
|
+
],
|
|
185
|
+
"tiers": [
|
|
186
|
+
"read",
|
|
187
|
+
"write",
|
|
188
|
+
"external",
|
|
189
|
+
"destructive"
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "SequenceSendersPanel",
|
|
194
|
+
"file": "components/sequence-senders-panel.tsx",
|
|
195
|
+
"purpose": "The senders (mailboxes, numbers, LinkedIn accounts) a sequence sends from, with remove (confirmed).",
|
|
196
|
+
"operations": [
|
|
197
|
+
"list_sequence_channels_sequences__sequence_id__channels_get",
|
|
198
|
+
"remove_sequence_channel_sequences__sequence_id__channels__sequence_channel_id__delete"
|
|
199
|
+
],
|
|
200
|
+
"tiers": [
|
|
201
|
+
"read",
|
|
202
|
+
"destructive"
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "SequenceTemplates",
|
|
207
|
+
"file": "components/sequence-templates.tsx",
|
|
208
|
+
"purpose": "Sequence templates: list, create, edit (only changes are sent), archive (confirmed) and restore, and use one to start a new sequence.",
|
|
209
|
+
"operations": [
|
|
210
|
+
"archive_sequence_template_sequencer_templates_sequences__sequence_template_id__archive_patch",
|
|
211
|
+
"create_sequence_template_sequencer_templates_sequences_post",
|
|
212
|
+
"list_sequence_templates_sequencer_templates_sequences_get",
|
|
213
|
+
"update_sequence_template_sequencer_templates_sequences__sequence_template_id__patch"
|
|
214
|
+
],
|
|
215
|
+
"tiers": [
|
|
216
|
+
"read",
|
|
217
|
+
"write",
|
|
218
|
+
"destructive"
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"name": "StepTemplates",
|
|
223
|
+
"file": "components/step-templates.tsx",
|
|
224
|
+
"purpose": "Step templates (also exports StepTemplatePicker): list, create, edit, archive (confirmed), restore, and have an agent draft a batch (billable, confirmed).",
|
|
225
|
+
"operations": [
|
|
226
|
+
"archive_step_template_sequencer_templates_steps__step_template_id__archive_patch",
|
|
227
|
+
"create_step_template_sequencer_templates_steps_post",
|
|
228
|
+
"generate_step_templates_sequencer_templates_steps_generate_post",
|
|
229
|
+
"list_sequencer_textual_agents_sequencer_agents_textual_get",
|
|
230
|
+
"list_step_templates_sequencer_templates_steps_get",
|
|
231
|
+
"update_step_template_sequencer_templates_steps__step_template_id__patch"
|
|
232
|
+
],
|
|
233
|
+
"tiers": [
|
|
234
|
+
"read",
|
|
235
|
+
"write",
|
|
236
|
+
"billable",
|
|
237
|
+
"destructive"
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "StepAiAssist",
|
|
242
|
+
"file": "components/step-ai-assist.tsx",
|
|
243
|
+
"purpose": "AI help for one email or LinkedIn step: improve the instructions and preview a draft for one contact; each AI call is billable and confirmed (the email draft shows its credit estimate), nothing is sent.",
|
|
244
|
+
"operations": [
|
|
245
|
+
"draft_email_sequencer_content_email_generate_post",
|
|
246
|
+
"draft_linkedin_message_sequencer_content_linkedin_generate_post",
|
|
247
|
+
"estimate_email_draft_sequencer_content_email_estimate_post",
|
|
248
|
+
"improve_step_instruction_sequencer_content_instructions_improve_post",
|
|
249
|
+
"list_contacts_contacts_get",
|
|
250
|
+
"list_sequencer_textual_agents_sequencer_agents_textual_get"
|
|
251
|
+
],
|
|
252
|
+
"tiers": [
|
|
253
|
+
"read",
|
|
254
|
+
"billable"
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"name": "WithdrawContactButton",
|
|
259
|
+
"file": "components/enrollment-withdraw.tsx",
|
|
260
|
+
"purpose": "Take one contact out of one sequence, recording the outcome (removed, completed, not interested); confirmed.",
|
|
261
|
+
"operations": [
|
|
262
|
+
"withdraw_contacts_from_sequences_contacts_withdraw_from_sequences_post"
|
|
263
|
+
],
|
|
264
|
+
"tiers": [
|
|
265
|
+
"write"
|
|
266
|
+
]
|
|
267
|
+
},
|
|
134
268
|
{
|
|
135
269
|
"name": "SequencesScreen",
|
|
136
270
|
"file": "components/sequences-screen.tsx",
|
|
137
|
-
"purpose": "Finished sequences page: status stats and filter, list with launch/pause/resume, new-sequence sheet, detail with results, steps and enroll-contacts.",
|
|
271
|
+
"purpose": "Finished sequences page: status stats and filter, list with launch/pause/resume, new-sequence sheet, templates, detail with results, steps, senders, edit, sync, delete and enroll-contacts.",
|
|
138
272
|
"operations": [
|
|
139
273
|
"list_sequences_sequences_get"
|
|
140
274
|
],
|
|
@@ -145,7 +279,7 @@
|
|
|
145
279
|
{
|
|
146
280
|
"name": "SequenceDetail",
|
|
147
281
|
"file": "components/sequence-detail.tsx",
|
|
148
|
-
"purpose": "One sequence, complete: every field, tabs for steps, contacts and results; launch (refused with the reasons when a step has no sender or the copy would block it), pause, resume, duplicate, archive and enroll (confirmed).",
|
|
282
|
+
"purpose": "One sequence, complete: every field, tabs for steps, contacts and results; launch (refused with the reasons when a step has no sender or the copy would block it), pause, resume, duplicate, archive and enroll (confirmed). Also edit, sync audience, save as template, delete and a Senders tab.",
|
|
149
283
|
"operations": [
|
|
150
284
|
"get_sequence_sequences__sequence_id__get",
|
|
151
285
|
"preview_sequence_sequences__sequence_id__preview_get",
|
|
@@ -189,6 +323,46 @@
|
|
|
189
323
|
"write",
|
|
190
324
|
"external"
|
|
191
325
|
]
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"name": "AddToSequenceButton",
|
|
329
|
+
"file": "components/add-to-sequence-button.tsx",
|
|
330
|
+
"purpose": "\"Add to sequence\" for any selection of contacts: pick a live sequence, then the enroll dialog (preflight and confirmation) enrolls them. Drops into ContactsScreen bulk and detail slots.",
|
|
331
|
+
"operations": [
|
|
332
|
+
"list_sequences_sequences_get"
|
|
333
|
+
],
|
|
334
|
+
"tiers": [
|
|
335
|
+
"read"
|
|
336
|
+
]
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"name": "RecordSequencesTab",
|
|
340
|
+
"file": "components/record-sequences-tab.tsx",
|
|
341
|
+
"purpose": "The sequences one contact is or was enrolled in, with state and step, Add to sequence, and a confirmed Withdraw; an extraDetailTabs entry for ContactsScreen. Withdraw is a write-tier op (the validator derives tiers from the contract), confirmed anyway with the destructive-styled dialog because it stops outreach.",
|
|
342
|
+
"operations": [
|
|
343
|
+
"get_contact_sequences_contacts__contact_id__sequences_get",
|
|
344
|
+
"withdraw_contacts_from_sequences_contacts_withdraw_from_sequences_post"
|
|
345
|
+
],
|
|
346
|
+
"tiers": [
|
|
347
|
+
"read",
|
|
348
|
+
"write"
|
|
349
|
+
]
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"name": "SequenceCampaignLink",
|
|
353
|
+
"file": "components/sequence-campaign-link.tsx",
|
|
354
|
+
"purpose": "Which campaign a sequence belongs to: the linked campaign with an Open campaign button and Unlink, or a picker to link one. Drop into SequencesScreen.renderDetailActions.",
|
|
355
|
+
"operations": [
|
|
356
|
+
"get_campaign_campaigns__campaign_id__get",
|
|
357
|
+
"list_campaigns_for_picker_campaigns_picker_get",
|
|
358
|
+
"link_sequence_campaign_sequences__sequence_id__campaign_put",
|
|
359
|
+
"unlink_sequence_campaign_sequences__sequence_id__campaign_delete"
|
|
360
|
+
],
|
|
361
|
+
"tiers": [
|
|
362
|
+
"read",
|
|
363
|
+
"write",
|
|
364
|
+
"destructive"
|
|
365
|
+
]
|
|
192
366
|
}
|
|
193
367
|
],
|
|
194
368
|
"hooks": [],
|
|
@@ -196,6 +370,10 @@
|
|
|
196
370
|
{
|
|
197
371
|
"file": "lib/sequencer-utils.ts",
|
|
198
372
|
"purpose": "Step kinds and the draft-to-API mapping, send-time rule checks, status helpers and envelope readers."
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"file": "lib/sequencer-manage.ts",
|
|
376
|
+
"purpose": "Edit-only-what-changed diffs, sequence and step template readers and bodies, contact and agent readers for AI drafting."
|
|
199
377
|
}
|
|
200
378
|
],
|
|
201
379
|
"recipes": [
|
|
@@ -243,6 +421,7 @@
|
|
|
243
421
|
"scopes": [
|
|
244
422
|
"campaigns:read",
|
|
245
423
|
"contacts:read",
|
|
424
|
+
"contacts:write",
|
|
246
425
|
"integrations:read",
|
|
247
426
|
"lists:read",
|
|
248
427
|
"sequences:delete",
|
|
@@ -285,6 +464,21 @@
|
|
|
285
464
|
"decision": "Audience list",
|
|
286
465
|
"default": "Required when creating a draft (the API cannot set it later, and a sequence without one cannot launch)",
|
|
287
466
|
"askHuman": false
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
"decision": "Deleting versus archiving a sequence",
|
|
470
|
+
"default": "Both are the API's soft delete (the sequence stops and is hidden, and cannot be restored through the API). Delete and Archive are both offered and both confirmed",
|
|
471
|
+
"askHuman": false
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"decision": "Reordering the steps of a saved sequence",
|
|
475
|
+
"default": "Not offered: the API has no reorder operation and a saved step has no order field to patch. Unsaved steps can be moved before they are added",
|
|
476
|
+
"askHuman": false
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"decision": "Who may spend credits on AI drafts",
|
|
480
|
+
"default": "Every AI call (improve instructions, preview a draft, draft step templates) goes through a confirm dialog that says it uses credits; the email draft shows the exact estimate first",
|
|
481
|
+
"askHuman": false
|
|
288
482
|
}
|
|
289
483
|
],
|
|
290
484
|
"peerDependencies": {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AddToSequenceButton: "Add to sequence" for any selection of contacts. Opens a sheet with every live sequence,
|
|
5
|
+
* then the enroll dialog (which runs the sending preflight and asks the person to confirm before anything is sent).
|
|
6
|
+
* Drop it into ContactsScreen's `renderBulkActions` / `renderDetailActions`.
|
|
7
|
+
*
|
|
8
|
+
* SDK operations, in order:
|
|
9
|
+
* 1. listSequences GET /sequences?page=&limit=100 (read; pages through the whole list, live ones only)
|
|
10
|
+
* 2. EnrollDialog: getSafeToSend, getSequence, addContactsToSequence (external: confirmed in the dialog)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useState } from "react";
|
|
14
|
+
import { Button, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Skeleton } from "@graph8/sdk/ui";
|
|
15
|
+
import { ListPlus } from "lucide-react";
|
|
16
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
17
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
18
|
+
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
19
|
+
import { g8Get } from "@/g8/lib/g8-client";
|
|
20
|
+
import { EnrollDialog } from "./enroll-dialog";
|
|
21
|
+
import { listOf, statusGroup, type SequenceRow } from "../lib/sequencer-utils";
|
|
22
|
+
|
|
23
|
+
/** Every active sequence, paging through the whole list (the API answers one page at a time). */
|
|
24
|
+
async function activeSequences(signal: AbortSignal): Promise<SequenceRow[]> {
|
|
25
|
+
const out: SequenceRow[] = [];
|
|
26
|
+
for (let page = 1; page <= 50; page++) {
|
|
27
|
+
const env = await g8Get("/sequences", { page, limit: 100 }, { signal });
|
|
28
|
+
const rows = listOf<SequenceRow>(env);
|
|
29
|
+
out.push(...rows.filter((s) => statusGroup(s.status) === "live"));
|
|
30
|
+
const more = (env as { pagination?: { has_next?: boolean } }).pagination?.has_next;
|
|
31
|
+
if (!more || rows.length === 0) break;
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface AddToSequenceButtonProps {
|
|
37
|
+
contactIds: number[];
|
|
38
|
+
onEnrolled?: () => void;
|
|
39
|
+
variant?: "outline" | "default";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Pick a sequence, then enroll the contacts in it. The enroll dialog shows the sending preflight before anything is sent. */
|
|
43
|
+
export function AddToSequenceButton({ contactIds, onEnrolled, variant = "outline" }: AddToSequenceButtonProps) {
|
|
44
|
+
const [step, setStep] = useState<"closed" | "pick" | "enroll">("closed");
|
|
45
|
+
const [sequenceId, setSequenceId] = useState("");
|
|
46
|
+
const sequences = useG8Query(activeSequences, [], { enabled: step === "pick" });
|
|
47
|
+
const chosen = (sequences.data ?? []).find((s) => s.id === sequenceId);
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<Button size="sm" variant={variant} disabled={contactIds.length === 0} onClick={() => { setSequenceId(""); setStep("pick"); }}>
|
|
51
|
+
<ListPlus className="mr-1 size-4" aria-hidden />
|
|
52
|
+
Add to sequence
|
|
53
|
+
</Button>
|
|
54
|
+
<Sheet open={step === "pick"} onOpenChange={(o) => { if (!o) setStep("closed"); }}>
|
|
55
|
+
<SheetContent className="w-full sm:max-w-md">
|
|
56
|
+
<SheetHeader>
|
|
57
|
+
<SheetTitle>Add to sequence</SheetTitle>
|
|
58
|
+
<SheetDescription>{contactIds.length === 1 ? "1 contact" : `${contactIds.length} contacts`} will be enrolled in the sequence you pick.</SheetDescription>
|
|
59
|
+
</SheetHeader>
|
|
60
|
+
<div className="space-y-3 p-4">
|
|
61
|
+
{sequences.error ? <ApiError error={sequences.error} title="Could not load sequences" onRetry={() => void sequences.refetch()} /> : null}
|
|
62
|
+
{sequences.loading && !sequences.data ? <Skeleton className="h-9 w-full" aria-label="Loading sequences" /> : null}
|
|
63
|
+
{sequences.data && sequences.data.length === 0 ? <EmptyState compact title="No active sequences" description="Launch a sequence on the Sequences page first." /> : null}
|
|
64
|
+
{sequences.data && sequences.data.length > 0 ? (
|
|
65
|
+
<>
|
|
66
|
+
<label htmlFor="enroll-sequence" className="text-sm font-medium">Sequence</label>
|
|
67
|
+
<select id="enroll-sequence" className="h-9 w-full rounded-md border bg-background px-2 text-sm" value={sequenceId} onChange={(e) => setSequenceId(e.target.value)}>
|
|
68
|
+
<option value="">Choose a sequence</option>
|
|
69
|
+
{sequences.data.map((s) => <option key={s.id} value={s.id}>{s.name || s.id}</option>)}
|
|
70
|
+
</select>
|
|
71
|
+
<Button disabled={!sequenceId} onClick={() => setStep("enroll")}>Continue</Button>
|
|
72
|
+
</>
|
|
73
|
+
) : null}
|
|
74
|
+
</div>
|
|
75
|
+
</SheetContent>
|
|
76
|
+
</Sheet>
|
|
77
|
+
{step === "enroll" && chosen ? (
|
|
78
|
+
<EnrollDialog open sequenceId={chosen.id} sequenceName={chosen.name ?? undefined} contactIds={contactIds} onClose={() => setStep("closed")} onEnrolled={() => { setStep("closed"); onEnrolled?.(); }} />
|
|
79
|
+
) : null}
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -46,7 +46,7 @@ function Verdict() {
|
|
|
46
46
|
const v = objectOf(verdict.data);
|
|
47
47
|
if ((v.safe_to_send ?? v.safe) !== false) return null;
|
|
48
48
|
return (
|
|
49
|
-
<p role="alert" className="mt-2 rounded bg-
|
|
49
|
+
<p role="alert" className="mt-2 rounded bg-warning p-2 text-warning-foreground">
|
|
50
50
|
Deliverability warning: {typeof v.reason === "string" ? v.reason : "sending is not recommended right now."}
|
|
51
51
|
</p>
|
|
52
52
|
);
|
|
@@ -81,7 +81,7 @@ export function EnrollDialog({ open, sequenceId, sequenceName, contactIds, listI
|
|
|
81
81
|
<p>They start receiving this sequence's steps. Contacts already enrolled are skipped.</p>
|
|
82
82
|
{note ? <div className="mt-2">{note}</div> : null}
|
|
83
83
|
{missingList ? (
|
|
84
|
-
<p role="alert" className="mt-2 text-
|
|
84
|
+
<p role="alert" className="mt-2 text-error-foreground">
|
|
85
85
|
This sequence has no audience list. Pass the list these contacts came from as `listId`.
|
|
86
86
|
</p>
|
|
87
87
|
) : null}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WithdrawContactButton: take ONE contact out of ONE sequence for good (they stop receiving its steps), recording why.
|
|
5
|
+
*
|
|
6
|
+
* SDK operations:
|
|
7
|
+
* POST /contacts/withdraw-from-sequences (contacts.withdrawContactsFromSequences) tier `write`, but it ends the contact's
|
|
8
|
+
* place in the sequence, so it asks first through ConfirmSpendDialog as a DESTRUCTIVE action. The body is always the one
|
|
9
|
+
* `sequence_id` and the one `contact_id`; `remove_all` (every sequence the contact is in) is never sent from here.
|
|
10
|
+
* The outcome to record is the person's choice: removed, completed, or not interested.
|
|
11
|
+
*/
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { UserMinus } from "lucide-react";
|
|
14
|
+
import { Button } from "@graph8/sdk/ui";
|
|
15
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
16
|
+
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
17
|
+
import { g8Post } from "@/g8/lib/g8-client";
|
|
18
|
+
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
19
|
+
|
|
20
|
+
export interface WithdrawContactButtonProps {
|
|
21
|
+
sequenceId: string;
|
|
22
|
+
contactId: number;
|
|
23
|
+
/** Shown in the dialog. */
|
|
24
|
+
contactName?: string;
|
|
25
|
+
/** Called after the contact was withdrawn so the list can refresh. */
|
|
26
|
+
onWithdrawn?: () => void;
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const OUTCOMES = [
|
|
31
|
+
{ value: "removed", label: "Removed (no reason recorded)" },
|
|
32
|
+
{ value: "completed", label: "Completed (they did what we wanted)" },
|
|
33
|
+
{ value: "not_interested", label: "Not interested" },
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
export function WithdrawContactButton({ sequenceId, contactId, contactName, onWithdrawn, className }: WithdrawContactButtonProps) {
|
|
37
|
+
const [open, setOpen] = useState(false);
|
|
38
|
+
const [outcome, setOutcome] = useState<(typeof OUTCOMES)[number]["value"]>("removed");
|
|
39
|
+
const withdraw = useG8Mutation(async () => {
|
|
40
|
+
await g8Post("/contacts/withdraw-from-sequences", { sequence_id: sequenceId, contact_ids: [contactId], target_state: outcome, remove_all: false });
|
|
41
|
+
setOpen(false);
|
|
42
|
+
onWithdrawn?.();
|
|
43
|
+
}, { invalidates: ["sequences"], onError: () => setOpen(false) });
|
|
44
|
+
const who = contactName || `contact ${contactId}`;
|
|
45
|
+
return (
|
|
46
|
+
<span className={className}>
|
|
47
|
+
<Button size="sm" variant="ghost" onClick={() => setOpen(true)}><UserMinus className="mr-1 size-4" aria-hidden />Remove from sequence</Button>
|
|
48
|
+
{withdraw.error ? <ApiError error={withdraw.error} title="Could not remove the contact" /> : null}
|
|
49
|
+
<ConfirmSpendDialog
|
|
50
|
+
open={open}
|
|
51
|
+
tier="destructive"
|
|
52
|
+
title={`Remove ${who} from this sequence?`}
|
|
53
|
+
description={
|
|
54
|
+
<div className="space-y-2">
|
|
55
|
+
<p>They stop receiving its steps. They stay in your contacts and in every other sequence.</p>
|
|
56
|
+
<label className="block text-sm">Record it as
|
|
57
|
+
<select aria-label="Outcome" className="mt-1 h-9 w-full rounded-md border border-border bg-background px-2" value={outcome} onChange={(e) => setOutcome(e.target.value as typeof outcome)}>
|
|
58
|
+
{OUTCOMES.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
|
|
59
|
+
</select>
|
|
60
|
+
</label>
|
|
61
|
+
</div>
|
|
62
|
+
}
|
|
63
|
+
confirmLabel="Remove from sequence"
|
|
64
|
+
affected={1}
|
|
65
|
+
confirming={withdraw.loading}
|
|
66
|
+
onConfirm={() => void withdraw.mutate(undefined)}
|
|
67
|
+
onCancel={() => setOpen(false)}
|
|
68
|
+
/>
|
|
69
|
+
</span>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RecordSequencesTab: every sequence one contact is or was enrolled in, with its state and step, "Add to sequence"
|
|
5
|
+
* (the sending preflight and confirmation live in the enroll dialog) and a confirmed "Withdraw" per active enrollment.
|
|
6
|
+
* Use it as an `extraDetailTabs` entry of ContactsScreen:
|
|
7
|
+
* { id: "sequences", label: "Sequences", render: (c) => <RecordSequencesTab contactId={c.id} onOpenSequence={(id) => router.push(`/sequences?id=${id}`)} /> }
|
|
8
|
+
*
|
|
9
|
+
* SDK operations, in order:
|
|
10
|
+
* 1. getContactSequences GET /contacts/{id}/sequences (read; the API answers the 50 most recent enrollments)
|
|
11
|
+
* 2. AddToSequenceButton: listSequences, getSafeToSend, getSequence, addContactsToSequence (external: confirmed in the dialog)
|
|
12
|
+
* 3. withdrawContactsFromSequences POST /contacts/withdraw-from-sequences (write; stops the contact's steps, confirmed here)
|
|
13
|
+
* Withdraw is contract-tier `write`, but it stops a contact's outreach, so it is confirmed with the destructive dialog anyway.
|
|
14
|
+
* Withdraw names ONE sequence; it never uses the API's remove-from-all switch.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useState } from "react";
|
|
18
|
+
import { Button, Skeleton } from "@graph8/sdk/ui";
|
|
19
|
+
import { Workflow } from "lucide-react";
|
|
20
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
21
|
+
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
22
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
23
|
+
import { StatusPill } from "@/g8/components/screen/status-pill";
|
|
24
|
+
import type { Tone } from "@/g8/components/screen/status-pill";
|
|
25
|
+
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
26
|
+
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
27
|
+
import { g8Get, g8Post } from "@/g8/lib/g8-client";
|
|
28
|
+
import { AddToSequenceButton } from "./add-to-sequence-button";
|
|
29
|
+
import { listOf } from "../lib/sequencer-utils";
|
|
30
|
+
|
|
31
|
+
/** One row of GET /contacts/{id}/sequences. */
|
|
32
|
+
interface Enrollment { id?: number | null; sequence_id?: string | null; sequence_name?: string | null; sequence_status?: string | null; state?: string | null; current_step_order?: number | null; created_at?: string | null }
|
|
33
|
+
|
|
34
|
+
/** The server returns at most this many enrollments, newest first. */
|
|
35
|
+
export const SEQUENCES_TAB_API_LIMIT = 50;
|
|
36
|
+
|
|
37
|
+
const TONE: Record<string, Tone> = { waiting: "info", paused: "warning", completed: "positive", replied: "positive", bounced: "negative", skipped: "neutral" };
|
|
38
|
+
/** Enrollments that are already over cannot be withdrawn. */
|
|
39
|
+
const OVER = new Set(["completed", "replied", "bounced", "skipped", "withdrawn", "removed"]);
|
|
40
|
+
|
|
41
|
+
function when(iso: string | null | undefined): string {
|
|
42
|
+
const d = iso ? new Date(iso) : null;
|
|
43
|
+
return d && !Number.isNaN(d.getTime()) ? d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }) : "";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface RecordSequencesTabProps {
|
|
47
|
+
contactId: number;
|
|
48
|
+
/** Route to the sequence in your sequences screen. Without it the rows link to `${sequencesPath}?id=<sequence id>`. */
|
|
49
|
+
onOpenSequence?: (sequenceId: string) => void;
|
|
50
|
+
/** Path of your sequences screen for the plain links. Default "/sequences". */
|
|
51
|
+
sequencesPath?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function RecordSequencesTab({ contactId, onOpenSequence, sequencesPath = "/sequences" }: RecordSequencesTabProps) {
|
|
55
|
+
const [withdrawing, setWithdrawing] = useState<Enrollment | null>(null);
|
|
56
|
+
const q = useG8Query(async (signal) => listOf<Enrollment>(await g8Get(`/contacts/${contactId}/sequences`, undefined, { signal })), [contactId]);
|
|
57
|
+
const withdraw = useG8Mutation(async (e: Enrollment) => {
|
|
58
|
+
await g8Post("/contacts/withdraw-from-sequences", { contact_ids: [contactId], sequence_id: e.sequence_id });
|
|
59
|
+
setWithdrawing(null);
|
|
60
|
+
await q.refetch();
|
|
61
|
+
}, { invalidates: [["sequences"]], onError: () => setWithdrawing(null) });
|
|
62
|
+
|
|
63
|
+
if (q.error) return <ApiError error={q.error} title="Could not load sequences" onRetry={() => void q.refetch()} />;
|
|
64
|
+
if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading sequences" />;
|
|
65
|
+
const rows = q.data;
|
|
66
|
+
return (
|
|
67
|
+
<div className="space-y-3">
|
|
68
|
+
<div className="flex items-center justify-between gap-2">
|
|
69
|
+
<p className="text-sm text-muted-foreground">{rows.length === 1 ? "1 enrollment" : `${rows.length.toLocaleString()} enrollments`}</p>
|
|
70
|
+
<AddToSequenceButton contactIds={[contactId]} onEnrolled={() => void q.refetch()} />
|
|
71
|
+
</div>
|
|
72
|
+
{withdraw.error ? <ApiError error={withdraw.error} title="Could not withdraw the contact" /> : null}
|
|
73
|
+
{rows.length === 0 ? (
|
|
74
|
+
<EmptyState compact icon={<Workflow className="size-8" />} title="Not in any sequence" description="Add this contact to a sequence to start its steps." />
|
|
75
|
+
) : (
|
|
76
|
+
<ul className="divide-y rounded-md border" aria-label="Sequences">
|
|
77
|
+
{rows.map((e, i) => {
|
|
78
|
+
const state = (e.state ?? "").toLowerCase();
|
|
79
|
+
return (
|
|
80
|
+
<li key={e.id ?? `${e.sequence_id}-${i}`} className="flex items-center justify-between gap-3 p-3 text-sm">
|
|
81
|
+
<div className="min-w-0">
|
|
82
|
+
{e.sequence_id ? (
|
|
83
|
+
<a
|
|
84
|
+
href={`${sequencesPath}?id=${encodeURIComponent(e.sequence_id)}`}
|
|
85
|
+
className="block truncate font-medium hover:underline"
|
|
86
|
+
onClick={(ev) => {
|
|
87
|
+
if (!onOpenSequence || ev.defaultPrevented || ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
|
|
88
|
+
ev.preventDefault();
|
|
89
|
+
onOpenSequence(e.sequence_id as string);
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{e.sequence_name || "Untitled sequence"}
|
|
93
|
+
</a>
|
|
94
|
+
) : <span className="font-medium">{e.sequence_name || "Untitled sequence"}</span>}
|
|
95
|
+
<p className="text-xs text-muted-foreground">
|
|
96
|
+
{[e.current_step_order != null ? `Step ${e.current_step_order}` : null, e.created_at ? `Enrolled ${when(e.created_at)}` : null, e.sequence_status ? `Sequence ${e.sequence_status.toLowerCase()}` : null].filter(Boolean).join(" · ")}
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
100
|
+
<StatusPill tone={TONE[state] ?? "neutral"} dot>{state || "unknown"}</StatusPill>
|
|
101
|
+
{e.sequence_id && !OVER.has(state) ? (
|
|
102
|
+
<Button size="sm" variant="ghost" loading={withdraw.loading && withdrawing?.id === e.id} onClick={() => setWithdrawing(e)}>Withdraw</Button>
|
|
103
|
+
) : null}
|
|
104
|
+
</div>
|
|
105
|
+
</li>
|
|
106
|
+
);
|
|
107
|
+
})}
|
|
108
|
+
</ul>
|
|
109
|
+
)}
|
|
110
|
+
{rows.length >= SEQUENCES_TAB_API_LIMIT ? (
|
|
111
|
+
<p role="note" className="text-xs text-muted-foreground">Showing the {SEQUENCES_TAB_API_LIMIT} most recent enrollments. Older ones are in the sequence itself.</p>
|
|
112
|
+
) : null}
|
|
113
|
+
<ConfirmSpendDialog
|
|
114
|
+
open={withdrawing !== null}
|
|
115
|
+
tier="destructive"
|
|
116
|
+
title={`Withdraw from ${withdrawing?.sequence_name || "this sequence"}`}
|
|
117
|
+
description="The contact stops receiving this sequence's steps. Their other sequences are not changed."
|
|
118
|
+
affected={1}
|
|
119
|
+
confirmLabel="Withdraw"
|
|
120
|
+
confirming={withdraw.loading}
|
|
121
|
+
onConfirm={() => withdrawing && void withdraw.mutate(withdrawing)}
|
|
122
|
+
onCancel={() => setWithdrawing(null)}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|