@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,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RecordCallsTab: one contact's call history, newest first, with a "Call" button that places one confirmed call through the
|
|
5
|
+
* dialer. Use it as an `extraDetailTabs` entry of ContactsScreen:
|
|
6
|
+
* { id: "calls", label: "Calls", render: (c) => <RecordCallsTab contact={{ id: c.id, first_name: c.first_name, last_name: c.last_name, email: c.work_email, mobile_phone: c.mobile_phone, direct_phone: c.direct_phone }} onOpenCall={(room) => router.push(`/calls?id=${room}`)} /> }
|
|
7
|
+
*
|
|
8
|
+
* SDK operations, in order:
|
|
9
|
+
* 1. listCallsForContact GET /voice/dialer/calls?contact_id=&limit= (read; 25 at first, "Show more" asks for up to 100, the API maximum)
|
|
10
|
+
* 2. CallContactButton: getMyTeamMember, listDialerSessions, listDialerNumbers, manualDial (external: confirmed in its dialog)
|
|
11
|
+
* The API has no offset, so past 100 calls the tab says so instead of cutting the list silently.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { useState } from "react";
|
|
15
|
+
import { Button, Skeleton } from "@graph8/sdk/ui";
|
|
16
|
+
import { PhoneCall } from "lucide-react";
|
|
17
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
18
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
19
|
+
import { StatusPill } from "@/g8/components/screen/status-pill";
|
|
20
|
+
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
21
|
+
import { g8Get } from "@/g8/lib/g8-client";
|
|
22
|
+
import { CallContactButton } from "./call-contact-button";
|
|
23
|
+
import type { CallableContact } from "./call-contact-button";
|
|
24
|
+
import { dataOf, formatDuration, type CallSummary } from "../lib/dialer-types";
|
|
25
|
+
import { outcomeTone } from "./call-log-screen";
|
|
26
|
+
|
|
27
|
+
/** The most calls one read can return. */
|
|
28
|
+
export const CALLS_TAB_API_MAX = 100;
|
|
29
|
+
|
|
30
|
+
function when(iso?: string | null): string {
|
|
31
|
+
const d = iso ? new Date(iso) : null;
|
|
32
|
+
return d && !Number.isNaN(d.getTime()) ? d.toLocaleString(undefined, { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit" }) : "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface RecordCallsTabProps {
|
|
36
|
+
/** The contact: `id` scopes the history; the name and numbers feed the call button. */
|
|
37
|
+
contact: CallableContact & { id: string | number };
|
|
38
|
+
/** Route to the call in your calls screen; receives the call's room name. Without it the rows link to `${callsPath}?id=<room name>`. */
|
|
39
|
+
onOpenCall?: (roomName: string) => void;
|
|
40
|
+
/** Path of your calls screen for the plain links. Default "/calls". */
|
|
41
|
+
callsPath?: string;
|
|
42
|
+
/** Calls to read first. Default 25. */
|
|
43
|
+
pageSize?: number;
|
|
44
|
+
/** Pass the rep's softphone identity through to the call button (see CallContactButton). */
|
|
45
|
+
userId?: string;
|
|
46
|
+
userEmail?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function RecordCallsTab({ contact, onOpenCall, callsPath = "/calls", pageSize = 25, userId, userEmail }: RecordCallsTabProps) {
|
|
50
|
+
const [limit, setLimit] = useState(Math.min(pageSize, CALLS_TAB_API_MAX));
|
|
51
|
+
const contactId = String(contact.id);
|
|
52
|
+
const q = useG8Query(
|
|
53
|
+
async (signal) => dataOf<{ calls?: CallSummary[] }>(await g8Get("/voice/dialer/calls", { contact_id: contactId, limit }, { signal })).calls ?? [],
|
|
54
|
+
[contactId, limit],
|
|
55
|
+
);
|
|
56
|
+
const call = <CallContactButton contact={contact} userId={userId} userEmail={userEmail} onCalled={() => void q.refetch()} />;
|
|
57
|
+
if (q.error) return <ApiError error={q.error} title="Could not load calls" onRetry={() => void q.refetch()} />;
|
|
58
|
+
if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading calls" />;
|
|
59
|
+
const calls = q.data;
|
|
60
|
+
const full = calls.length >= limit;
|
|
61
|
+
return (
|
|
62
|
+
<div className="space-y-3">
|
|
63
|
+
<div className="flex items-center justify-between gap-2">
|
|
64
|
+
<p className="text-sm text-muted-foreground">{calls.length === 1 ? "1 call" : `${calls.length.toLocaleString()}${full ? "+" : ""} calls`}</p>
|
|
65
|
+
{call}
|
|
66
|
+
</div>
|
|
67
|
+
{calls.length === 0 ? (
|
|
68
|
+
<EmptyState compact icon={<PhoneCall className="size-8" />} title="No calls yet" description="Calls the dialer places to this contact show up here." />
|
|
69
|
+
) : (
|
|
70
|
+
<ul className="divide-y rounded-md border" aria-label="Calls">
|
|
71
|
+
{calls.map((c) => (
|
|
72
|
+
<li key={c.room_name}>
|
|
73
|
+
<a
|
|
74
|
+
href={`${callsPath}?id=${encodeURIComponent(c.room_name)}`}
|
|
75
|
+
className="flex items-center justify-between gap-3 p-3 text-sm hover:bg-muted"
|
|
76
|
+
onClick={(e) => {
|
|
77
|
+
if (!onOpenCall || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
onOpenCall(c.room_name);
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<span className="min-w-0">
|
|
83
|
+
<span className="block font-medium">{when(c.created_at) || "Call"}</span>
|
|
84
|
+
<span className="block truncate text-xs text-muted-foreground">{[c.to, formatDuration(c.call_duration), c.summary ?? (c.has_transcript ? "Transcript available" : null)].filter(Boolean).join(" · ")}</span>
|
|
85
|
+
</span>
|
|
86
|
+
<StatusPill tone={outcomeTone(c.disposition)}>{c.disposition ? c.disposition.replace(/_/g, " ") : "no outcome"}</StatusPill>
|
|
87
|
+
</a>
|
|
88
|
+
</li>
|
|
89
|
+
))}
|
|
90
|
+
</ul>
|
|
91
|
+
)}
|
|
92
|
+
{full && limit < CALLS_TAB_API_MAX ? (
|
|
93
|
+
<div className="flex items-center justify-between text-sm text-muted-foreground">
|
|
94
|
+
<span role="status">Showing {calls.length.toLocaleString()} calls</span>
|
|
95
|
+
<Button size="sm" variant="outline" onClick={() => setLimit(CALLS_TAB_API_MAX)}>Show more</Button>
|
|
96
|
+
</div>
|
|
97
|
+
) : null}
|
|
98
|
+
{full && limit >= CALLS_TAB_API_MAX ? <p role="note" className="text-xs text-muted-foreground">Showing the {CALLS_TAB_API_MAX} most recent calls. Older calls are in the call log by rep or session.</p> : null}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
* SessionStartForm: create a DRAFT dialer session for a list. A draft dials NOBODY; dialing starts only when
|
|
5
5
|
* CallQueue's "Dial next batch" is confirmed.
|
|
6
6
|
*
|
|
7
|
+
* No ids to type: the rep defaults to the signed-in team member (`useCurrentMember`, GET /team-members/me) and can be
|
|
8
|
+
* switched with a MemberPicker; the list is picked with a ListPicker. A `userId` / `listId` prop still wins, and the
|
|
9
|
+
* raw id fields stay under "Use a ... id instead" for a key that is not a person (an organisation key).
|
|
10
|
+
*
|
|
7
11
|
* SDK operations, in order:
|
|
12
|
+
* 0. GET /team-members/me (teamMembers.getMyTeamMember) read - only without `userId`: the rep is you
|
|
8
13
|
* 1. GET /voice/dialer/numbers (voice.listDialerNumbers) read - caller IDs and today's usage
|
|
9
14
|
* 2. POST /dialer/sessions/draft (dialer.createSessionDraft) write - safe: resolves the list, dials no one
|
|
15
|
+
* A personal key may only name itself as the rep: naming someone else answers 403, which the form explains.
|
|
10
16
|
* Calling the draft twice for the same list returns the existing live session instead of making a second one.
|
|
11
17
|
*/
|
|
12
18
|
import { useState } from "react";
|
|
@@ -16,6 +22,11 @@ import { g8Post } from "@/g8/lib/g8-client";
|
|
|
16
22
|
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
17
23
|
import { useDialerNumbers } from "../hooks/use-dialer-numbers";
|
|
18
24
|
import { dataOf, type DialerMode } from "../lib/dialer-types";
|
|
25
|
+
import { useBrand } from "@/g8/hooks/use-brand";
|
|
26
|
+
import { useCurrentMember } from "@/g8/pickers/hooks/use-current-member";
|
|
27
|
+
import { MemberPicker } from "@/g8/pickers/components/member-picker";
|
|
28
|
+
import type { MemberOption } from "@/g8/pickers/components/member-picker";
|
|
29
|
+
import { ListPicker } from "@/g8/lists/components/list-picker";
|
|
19
30
|
|
|
20
31
|
export interface SessionStartFormProps {
|
|
21
32
|
/** The rep these calls are attributed to. An organisation API key has no user of its own, so you name one. */
|
|
@@ -30,14 +41,25 @@ export interface SessionStartFormProps {
|
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
export function SessionStartForm({ userId, userEmail, listId: initialListId = "", listTitle, onCreated, className }: SessionStartFormProps) {
|
|
44
|
+
const brand = useBrand();
|
|
33
45
|
const { numbers, loading, error, refetch } = useDialerNumbers();
|
|
34
46
|
const [listId, setListId] = useState(initialListId);
|
|
47
|
+
const [pickedTitle, setPickedTitle] = useState("");
|
|
35
48
|
const [typedUserId, setTypedUserId] = useState("");
|
|
36
|
-
const
|
|
49
|
+
const [refused, setRefused] = useState(false);
|
|
50
|
+
// With a passed userId nobody is looked up, until a 403 needs to know whether the rep is someone else.
|
|
51
|
+
const me = useCurrentMember({ enabled: !userId || refused });
|
|
52
|
+
const [picked, setPicked] = useState<MemberOption | null>(null);
|
|
53
|
+
// Who the session is for: a passed userId, else a typed id, else the chosen teammate, else you. user_id and user_email
|
|
54
|
+
// always describe the same person.
|
|
55
|
+
const resolved = userId || typedUserId.trim() ? null : (picked ?? me.member);
|
|
56
|
+
const repId = userId || typedUserId.trim() || resolved?.id || "";
|
|
57
|
+
const repEmail = resolved ? (resolved.email ?? undefined) : userEmail;
|
|
37
58
|
const [fromPhone, setFromPhone] = useState("");
|
|
38
59
|
const [mode, setMode] = useState<DialerMode>("power");
|
|
39
60
|
const [parallelCount, setParallelCount] = useState(3);
|
|
40
61
|
const [name, setName] = useState(listTitle ? `Dialing ${listTitle}` : "");
|
|
62
|
+
const title = listTitle ?? pickedTitle;
|
|
41
63
|
|
|
42
64
|
const chosenPhone = fromPhone || numbers[0]?.phone_number || "";
|
|
43
65
|
const create = useG8Mutation(
|
|
@@ -45,9 +67,9 @@ export function SessionStartForm({ userId, userEmail, listId: initialListId = ""
|
|
|
45
67
|
const count = mode === "parallel" ? Math.min(10, Math.max(2, Math.round(parallelCount) || 2)) : 1;
|
|
46
68
|
const env = await g8Post("/dialer/sessions/draft", {
|
|
47
69
|
user_id: repId,
|
|
48
|
-
user_email:
|
|
70
|
+
user_email: repEmail,
|
|
49
71
|
list_id: listId,
|
|
50
|
-
list_title:
|
|
72
|
+
list_title: title || undefined,
|
|
51
73
|
name: name || undefined,
|
|
52
74
|
from_phone: chosenPhone || undefined,
|
|
53
75
|
dialing_mode: mode,
|
|
@@ -55,24 +77,44 @@ export function SessionStartForm({ userId, userEmail, listId: initialListId = ""
|
|
|
55
77
|
user_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
56
78
|
});
|
|
57
79
|
const sessionId = dataOf<{ session_id?: string }>(env).session_id;
|
|
58
|
-
if (!sessionId) throw new Error(
|
|
80
|
+
if (!sessionId) throw new Error(`${brand.linkLabel} did not return a session id`);
|
|
59
81
|
return { sessionId, mode, parallelCount: count, fromPhone: chosenPhone };
|
|
60
82
|
},
|
|
61
|
-
{ onSuccess: onCreated },
|
|
83
|
+
{ onSuccess: onCreated, onError: (e) => setRefused((e as { status?: number } | undefined)?.status === 403) },
|
|
62
84
|
);
|
|
63
85
|
|
|
64
86
|
return (
|
|
65
87
|
<form className={`space-y-3 rounded-lg border p-4 ${className ?? ""}`} onSubmit={(e) => { e.preventDefault(); if (listId && repId) void create.mutate(undefined); }}>
|
|
66
|
-
<h3 className="text-sm font-
|
|
88
|
+
<h3 className="text-sm font-medium">Start a dialing session</h3>
|
|
67
89
|
<div className="grid gap-3 sm:grid-cols-2">
|
|
68
|
-
<div className="space-y-1">
|
|
69
|
-
<
|
|
70
|
-
|
|
90
|
+
<div className="space-y-1 sm:col-span-2">
|
|
91
|
+
<span className="text-sm font-medium leading-none">List</span>
|
|
92
|
+
{initialListId ? (
|
|
93
|
+
<p className="text-sm">{title || `List ${initialListId}`}</p>
|
|
94
|
+
) : (
|
|
95
|
+
<ListPicker type="contacts" allowCreate={false} value={Number(listId) || null} onChange={(l) => { setListId(String(l.id)); setPickedTitle(l.title); setName((n) => n || `Dialing ${l.title}`); }} />
|
|
96
|
+
)}
|
|
97
|
+
<details className="text-sm">
|
|
98
|
+
<summary className="cursor-pointer text-muted-foreground">Use a list id instead</summary>
|
|
99
|
+
<div className="mt-1 space-y-1">
|
|
100
|
+
<Label htmlFor="ds-list">List id</Label>
|
|
101
|
+
<Input id="ds-list" value={listId} onChange={(e) => setListId(e.target.value)} placeholder="From your lists" />
|
|
102
|
+
</div>
|
|
103
|
+
</details>
|
|
71
104
|
</div>
|
|
72
105
|
{userId ? null : (
|
|
73
|
-
<div className="space-y-1">
|
|
74
|
-
<
|
|
75
|
-
<
|
|
106
|
+
<div className="space-y-1 sm:col-span-2">
|
|
107
|
+
<span className="text-sm font-medium leading-none">Rep</span>
|
|
108
|
+
{me.loading ? <p className="text-sm text-muted-foreground">Finding your user...</p> : null}
|
|
109
|
+
{resolved ? <p className="text-sm">Calling as {resolved.name}{resolved.id === me.userId ? " (you)" : ""}</p> : null}
|
|
110
|
+
<MemberPicker label={resolved ? "Switch rep" : "Rep"} value={resolved?.id ?? null} onChange={(m) => { setPicked(m); setTypedUserId(""); }} placeholder="Choose who is calling" />
|
|
111
|
+
<details className="text-sm">
|
|
112
|
+
<summary className="cursor-pointer text-muted-foreground">Use a user id instead</summary>
|
|
113
|
+
<div className="mt-1 space-y-1">
|
|
114
|
+
<Label htmlFor="ds-user">Rep user id</Label>
|
|
115
|
+
<Input id="ds-user" value={typedUserId} onChange={(e) => setTypedUserId(e.target.value)} placeholder={`${brand.linkLabel} user id`} />
|
|
116
|
+
</div>
|
|
117
|
+
</details>
|
|
76
118
|
</div>
|
|
77
119
|
)}
|
|
78
120
|
<div className="space-y-1">
|
|
@@ -103,6 +145,13 @@ export function SessionStartForm({ userId, userEmail, listId: initialListId = ""
|
|
|
103
145
|
</div>
|
|
104
146
|
{error ? <ApiError error={error} title="Could not load caller IDs" onRetry={() => void refetch()} /> : null}
|
|
105
147
|
{create.error ? <ApiError error={create.error} title="Could not create the session" /> : null}
|
|
148
|
+
{(create.error as { status?: number } | undefined)?.status === 403 ? (
|
|
149
|
+
<p role="alert" className="text-sm text-muted-foreground">
|
|
150
|
+
{me.userId && repId && repId !== me.userId
|
|
151
|
+
? "A personal API key can only act as its own user, so this session cannot be started for someone else. Switch the rep back to yourself."
|
|
152
|
+
: "This API key is not allowed to start dialer sessions. Check that it has the voice scopes, or ask an admin."}
|
|
153
|
+
</p>
|
|
154
|
+
) : null}
|
|
106
155
|
<Button type="submit" disabled={!listId || !repId || create.loading} loading={create.loading}>
|
|
107
156
|
Create session (dials no one yet)
|
|
108
157
|
</Button>
|
|
@@ -22,6 +22,7 @@ import { g8Get } from "@/g8/lib/g8-client";
|
|
|
22
22
|
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
23
23
|
import { dataOf } from "../lib/dialer-types";
|
|
24
24
|
import type { SoftphoneTransport } from "../lib/softphone-transport";
|
|
25
|
+
import { useBrand } from "@/g8/hooks/use-brand";
|
|
25
26
|
|
|
26
27
|
export interface SoftphoneProps {
|
|
27
28
|
/** The rep whose identity the credential is minted for. */
|
|
@@ -38,6 +39,7 @@ export interface SoftphoneProps {
|
|
|
38
39
|
export type SoftphoneState = "off" | "connecting" | "ready" | "in-call" | "error";
|
|
39
40
|
|
|
40
41
|
export function Softphone({ userId, transport, conferenceName, joinMuted, onStateChange, className }: SoftphoneProps) {
|
|
42
|
+
const brand = useBrand();
|
|
41
43
|
const [state, setState] = useState<SoftphoneState>("off");
|
|
42
44
|
const [muted, setMuted] = useState(false);
|
|
43
45
|
const [expiring, setExpiring] = useState(false);
|
|
@@ -61,7 +63,7 @@ export function Softphone({ userId, transport, conferenceName, joinMuted, onStat
|
|
|
61
63
|
const mint = useG8Mutation(async () => {
|
|
62
64
|
const env = await g8Get("/dialer/sessions/token", { user_id: userId });
|
|
63
65
|
const { token, identity } = dataOf<{ token?: string; identity?: string }>(env);
|
|
64
|
-
if (!token) throw new Error(
|
|
66
|
+
if (!token) throw new Error(`${brand.linkLabel} did not return a softphone credential`);
|
|
65
67
|
return { token, identity };
|
|
66
68
|
});
|
|
67
69
|
|
|
@@ -91,7 +93,7 @@ export function Softphone({ userId, transport, conferenceName, joinMuted, onStat
|
|
|
91
93
|
return (
|
|
92
94
|
<section className={`space-y-3 rounded-lg border p-4 ${className ?? ""}`} aria-label="Softphone">
|
|
93
95
|
<div className="flex items-center justify-between">
|
|
94
|
-
<h3 className="text-sm font-
|
|
96
|
+
<h3 className="text-sm font-medium">Softphone</h3>
|
|
95
97
|
<span role="status" className="text-sm text-muted-foreground">{label}</span>
|
|
96
98
|
</div>
|
|
97
99
|
{transportError ? <p role="alert" className="text-sm text-destructive">{transportError}</p> : null}
|
|
@@ -5,14 +5,26 @@ contacts of a list in one job, and manage the provider pipelines behind it. Buil
|
|
|
5
5
|
|
|
6
6
|
Install: `npx @graph8/sdk add enrichment` (installs `foundation`, `crm` and `lists` first).
|
|
7
7
|
|
|
8
|
+
## Screens
|
|
9
|
+
|
|
10
|
+
`EnrichmentScreen` is the finished enrichment page; mount it with no props. Tabs (`?tab=`): **Enrich a list** (pick a list,
|
|
11
|
+
tick contacts, run a bulk enrichment with preflight, confirmed price, progress and cancel; open a contact to look it up
|
|
12
|
+
or verify its email), **Look up** (one person or company, and email verification), **Pipelines** (a list's saved
|
|
13
|
+
provider waterfalls: create, delete) and **Providers** (graph8-funded vs your own key, credits per action; keys are
|
|
14
|
+
added in graph8 settings, so it links out). Every paid step goes through the confirmation dialog.
|
|
15
|
+
Also a **Jobs** tab (`?job=`, `EnrichmentJobDetail`: live progress, counts, credits, provider errors, cancel; the jobs running now come from `GET /enrichment/active-jobs` and jobs started this session are listed, so no id has to be pasted; an older job can still be opened by id) and a pipeline detail (`?pipeline=`: steps, credential status, missing keys). `?view=full` opens any detail full-page.
|
|
16
|
+
Slots: `toolbarActions`, `renderDetailActions(contact)`, `extraDetailTabs`.
|
|
17
|
+
|
|
18
|
+
Prerequisites: `crm` and `lists` installed, proxy allowlist `enrichment`, `lists`, `contacts`, `companies`; provider keys and credits as below.
|
|
19
|
+
|
|
8
20
|
## What you get
|
|
9
21
|
|
|
10
22
|
| Component | Use it for | Props |
|
|
11
23
|
|---|---|---|
|
|
12
24
|
| `EnrichButton` | Look up one person/company; optionally apply the found fields to the record | `kind`, `lookup`, `record`, `onResult`, `onApplied` |
|
|
13
|
-
| `EmailVerifyBadge` | Status chip (Unverified / Valid / Risky / Invalid)
|
|
25
|
+
| `EmailVerifyBadge` | Status chip (Unverified / Valid / Risky / Invalid / Couldn't verify) read from the verifier's own result codes: catch-all (`ok_for_all`) is Risky, a hard failure (`email_disabled`, `invalid_mx`, `dead_server`, `error`, ...) is Invalid and offers no second paid Verify, `unknown` (what the endpoint answers when the verifier itself fails, uncharged) is "Couldn't verify" with Try again. The verdict is shown, not saved to the contact (`POST /enrichment/verify-email` takes only `email`) | `email`, `initialStatus`, `onVerified`, `readOnly` |
|
|
14
26
|
| `BulkEnrichPanel` | Fill work email / phones for many contacts of a list: preflight, estimate, job, progress | `listId`, `contactIds`, `defaultProviders`, `onDone` |
|
|
15
|
-
| `WaterfallConfig` | Saved pipelines of a list and a form to create one | `listId`, `onChanged` |
|
|
27
|
+
| `WaterfallConfig` | Saved pipelines of a list and a form to create one. Creating is idempotent per field and list: if the list already has a pipeline for the field, the API returns it (`created: false`, its own providers) and the form says "Already exists, reused" | `listId`, `onChanged` |
|
|
16
28
|
|
|
17
29
|
`BulkEnrichPanel` with no props is a complete page (choose a list, tick contacts, enrich), so it can be mounted as a
|
|
18
30
|
route as is; give it `listId` and `contactIds` to embed it. For that reason this block depends on `lists`.
|
|
@@ -37,11 +49,20 @@ you can afford it; an under-funded run fails with 402.
|
|
|
37
49
|
Masked search results have no ids and cannot be enriched: save them to a list first (`search` block).
|
|
38
50
|
2. `POST /enrichment/preflight { list_id, contact_ids, mode: "waterfall", providers, target_field }` (free) ->
|
|
39
51
|
`contact_count`, `not_in_list`, `missing_credentials`, `warnings`, `credits_per_record`, `max_total_credits`.
|
|
40
|
-
3. The person confirms the estimate. `POST /enrichment/
|
|
41
|
-
|
|
52
|
+
3. The person confirms the estimate. `POST /enrichment/waterfall/configs { list_id, name, field: "CONTACT_WORK_EMAIL", providers }`
|
|
53
|
+
creates the list's pipeline for that field, or reuses the one it already has (`created: false`; the run then uses
|
|
54
|
+
ITS providers) -> `column_id`. Then `POST /enrichment/waterfall/enrich { column_id, list_id, record_ids }` -> 202
|
|
55
|
+
`{ job_id, status }`. A `status` of `"error"` (or a 502) means the job never started: show it as a failure, do not
|
|
56
|
+
poll. Providers must suit the field: phones take leadmagic, prospeo, apollo only (`FIELD_PROVIDERS`); the panel
|
|
57
|
+
resets the providers when the field changes. Before the confirm, the panel reads the list's pipelines
|
|
58
|
+
(`GET /enrichment/waterfall/configs?list_id`): when one already fills the field, the quote, the confirm and the run
|
|
59
|
+
use ITS providers (the API reuses it), and the panel says so instead of offering a provider editor.
|
|
60
|
+
`POST /enrichment/enrich` is not used: until the API fix of 2026-10-16 it dispatched the field name instead of a
|
|
61
|
+
pipeline and silently enriched nothing, so apps on older SDK copies should take this path.
|
|
42
62
|
4. Poll `GET /enrichment/jobs/{job_id}` (`useJobPoll`, backoff; there is no stream and no completion webhook) until
|
|
43
63
|
`status` is `completed`, `failed` or `cancelled`. It reports `completed/total`, `successful_enrichments`, `failed_enrichments`,
|
|
44
64
|
`total_credits_used`. If nothing was enriched, `?include_provider_errors=true` explains why (usually a missing provider key).
|
|
65
|
+
When the job ends the panel refreshes every cached `contacts` query and calls `onDone`, so tables and records reload.
|
|
45
66
|
5. `POST /enrichment/jobs/{job_id}/cancel` stops a running job; records already done keep their data and charge.
|
|
46
67
|
|
|
47
68
|
```tsx
|
|
@@ -85,7 +106,7 @@ Ask the person: who may spend credits (decide it in the proxy's `authorize`).
|
|
|
85
106
|
| File | Does |
|
|
86
107
|
|---|---|
|
|
87
108
|
| `recipes/lookup-and-verify.ts` | One person/company lookup and an email check |
|
|
88
|
-
| `recipes/bulk-enrich-and-poll.ts` | Preflight quote,
|
|
109
|
+
| `recipes/bulk-enrich-and-poll.ts` | Preflight quote, create or reuse the pipeline and run it, poll with backoff, read provider errors |
|
|
89
110
|
| `recipes/waterfall-pipeline.ts` | Provider readiness, create and list pipelines |
|
|
90
111
|
|
|
91
112
|
## Operations
|
|
@@ -95,11 +116,12 @@ Ask the person: who may spend credits (decide it in the proxy's `authorize`).
|
|
|
95
116
|
| lookupPerson / lookupCompany | POST /enrichment/lookup/person, /company | billable | EnrichButton |
|
|
96
117
|
| verifyEmail | POST /enrichment/verify-email | billable | EmailVerifyBadge |
|
|
97
118
|
| preflightEnrichment | POST /enrichment/preflight | read | BulkEnrichPanel |
|
|
98
|
-
| startEnrichment | POST /enrichment/enrich | billable |
|
|
119
|
+
| startEnrichment | POST /enrichment/enrich | billable | (not used by the components; see the flow) |
|
|
120
|
+
| runWaterfallPipeline | POST /enrichment/waterfall/enrich | billable | BulkEnrichPanel |
|
|
99
121
|
| getEnrichmentJob | GET /enrichment/jobs/{job_id} | read | BulkEnrichPanel |
|
|
100
122
|
| cancelEnrichmentJob | POST /enrichment/jobs/{job_id}/cancel | write | BulkEnrichPanel |
|
|
101
123
|
| listEnrichmentProviders | GET /enrichment/providers | read | BulkEnrichPanel, WaterfallConfig |
|
|
102
124
|
| listWaterfallConfigs | GET /enrichment/waterfall/configs | read | WaterfallConfig |
|
|
103
|
-
| createDefaultWaterfallConfig | POST /enrichment/waterfall/configs | billable | WaterfallConfig |
|
|
125
|
+
| createDefaultWaterfallConfig | POST /enrichment/waterfall/configs | billable | WaterfallConfig, BulkEnrichPanel |
|
|
104
126
|
| deleteWaterfallConfig | DELETE /enrichment/waterfall/configs/{column_id} | destructive | WaterfallConfig |
|
|
105
127
|
| updateContact / updateCompany | PATCH /contacts/{id}, /companies/{id} | write | EnrichButton (Apply) |
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
"single person/company lookup that can apply found fields to the record",
|
|
8
8
|
"email verification chip with a one-click verify",
|
|
9
9
|
"bulk enrichment with a free preflight, credit estimate confirmation, polled progress and cancel",
|
|
10
|
-
"waterfall pipeline manager with provider order and credential readiness"
|
|
10
|
+
"waterfall pipeline manager with provider order and credential readiness",
|
|
11
|
+
"finished EnrichmentScreen page"
|
|
11
12
|
],
|
|
12
13
|
"dependsOn": [
|
|
13
|
-
"lists"
|
|
14
|
+
"lists",
|
|
15
|
+
"pickers"
|
|
14
16
|
],
|
|
15
17
|
"operations": [
|
|
16
18
|
"cancel_enrichment_job_enrichment_jobs__job_id__cancel_post",
|
|
@@ -18,7 +20,11 @@
|
|
|
18
20
|
"create_list_lists_post",
|
|
19
21
|
"delete_contact_contacts__contact_id__delete",
|
|
20
22
|
"delete_waterfall_config_enrichment_waterfall_configs__column_id__delete",
|
|
23
|
+
"get_company_companies__company_id__get",
|
|
24
|
+
"get_contact_contacts__contact_id__get",
|
|
21
25
|
"get_enrichment_job_enrichment_jobs__job_id__get",
|
|
26
|
+
"list_active_enrichment_jobs_enrichment_active_jobs_get",
|
|
27
|
+
"list_companies_companies_get",
|
|
22
28
|
"list_contact_fields_fields_get",
|
|
23
29
|
"list_contacts_contacts_get",
|
|
24
30
|
"list_enrichment_providers_enrichment_providers_get",
|
|
@@ -27,6 +33,7 @@
|
|
|
27
33
|
"lookup_company_enrichment_lookup_company_post",
|
|
28
34
|
"lookup_person_enrichment_lookup_person_post",
|
|
29
35
|
"preflight_enrichment_enrichment_preflight_post",
|
|
36
|
+
"run_waterfall_pipeline_enrichment_waterfall_enrich_post",
|
|
30
37
|
"start_enrichment_enrichment_enrich_post",
|
|
31
38
|
"update_company_companies__company_id__patch",
|
|
32
39
|
"update_contact_contacts__contact_id__patch",
|
|
@@ -41,9 +48,12 @@
|
|
|
41
48
|
"lookup_person_enrichment_lookup_person_post",
|
|
42
49
|
"lookup_company_enrichment_lookup_company_post",
|
|
43
50
|
"update_contact_contacts__contact_id__patch",
|
|
44
|
-
"update_company_companies__company_id__patch"
|
|
51
|
+
"update_company_companies__company_id__patch",
|
|
52
|
+
"get_contact_contacts__contact_id__get",
|
|
53
|
+
"get_company_companies__company_id__get"
|
|
45
54
|
],
|
|
46
55
|
"tiers": [
|
|
56
|
+
"read",
|
|
47
57
|
"write",
|
|
48
58
|
"billable"
|
|
49
59
|
]
|
|
@@ -51,7 +61,7 @@
|
|
|
51
61
|
{
|
|
52
62
|
"name": "EmailVerifyBadge",
|
|
53
63
|
"file": "components/email-verify-badge.tsx",
|
|
54
|
-
"purpose": "Status chip for an email (Unverified, Valid, Risky, Invalid) with a confirmed 1-credit Verify action; never verifies a locked email.",
|
|
64
|
+
"purpose": "Status chip for an email (Unverified, Valid, Risky, Invalid, Couldn't verify) from the verifier's own result codes, with a confirmed 1-credit Verify action; never verifies a locked email.",
|
|
55
65
|
"operations": [
|
|
56
66
|
"verify_email_enrichment_verify_email_post"
|
|
57
67
|
],
|
|
@@ -62,11 +72,13 @@
|
|
|
62
72
|
{
|
|
63
73
|
"name": "BulkEnrichPanel",
|
|
64
74
|
"file": "components/bulk-enrich-panel.tsx",
|
|
65
|
-
"purpose": "Fill a field for many contacts of a list: free preflight, credit estimate in ConfirmSpendDialog,
|
|
75
|
+
"purpose": "Fill a field for many contacts of a list: free preflight, credit estimate in ConfirmSpendDialog, create or reuse the field's pipeline and run it, poll it with useJobPoll, cancel. With no props it is a whole page (pick a list, tick contacts).",
|
|
66
76
|
"operations": [
|
|
67
77
|
"list_enrichment_providers_enrichment_providers_get",
|
|
78
|
+
"list_waterfall_configs_enrichment_waterfall_configs_get",
|
|
68
79
|
"preflight_enrichment_enrichment_preflight_post",
|
|
69
|
-
"
|
|
80
|
+
"create_default_waterfall_config_enrichment_waterfall_configs_post",
|
|
81
|
+
"run_waterfall_pipeline_enrichment_waterfall_enrich_post",
|
|
70
82
|
"get_enrichment_job_enrichment_jobs__job_id__get",
|
|
71
83
|
"cancel_enrichment_job_enrichment_jobs__job_id__cancel_post",
|
|
72
84
|
"list_lists_lists_get",
|
|
@@ -97,6 +109,32 @@
|
|
|
97
109
|
"billable",
|
|
98
110
|
"destructive"
|
|
99
111
|
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "EnrichmentScreen",
|
|
115
|
+
"file": "components/enrichment-screen.tsx",
|
|
116
|
+
"purpose": "Finished enrichment page: enrich a list, one-off lookups and email verification, pipelines and the provider catalog.",
|
|
117
|
+
"operations": [
|
|
118
|
+
"list_contacts_contacts_get",
|
|
119
|
+
"list_companies_companies_get",
|
|
120
|
+
"list_active_enrichment_jobs_enrichment_active_jobs_get"
|
|
121
|
+
],
|
|
122
|
+
"tiers": [
|
|
123
|
+
"read"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "EnrichmentJobDetail",
|
|
128
|
+
"file": "components/enrichment-job-detail.tsx",
|
|
129
|
+
"purpose": "One bulk enrichment job: live progress, counts, credits, warnings, provider errors and cancel.",
|
|
130
|
+
"operations": [
|
|
131
|
+
"cancel_enrichment_job_enrichment_jobs__job_id__cancel_post",
|
|
132
|
+
"get_enrichment_job_enrichment_jobs__job_id__get"
|
|
133
|
+
],
|
|
134
|
+
"tiers": [
|
|
135
|
+
"read",
|
|
136
|
+
"write"
|
|
137
|
+
]
|
|
100
138
|
}
|
|
101
139
|
],
|
|
102
140
|
"hooks": [
|
|
@@ -124,12 +162,12 @@
|
|
|
124
162
|
{
|
|
125
163
|
"file": "recipes/bulk-enrich-and-poll.ts",
|
|
126
164
|
"title": "Enrich many contacts of a list and wait for the job",
|
|
127
|
-
"when": "Filling work emails or phones for a list. Order: preflight (free: counts, missing provider keys and the credit quote) -> a human approves the quote ->
|
|
165
|
+
"when": "Filling work emails or phones for a list. Order: preflight (free: counts, missing provider keys and the credit quote) -> a human approves the quote -> create or reuse the field's pipeline and run it -> poll it (the API has no streams or webhook for completion) -> read provider errors if nothing was written. Contact ids must be on the list; ids not on it are skipped and not charged. Never enrich contacts you only have as masked search results: save them to a list first."
|
|
128
166
|
},
|
|
129
167
|
{
|
|
130
168
|
"file": "recipes/waterfall-pipeline.ts",
|
|
131
169
|
"title": "Create a waterfall pipeline for a list and see which providers are ready",
|
|
132
|
-
"when": "Setting up \"try provider A, then B\" enrichment for a column once, then reusing it. Check the provider catalog first: graph8-funded providers spend credits when the pipeline runs, providers on your own key do not, and a provider without a key is skipped. Creating is idempotent by (list_id, name)."
|
|
170
|
+
"when": "Setting up \"try provider A, then B\" enrichment for a column once, then reusing it. Check the provider catalog first: graph8-funded providers spend credits when the pipeline runs, providers on your own key do not, and a provider without a key is skipped. Creating with `field` is idempotent per field and list: if the list already has a pipeline for that field it is returned with `created: false` and keeps its own providers, whatever name or providers you send (delete it first to change them). Without `field`, a pipeline is reused by (list_id, name)."
|
|
133
171
|
}
|
|
134
172
|
],
|
|
135
173
|
"prerequisites": [
|
|
@@ -159,6 +197,7 @@
|
|
|
159
197
|
}
|
|
160
198
|
],
|
|
161
199
|
"scopes": [
|
|
200
|
+
"companies:read",
|
|
162
201
|
"companies:write",
|
|
163
202
|
"contacts:delete",
|
|
164
203
|
"contacts:read",
|
|
@@ -174,7 +213,7 @@
|
|
|
174
213
|
"defaults": [
|
|
175
214
|
{
|
|
176
215
|
"decision": "Providers a bulk run tries",
|
|
177
|
-
"default": "prospeo then dropcontact
|
|
216
|
+
"default": "The API's own per field (work email: prospeo then dropcontact; phones: leadmagic, prospeo, apollo), kept to providers in the catalog that the field supports; editable in the panel. A list that already has a pipeline for the field runs with that pipeline's providers",
|
|
178
217
|
"askHuman": false
|
|
179
218
|
},
|
|
180
219
|
{
|
|
@@ -199,6 +238,7 @@
|
|
|
199
238
|
}
|
|
200
239
|
],
|
|
201
240
|
"peerDependencies": {
|
|
241
|
+
"lucide-react": ">=0.400.0",
|
|
202
242
|
"react": ">=18"
|
|
203
243
|
}
|
|
204
244
|
}
|