@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
|
@@ -45,21 +45,21 @@ export function StageEditor({ pipelineId, onChanged, className }: StageEditorPro
|
|
|
45
45
|
const saved = dataOf<Stage>(await g8Put(`${base}/stages/${encodeURIComponent(s.id)}`, { name: s.name, probability: s.probability ?? 0, stage_type: s.stage_type ?? "open", color: s.color ?? undefined }));
|
|
46
46
|
done(stages.map((x) => (x.id === saved.id ? { ...x, ...saved } : x)));
|
|
47
47
|
} finally { setSavingId(null); }
|
|
48
|
-
});
|
|
48
|
+
}, { invalidates: [["deals", "pipelines"]] });
|
|
49
49
|
const add = useG8Mutation(async (name: string) => {
|
|
50
50
|
const created = dataOf<Stage>(await g8Post(`${base}/stages`, { name, probability: 0, stage_type: "open" }));
|
|
51
51
|
done([...stages, created]);
|
|
52
52
|
setNewName("");
|
|
53
|
-
});
|
|
53
|
+
}, { invalidates: [["deals", "pipelines"]] });
|
|
54
54
|
const reorder = useG8Mutation(async (next: Stage[]) => {
|
|
55
55
|
await g8Put(`${base}/stages/reorder`, { stage_ids: next.map((s) => s.id) });
|
|
56
56
|
done(next);
|
|
57
|
-
});
|
|
57
|
+
}, { invalidates: [["deals", "pipelines"]] });
|
|
58
58
|
const remove = useG8Mutation(async (s: Stage) => {
|
|
59
59
|
await g8Delete(`${base}/stages/${encodeURIComponent(s.id)}`);
|
|
60
60
|
done(stages.filter((x) => x.id !== s.id));
|
|
61
61
|
setRemoving(null);
|
|
62
|
-
}, { onError: () => setRemoving(null) });
|
|
62
|
+
}, { invalidates: [["deals", "pipelines"]], onError: () => setRemoving(null) });
|
|
63
63
|
|
|
64
64
|
const patch = (id: string, change: Partial<Stage>) => setStages((all) => all.map((s) => (s.id === id ? { ...s, ...change } : s)));
|
|
65
65
|
const swap = (i: number, j: number) => { const next = [...stages]; [next[i], next[j]] = [next[j], next[i]]; void reorder.mutate(next); };
|
|
@@ -69,7 +69,7 @@ export function StageEditor({ pipelineId, onChanged, className }: StageEditorPro
|
|
|
69
69
|
if (pipeline.loading && !pipeline.data) return <Skeleton className="h-48 w-full" aria-label="Loading stages" />;
|
|
70
70
|
return (
|
|
71
71
|
<div className={`space-y-3 ${className ?? ""}`}>
|
|
72
|
-
<h2 className="text-base font-
|
|
72
|
+
<h2 className="text-base font-medium">{pipeline.data?.name} stages</h2>
|
|
73
73
|
{failure ? <ApiError error={failure} title="That stage change failed" /> : null}
|
|
74
74
|
<ol className="space-y-2">
|
|
75
75
|
{stages.map((s, i) => (
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* usePipelineDeals: the deals in one pipeline, all pages up to `maxDeals`, with an optimistic stage move.
|
|
5
5
|
*
|
|
6
6
|
* SDK operations:
|
|
7
|
-
* GET /deals?pipeline_id=&search=&owner_id=&page=&limit= (deals.listDeals) read
|
|
7
|
+
* GET /deals?pipeline_id=&search=&owner_id=&stage_id=&outcome=&stale_before=&has_activity=&close_after=&close_before=&has_contact=&page=&limit= (deals.listDeals) read
|
|
8
8
|
* PATCH /deals/{deal_id} (deals.updateDeal) write - the stage move
|
|
9
9
|
*
|
|
10
10
|
* `moveDeal` updates the board immediately and, if the API refuses, refetches the list (the server is the truth)
|
|
@@ -15,12 +15,26 @@
|
|
|
15
15
|
import { useCallback, useEffect, useState } from "react";
|
|
16
16
|
import { g8Get, g8Patch } from "@/g8/lib/g8-client";
|
|
17
17
|
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
18
|
+
import { invalidate } from "@/g8/lib/g8-cache";
|
|
18
19
|
import type { Deal } from "../lib/deals-types";
|
|
19
20
|
import { dataOf } from "../lib/deals-types";
|
|
20
21
|
|
|
21
22
|
export interface PipelineDealFilters {
|
|
22
23
|
search?: string;
|
|
23
24
|
ownerId?: string;
|
|
25
|
+
/** Only this stage (GET /deals stage_id). */
|
|
26
|
+
stageId?: string;
|
|
27
|
+
/** Exactly one of open, won or lost. */
|
|
28
|
+
outcome?: string;
|
|
29
|
+
/** Deals last touched before this timestamp OR never touched (GET /deals stale_before). */
|
|
30
|
+
staleBefore?: string;
|
|
31
|
+
hasActivity?: boolean;
|
|
32
|
+
/** Close date on or after this timestamp or YYYY-MM-DD (inclusive). */
|
|
33
|
+
closeAfter?: string;
|
|
34
|
+
/** Close date strictly before this timestamp or YYYY-MM-DD (exclusive). */
|
|
35
|
+
closeBefore?: string;
|
|
36
|
+
/** false returns the deals with no contact. */
|
|
37
|
+
hasContact?: boolean;
|
|
24
38
|
}
|
|
25
39
|
|
|
26
40
|
export interface MoveExtra {
|
|
@@ -35,14 +49,14 @@ export function usePipelineDeals(pipelineId: string | undefined, filters: Pipeli
|
|
|
35
49
|
async (signal) => {
|
|
36
50
|
const all: Deal[] = [];
|
|
37
51
|
for (let page = 1; all.length < maxDeals; page++) {
|
|
38
|
-
const env = await g8Get<Deal[]>("/deals", { pipeline_id: pipelineId, search: filters.search || undefined, owner_id: filters.ownerId || undefined, page, limit: 100 }, { signal });
|
|
52
|
+
const env = await g8Get<Deal[]>("/deals", { pipeline_id: pipelineId, search: filters.search || undefined, owner_id: filters.ownerId || undefined, stage_id: filters.stageId, outcome: filters.outcome, stale_before: filters.staleBefore, has_activity: filters.hasActivity, close_after: filters.closeAfter, close_before: filters.closeBefore, has_contact: filters.hasContact, page, limit: 100 }, { signal });
|
|
39
53
|
all.push(...(env.data ?? []));
|
|
40
54
|
if (!env.pagination?.has_next) break;
|
|
41
55
|
}
|
|
42
56
|
return all.slice(0, maxDeals);
|
|
43
57
|
},
|
|
44
|
-
[pipelineId, filters.search, filters.ownerId, maxDeals],
|
|
45
|
-
{ enabled: Boolean(pipelineId) },
|
|
58
|
+
[pipelineId, filters.search, filters.ownerId, filters.stageId, filters.outcome, filters.staleBefore, filters.hasActivity, filters.closeAfter, filters.closeBefore, filters.hasContact, maxDeals],
|
|
59
|
+
{ enabled: Boolean(pipelineId), key: ["deals", "list", "board", { pipelineId, search: filters.search || undefined, ownerId: filters.ownerId || undefined, stageId: filters.stageId, outcome: filters.outcome, staleBefore: filters.staleBefore, hasActivity: filters.hasActivity, closeAfter: filters.closeAfter, closeBefore: filters.closeBefore, hasContact: filters.hasContact, maxDeals }] },
|
|
46
60
|
);
|
|
47
61
|
useEffect(() => { if (query.data) setDeals(query.data); }, [query.data]);
|
|
48
62
|
|
|
@@ -53,6 +67,9 @@ export function usePipelineDeals(pipelineId: string | undefined, filters: Pipeli
|
|
|
53
67
|
try {
|
|
54
68
|
const saved = dataOf<Deal>(await g8Patch(`/deals/${encodeURIComponent(deal.id)}`, { stage_id: stageId, expected_revision: deal.revision ?? undefined, ...extra }));
|
|
55
69
|
setDeals((all) => all.map((d) => (d.id === deal.id ? { ...d, ...saved } : d)));
|
|
70
|
+
// The deal's revision changed: a cached detail or history of it is out of date.
|
|
71
|
+
void invalidate(["deals", "detail"]);
|
|
72
|
+
void invalidate(["deals", "history"]);
|
|
56
73
|
return saved;
|
|
57
74
|
} catch (err) {
|
|
58
75
|
// Roll back by re-reading the server's truth: a snapshot taken before the move could itself be stale.
|
|
@@ -13,7 +13,7 @@ export function usePipelines() {
|
|
|
13
13
|
const q = useG8Query(async (signal) => {
|
|
14
14
|
const env = await g8Get<Pipeline[]>("/deals/pipelines", undefined, { signal });
|
|
15
15
|
return (env.data ?? []).map((p) => ({ ...p, stages: sortedStages(p) }));
|
|
16
|
-
}, []);
|
|
16
|
+
}, [], { key: ["deals", "pipelines"] });
|
|
17
17
|
const pipelines = q.data ?? [];
|
|
18
18
|
return { ...q, pipelines, defaultPipeline: pipelines.find((p) => p.is_default) ?? pipelines[0] };
|
|
19
19
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shapes and pure helpers for the deal depth components: notes, tasks, contact roles, line items, close reasons
|
|
5
|
+
* and the list filters. Types only describe the fields the components read; the API may return more.
|
|
6
|
+
*
|
|
7
|
+
* Money: a deal's `amount` is in DOLLARS (major units); a LINE ITEM's `unit_amount` and `line_total` are in CENTS.
|
|
8
|
+
* Dates: date-only values are `YYYY-MM-DD` and are shifted with UTC arithmetic, never through a local Date.
|
|
9
|
+
*/
|
|
10
|
+
import { dataOf } from "./deals-types";
|
|
11
|
+
|
|
12
|
+
/** Buying-committee roles the API accepts on a deal contact (any other value is a 422). */
|
|
13
|
+
export const CONTACT_ROLES = ["champion", "decision_maker", "influencer", "blocker", "coach", "end_user"] as const;
|
|
14
|
+
|
|
15
|
+
export function roleLabel(role: string | null | undefined): string {
|
|
16
|
+
if (!role) return "No role";
|
|
17
|
+
const text = role.replace(/_/g, " ");
|
|
18
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DealNote {
|
|
22
|
+
id: string;
|
|
23
|
+
content: string;
|
|
24
|
+
created_by_name?: string | null;
|
|
25
|
+
created_at?: string | null;
|
|
26
|
+
updated_at?: string | null;
|
|
27
|
+
read_only?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DealTask {
|
|
31
|
+
id: string;
|
|
32
|
+
title: string;
|
|
33
|
+
description?: string | null;
|
|
34
|
+
due_date?: string | null;
|
|
35
|
+
status?: string | null;
|
|
36
|
+
priority?: number | null;
|
|
37
|
+
assignee_name?: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DealLineItem {
|
|
41
|
+
id?: string;
|
|
42
|
+
product_name: string;
|
|
43
|
+
description?: string | null;
|
|
44
|
+
quantity: number;
|
|
45
|
+
/** Cents. */
|
|
46
|
+
unit_amount: number;
|
|
47
|
+
discount_pct: number;
|
|
48
|
+
billing_frequency?: string | null;
|
|
49
|
+
sort_order?: number;
|
|
50
|
+
stripe_product_id?: string | null;
|
|
51
|
+
stripe_price_id?: string | null;
|
|
52
|
+
manual_product_id?: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface LineItemsPayload {
|
|
56
|
+
deal_id: string;
|
|
57
|
+
/** Dollars. */
|
|
58
|
+
amount?: number | null;
|
|
59
|
+
currency?: string | null;
|
|
60
|
+
line_items?: DealLineItem[];
|
|
61
|
+
amount_mismatch?: boolean;
|
|
62
|
+
/** Dollars. */
|
|
63
|
+
linked_quote_total?: number | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CloseReason {
|
|
67
|
+
id: string;
|
|
68
|
+
label: string;
|
|
69
|
+
kind: string;
|
|
70
|
+
/** Whether this person may archive it (created it themselves, or may manage all). */
|
|
71
|
+
canArchive: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Rows of a list response that is either a bare array or `{ items | tasks | notes | reasons | contacts: [] }`. */
|
|
75
|
+
export function rowsOf<T>(env: unknown): T[] {
|
|
76
|
+
const inner = dataOf<unknown>(env);
|
|
77
|
+
if (Array.isArray(inner)) return inner as T[];
|
|
78
|
+
const o = inner as Record<string, unknown> | null;
|
|
79
|
+
for (const key of ["items", "tasks", "notes", "reasons", "contacts"]) if (o && Array.isArray(o[key])) return o[key] as T[];
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The close reasons of `GET /deals/close-reasons`: `{ can_manage, reasons: [{ id, label, kind, archived? }] }`. */
|
|
84
|
+
export function reasonsOf(env: unknown): CloseReason[] {
|
|
85
|
+
const inner = dataOf<{ can_manage?: boolean | null; reasons?: Array<Record<string, unknown>> } | null>(env);
|
|
86
|
+
const manage = Boolean(inner?.can_manage);
|
|
87
|
+
return (inner?.reasons ?? [])
|
|
88
|
+
.filter((r) => !r.archived && !r.archived_at)
|
|
89
|
+
.map((r) => ({ id: String(r.id ?? ""), label: String(r.label ?? r.name ?? ""), kind: String(r.kind ?? ""), canArchive: manage || r.can_archive === true || r.can_delete === true }))
|
|
90
|
+
.filter((r) => r.id && r.label);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Whole cents from a dollars text input; null when it is not a non-negative number. */
|
|
94
|
+
export function dollarsToCents(text: string): number | null {
|
|
95
|
+
const n = Number(text.trim());
|
|
96
|
+
return text.trim() !== "" && Number.isFinite(n) && n >= 0 ? Math.round(n * 100) : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const centsToDollarsText = (cents: number): string => (cents / 100).toFixed(2);
|
|
100
|
+
|
|
101
|
+
export function lineTotalCents(l: Pick<DealLineItem, "unit_amount" | "quantity" | "discount_pct">): number {
|
|
102
|
+
return Math.round(l.unit_amount * l.quantity * (1 - Math.min(100, Math.max(0, l.discount_pct)) / 100));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function formatCents(cents: number | null | undefined, currency?: string | null): string {
|
|
106
|
+
if (cents == null) return "";
|
|
107
|
+
try {
|
|
108
|
+
return new Intl.NumberFormat(undefined, { style: "currency", currency: currency || "USD" }).format(cents / 100);
|
|
109
|
+
} catch {
|
|
110
|
+
return String(cents / 100);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** `date` (YYYY-MM-DD) shifted by whole days, in UTC so the result is the same in every time zone. */
|
|
115
|
+
export function addDaysUtc(date: string, days: number): string {
|
|
116
|
+
const d = new Date(`${date}T00:00:00Z`);
|
|
117
|
+
d.setUTCDate(d.getUTCDate() + days);
|
|
118
|
+
return d.toISOString().slice(0, 10);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Midnight UTC `days` before today, as an ISO timestamp. Stable all day, so it can sit in a query key. */
|
|
122
|
+
export function staleCutoff(days: number, now = new Date()): string {
|
|
123
|
+
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - days)).toISOString();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface DealFilterState {
|
|
127
|
+
ownerId: string;
|
|
128
|
+
stageId: string;
|
|
129
|
+
outcome: "" | "open" | "won" | "lost";
|
|
130
|
+
/** Only deals untouched for this many days (or never touched). "" = off. */
|
|
131
|
+
staleDays: "" | "7" | "14" | "30" | "60" | "90";
|
|
132
|
+
activity: "" | "yes" | "no";
|
|
133
|
+
/** Inclusive date range of the close date. */
|
|
134
|
+
closeFrom: string;
|
|
135
|
+
closeTo: string;
|
|
136
|
+
/** Only deals with no contact at all. */
|
|
137
|
+
orphaned: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const NO_FILTERS: DealFilterState = { ownerId: "", stageId: "", outcome: "", staleDays: "", activity: "", closeFrom: "", closeTo: "", orphaned: false };
|
|
141
|
+
|
|
142
|
+
export const filtersActive = (f: DealFilterState): boolean => (Object.keys(NO_FILTERS) as Array<keyof DealFilterState>).some((k) => f[k] !== NO_FILTERS[k]);
|
|
143
|
+
|
|
144
|
+
/** The `GET /deals` query for a filter state. `close_before` is exclusive, so the inclusive "to" date becomes the next day. */
|
|
145
|
+
export function filterQuery(f: DealFilterState, now = new Date()) {
|
|
146
|
+
return {
|
|
147
|
+
ownerId: f.ownerId || undefined,
|
|
148
|
+
stageId: f.stageId || undefined,
|
|
149
|
+
outcome: f.outcome || undefined,
|
|
150
|
+
staleBefore: f.staleDays ? staleCutoff(Number(f.staleDays), now) : undefined,
|
|
151
|
+
hasActivity: f.activity === "" ? undefined : f.activity === "yes",
|
|
152
|
+
closeAfter: f.closeFrom || undefined,
|
|
153
|
+
closeBefore: f.closeTo ? addDaysUtc(f.closeTo, 1) : undefined,
|
|
154
|
+
hasContact: f.orphaned ? false : undefined,
|
|
155
|
+
};
|
|
156
|
+
}
|
package/blocks/dialer/README.md
CHANGED
|
@@ -9,6 +9,28 @@ browser, record outcomes, and review transcripts. Copy `DialerWorkspace` for the
|
|
|
9
9
|
> voicemail, or mints a softphone credential without a confirm dialog first, and the block never auto-advances to
|
|
10
10
|
> the next batch.
|
|
11
11
|
|
|
12
|
+
## Screens
|
|
13
|
+
|
|
14
|
+
- `DialerScreen` (`components/dialer-screen.tsx`): **Session** tab picks a list (ListPicker), creates a session (dials
|
|
15
|
+
nobody), then runs the queue: every batch, voicemail drop, softphone credential and stop is confirmed; finished calls
|
|
16
|
+
open the outcome panel and transcript. **Sessions** tab lists every session; a row opens `DialerSessionDetail` (stats, details, calls, queue; pause, confirmed resume, confirmed stop; `?tab=sessions&session=<id>`). **History** tab lists the rep's calls. Props are optional: `userId` (overrides the rep; without it the
|
|
17
|
+
rep is the signed-in team member from `GET /team-members/me`, so the softphone works out of the box with a personal key,
|
|
18
|
+
and a `MemberPicker` switches rep where the API allows it; a personal key naming another rep gets a 403, which the form
|
|
19
|
+
explains; an organisation key is not a person, so the rep is chosen with the picker), `userEmail`, `listId`, `sessionId`, `transport` (default: the Twilio adapter, loaded
|
|
20
|
+
on demand, needs `@twilio/voice-sdk`). Resume a session with `?session=<id>`. Add `&view=full` to any detail (session, call, and the same in the other screens) to open it as a full page.
|
|
21
|
+
- `CallLogScreen` (`components/call-log-screen.tsx`): history of one rep, contact or session with outcome filter and search;
|
|
22
|
+
open a call for recording, transcript and AI grade (`?id=<room_name>`), and **Call back** the number (`CallContactButton`). Needs a rep email (it asks) or a prop.
|
|
23
|
+
- Slots on both: `toolbarActions`, `renderRowActions`, `renderDetailActions`, `extraDetailTabs`.
|
|
24
|
+
- `CallContactButton` (`components/call-contact-button.tsx`): **call one contact from any screen**. Drop it into a
|
|
25
|
+
contact page's `renderDetailActions` (the apollo and outreach stacks do) or a row slot: `<CallContactButton contact={c} />`.
|
|
26
|
+
Closed, it loads nothing. Open: pick the number (masked `***` numbers are never offered), a PAUSED or COMPLETED
|
|
27
|
+
session to log the call under, and the caller ID; turn on the softphone; confirm (external, 1 recipient). It calls
|
|
28
|
+
`POST /dialer/parallel-calls/manual-dial` and the softphone joins the returned `conference_name`. The rep is
|
|
29
|
+
`userId` if passed, else the personal key's user (`GET /team-members/me`); an organisation key is not a rep, so it
|
|
30
|
+
then asks for `userId`. Why not `next-batch` with `force_next_contact`: that only picks a contact already in the
|
|
31
|
+
session's list and dials a whole batch.
|
|
32
|
+
Scopes: `voice:read`, `voice:write`, `voice:run` (and `team:read` for `CallContactButton` without `userId`). A phone number must exist (buy or verify it in the graph8 app).
|
|
33
|
+
|
|
12
34
|
## How it works (read this before building)
|
|
13
35
|
|
|
14
36
|
- graph8 places the calls **from its servers** into a conference. The rep's browser only **joins that
|
|
@@ -70,8 +92,10 @@ Softphone), `call_id` = `room_name` (live call -> disposition -> transcript -> r
|
|
|
70
92
|
|
|
71
93
|
## Stack nav pages
|
|
72
94
|
|
|
73
|
-
`CallQueue` and `CallLog` render with no props (a stack uses them as pages): the start form
|
|
74
|
-
|
|
95
|
+
`CallQueue` and `CallLog` render with no props (a stack uses them as pages): the start form defaults the rep to the
|
|
96
|
+
signed-in team member (a `MemberPicker` switches it; the raw user id is only a fallback under "Use a user id instead"),
|
|
97
|
+
the list is a `ListPicker`, and the call log shows the signed-in rep's calls (a rep email can still be typed). Pass
|
|
98
|
+
`userId` / `userEmail` from your session to override.
|
|
75
99
|
|
|
76
100
|
## Gotchas
|
|
77
101
|
|
|
@@ -81,13 +105,22 @@ and the call log for a rep email. Pass `userId` / `userEmail` from your session
|
|
|
81
105
|
- Voicemail drop plays the rep's cloned voice to a stranger's phone. The confirm dialog names the contact.
|
|
82
106
|
- Another tenant's session id is a 404, the same as a missing one.
|
|
83
107
|
- Manual dial (`POST /dialer/parallel-calls/manual-dial`, external) needs a PAUSED or COMPLETED session and can
|
|
84
|
-
create a contact
|
|
108
|
+
create a contact (an unknown number is matched to a contact or one is created). `CallContactButton` uses it.
|
|
109
|
+
- **Late join.** The conference name of a batch is only known from the `next-batch` (or `manual-dial`) response; the
|
|
110
|
+
app learns the NEXT batch's name earlier over a WebSocket the API does not have, and the session read does not
|
|
111
|
+
expose it. Voice waits up to 2 s for the rep only when the name was announced, so an SDK softphone joins after the
|
|
112
|
+
dials go out and the first second of an instant answer can be missed (voice's late-join reconciliation still
|
|
113
|
+
connects the audio). Keep the softphone registered BEFORE dialing (`CallContactButton` only enables the call once
|
|
114
|
+
it is ready) so the join is a single `joinConference` the moment the response lands.
|
|
115
|
+
- `next-batch` `user_email`: with a personal key the dialer's rep is the key's own user (naming someone else is a
|
|
116
|
+
403); with an organisation key the name is passed through but not vouched for, so voice keeps dialing without
|
|
117
|
+
moving the session to that rep.
|
|
85
118
|
|
|
86
119
|
## Operations
|
|
87
120
|
|
|
88
121
|
| Operation | Method and path | Tier | Used by |
|
|
89
122
|
|---|---|---|---|
|
|
90
|
-
| `voice.listDialerNumbers` | GET /voice/dialer/numbers | read | SessionStartForm |
|
|
123
|
+
| `voice.listDialerNumbers` | GET /voice/dialer/numbers | read | SessionStartForm, CallContactButton |
|
|
91
124
|
| `dialer.createSessionDraft` | POST /dialer/sessions/draft | write | SessionStartForm |
|
|
92
125
|
| `dialer.readQueueStats` | GET /dialer/sessions/{id}/queue-stats | read | useDialerSession |
|
|
93
126
|
| `voice.getDialerSession` | GET /dialer/sessions/{id} | read | useDialerSession |
|
|
@@ -106,9 +139,17 @@ and the call log for a rep email. Pass `userId` / `userEmail` from your session
|
|
|
106
139
|
| `voice.getCallTranscript` | GET /voice/dialer/calls/{room}/transcript | read | TranscriptViewer |
|
|
107
140
|
| `voice.getCallGrading` | GET /voice/dialer/calls/{room}/grading | read | TranscriptViewer |
|
|
108
141
|
| `voice.downloadCallRecording` | GET /voice/calls/{id}/recording | read | TranscriptViewer |
|
|
142
|
+
| `teamMembers.getMyTeamMember` | GET /team-members/me | read | CallContactButton, DialerScreen, SessionStartForm, CallLogScreen (only without `userId` / `userEmail`; via the pickers block `useCurrentMember`) |
|
|
143
|
+
| `voice.listDialerSessions` | GET /voice/dialer/sessions | read | CallContactButton |
|
|
144
|
+
| `dialer.manualDial` | POST /dialer/parallel-calls/manual-dial | external | CallContactButton |
|
|
109
145
|
|
|
110
146
|
## Recipes (server side)
|
|
111
147
|
|
|
112
148
|
- `01-create-session-and-dial.ts`: draft, one confirmed batch, poll queue-stats.
|
|
113
149
|
- `02-disposition-calls.ts`: record outcomes from the org's accepted list.
|
|
114
150
|
- `03-review-a-call.ts`: transcript, AI grade, rep performance (`dialerAnalytics`).
|
|
151
|
+
|
|
152
|
+
## Cross-block glue
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
- `RecordCallsTab` (`components/record-calls-tab.tsx`): a contact's call history (`listCallsForContact`, 25 then "Show more" up to the API maximum of 100, then a notice) with a confirmed `CallContactButton`. `extraDetailTabs` entry for ContactsScreen.
|
package/blocks/dialer/block.json
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"call log, transcript, AI grading and recording per call"
|
|
12
12
|
],
|
|
13
13
|
"dependsOn": [
|
|
14
|
-
"lists"
|
|
14
|
+
"lists",
|
|
15
|
+
"pickers"
|
|
15
16
|
],
|
|
16
17
|
"operations": [
|
|
17
18
|
"create_session_draft_dialer_sessions_draft_post",
|
|
@@ -21,15 +22,20 @@
|
|
|
21
22
|
"get_call_grading_voice_dialer_calls__room_name__grading_get",
|
|
22
23
|
"get_call_transcript_voice_dialer_calls__room_name__transcript_get",
|
|
23
24
|
"get_dialer_session_dialer_sessions__session_id__get",
|
|
25
|
+
"get_my_team_member_team_members_me_get",
|
|
24
26
|
"hangup_session_call_dialer_sessions_calls_hangup_post",
|
|
25
27
|
"list_calls_for_contact_voice_dialer_calls_get",
|
|
26
28
|
"list_dialer_numbers_voice_dialer_numbers_get",
|
|
29
|
+
"list_dialer_sessions_voice_dialer_sessions_get",
|
|
30
|
+
"list_team_members_team_members_get",
|
|
27
31
|
"list_voice_dispositions_voice_dispositions_get",
|
|
32
|
+
"manual_dial_dialer_parallel_calls_manual_dial_post",
|
|
28
33
|
"mint_softphone_token_dialer_sessions_token_get",
|
|
29
34
|
"mute_call_dialer_sessions_calls_mute_post",
|
|
30
35
|
"pause_dialer_session_dialer_sessions__session_id__pause_post",
|
|
31
36
|
"read_queue_stats_dialer_sessions__session_id__queue_stats_get",
|
|
32
37
|
"read_session_grid_dialer_sessions__session_id__grid_post",
|
|
38
|
+
"resume_dialer_session_voice_dialer_sessions__session_id__resume_post",
|
|
33
39
|
"set_call_disposition_dialer_parallel_calls_calls__livekit_room_name__disposition_patch",
|
|
34
40
|
"skip_contact_dialer_sessions__session_id__calls_skip_contact_post",
|
|
35
41
|
"stop_dialer_session_dialer_sessions__session_id__stop_post"
|
|
@@ -60,7 +66,9 @@
|
|
|
60
66
|
"purpose": "Create a draft session (dials nobody) for a list, caller ID and mode.",
|
|
61
67
|
"operations": [
|
|
62
68
|
"create_session_draft_dialer_sessions_draft_post",
|
|
63
|
-
"
|
|
69
|
+
"get_my_team_member_team_members_me_get",
|
|
70
|
+
"list_dialer_numbers_voice_dialer_numbers_get",
|
|
71
|
+
"list_team_members_team_members_get"
|
|
64
72
|
],
|
|
65
73
|
"tiers": [
|
|
66
74
|
"read",
|
|
@@ -135,6 +143,72 @@
|
|
|
135
143
|
"purpose": "The whole rep screen wiring queue, softphone, outcome panel and transcript together.",
|
|
136
144
|
"operations": [],
|
|
137
145
|
"tiers": []
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "DialerScreen",
|
|
149
|
+
"file": "components/dialer-screen.tsx",
|
|
150
|
+
"purpose": "Finished calling page: pick a list, run a session, softphone, outcomes, transcript and history.",
|
|
151
|
+
"operations": [
|
|
152
|
+
"get_dialer_session_dialer_sessions__session_id__get",
|
|
153
|
+
"list_dialer_sessions_voice_dialer_sessions_get"
|
|
154
|
+
],
|
|
155
|
+
"tiers": [
|
|
156
|
+
"read"
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "CallLogScreen",
|
|
161
|
+
"file": "components/call-log-screen.tsx",
|
|
162
|
+
"purpose": "Finished call history page with outcome filter, recording, transcript and AI grade, and Call back on a call (CallContactButton, confirmed); also exports CallHistory.",
|
|
163
|
+
"operations": [
|
|
164
|
+
"list_calls_for_contact_voice_dialer_calls_get"
|
|
165
|
+
],
|
|
166
|
+
"tiers": [
|
|
167
|
+
"read"
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "DialerSessionDetail",
|
|
172
|
+
"file": "components/dialer-session-detail.tsx",
|
|
173
|
+
"purpose": "One dialing session as a full record: stats, details, calls and queue, with pause, confirmed resume and confirmed stop.",
|
|
174
|
+
"operations": [
|
|
175
|
+
"get_dialer_session_dialer_sessions__session_id__get",
|
|
176
|
+
"pause_dialer_session_dialer_sessions__session_id__pause_post",
|
|
177
|
+
"read_queue_stats_dialer_sessions__session_id__queue_stats_get",
|
|
178
|
+
"resume_dialer_session_voice_dialer_sessions__session_id__resume_post",
|
|
179
|
+
"stop_dialer_session_dialer_sessions__session_id__stop_post"
|
|
180
|
+
],
|
|
181
|
+
"tiers": [
|
|
182
|
+
"read",
|
|
183
|
+
"write",
|
|
184
|
+
"external"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "CallContactButton",
|
|
189
|
+
"file": "components/call-contact-button.tsx",
|
|
190
|
+
"purpose": "Call one contact from any screen (contact page, table row): pick the number, a paused or completed session and the caller ID, turn on the softphone, confirm (external), and the softphone joins the call.",
|
|
191
|
+
"operations": [
|
|
192
|
+
"get_my_team_member_team_members_me_get",
|
|
193
|
+
"list_dialer_sessions_voice_dialer_sessions_get",
|
|
194
|
+
"list_dialer_numbers_voice_dialer_numbers_get",
|
|
195
|
+
"manual_dial_dialer_parallel_calls_manual_dial_post"
|
|
196
|
+
],
|
|
197
|
+
"tiers": [
|
|
198
|
+
"read",
|
|
199
|
+
"external"
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"name": "RecordCallsTab",
|
|
204
|
+
"file": "components/record-calls-tab.tsx",
|
|
205
|
+
"purpose": "One contact's call history with a confirmed Call button; an extraDetailTabs entry for ContactsScreen.",
|
|
206
|
+
"operations": [
|
|
207
|
+
"list_calls_for_contact_voice_dialer_calls_get"
|
|
208
|
+
],
|
|
209
|
+
"tiers": [
|
|
210
|
+
"read"
|
|
211
|
+
]
|
|
138
212
|
}
|
|
139
213
|
],
|
|
140
214
|
"hooks": [
|
|
@@ -222,6 +296,7 @@
|
|
|
222
296
|
}
|
|
223
297
|
],
|
|
224
298
|
"scopes": [
|
|
299
|
+
"team:read",
|
|
225
300
|
"voice:read",
|
|
226
301
|
"voice:run",
|
|
227
302
|
"voice:write"
|
|
@@ -251,10 +326,16 @@
|
|
|
251
326
|
"decision": "Browser audio vendor for the Softphone",
|
|
252
327
|
"default": "Twilio via lib/twilio-transport.ts; graph8's dialer runs on Twilio conferences. For another vendor write a SoftphoneTransport and delete twilio-transport.ts",
|
|
253
328
|
"askHuman": true
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"decision": "How a single contact is called outside a dialing session",
|
|
332
|
+
"default": "manual-dial under a PAUSED or COMPLETED session the rep picks (same call log and analytics), only once the softphone is ready; the rep is the personal key's user unless userId is passed",
|
|
333
|
+
"askHuman": false
|
|
254
334
|
}
|
|
255
335
|
],
|
|
256
336
|
"peerDependencies": {
|
|
257
337
|
"react": ">=18",
|
|
258
|
-
"@twilio/voice-sdk": "^2.18.5"
|
|
338
|
+
"@twilio/voice-sdk": "^2.18.5",
|
|
339
|
+
"lucide-react": ">=0.400.0"
|
|
259
340
|
}
|
|
260
341
|
}
|