@graph8/sdk 0.370.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 +19 -0
- package/blocks/_foundation/README.md +14 -0
- package/blocks/_foundation/block.json +44 -0
- package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
- package/blocks/_foundation/components/empty-state.tsx +5 -2
- package/blocks/_foundation/components/list-select.tsx +112 -0
- package/blocks/_foundation/components/paging-notices.tsx +55 -0
- package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
- package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
- package/blocks/_foundation/components/screen/index.ts +1 -0
- package/blocks/_foundation/components/screen/split-view.tsx +1 -1
- package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
- package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
- package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
- package/blocks/_foundation/lib/paging.ts +57 -0
- package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
- package/blocks/agents/README.md +81 -4
- package/blocks/agents/block.json +311 -2
- package/blocks/agents/components/agent-activity.tsx +112 -0
- package/blocks/agents/components/agent-approvals.tsx +135 -0
- package/blocks/agents/components/agent-builder.tsx +17 -3
- package/blocks/agents/components/agent-conversations.tsx +143 -0
- package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
- package/blocks/agents/components/agent-operators.tsx +164 -0
- package/blocks/agents/components/agent-run-detail.tsx +20 -3
- package/blocks/agents/components/agent-runs.tsx +3 -3
- package/blocks/agents/components/agent-skills-panel.tsx +7 -7
- package/blocks/agents/components/agent-test-history.tsx +121 -0
- package/blocks/agents/components/agents-screen.tsx +53 -11
- package/blocks/agents/components/assistant-sessions.tsx +150 -0
- package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
- package/blocks/agents/components/from-template-button.tsx +65 -0
- package/blocks/agents/components/knowledge-browser.tsx +107 -0
- package/blocks/agents/components/knowledge-detail.tsx +156 -0
- package/blocks/agents/components/knowledge-form.tsx +69 -0
- package/blocks/agents/components/name-dialog.tsx +32 -0
- package/blocks/agents/components/receptionist-button.tsx +46 -0
- package/blocks/agents/components/save-as-template-button.tsx +32 -0
- package/blocks/agents/components/skill-editor.tsx +147 -0
- package/blocks/agents/components/skill-library.tsx +111 -0
- package/blocks/agents/lib/agent-types.ts +7 -1
- package/blocks/agents/lib/facts.tsx +13 -0
- package/blocks/agents/lib/knowledge-utils.ts +49 -0
- package/blocks/agents/lib/load-more.tsx +141 -0
- package/blocks/agents/lib/skill-utils.ts +91 -0
- package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
- package/blocks/analytics/components/analytics-screen.tsx +16 -3
- package/blocks/analytics/components/kpi-tiles.tsx +1 -1
- package/blocks/analytics/components/report-builder.tsx +15 -2
- package/blocks/appointments/README.md +6 -0
- package/blocks/appointments/block.json +27 -0
- package/blocks/appointments/components/booking-page.tsx +2 -2
- package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
- package/blocks/appointments/components/meeting-detail.tsx +2 -2
- package/blocks/appointments/components/meeting-list.tsx +29 -13
- package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
- package/blocks/campaigns/components/campaign-assets.tsx +1 -1
- package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
- package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
- package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
- package/blocks/campaigns/components/icp-detail.tsx +11 -2
- package/blocks/campaigns/components/icp-editor.tsx +8 -3
- package/blocks/campaigns/components/persona-detail.tsx +11 -2
- package/blocks/campaigns/components/persona-editor.tsx +6 -1
- package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
- package/blocks/coach/components/call-scorecard.tsx +7 -7
- package/blocks/company-intelligence/README.md +132 -0
- package/blocks/company-intelligence/block.json +318 -0
- package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
- package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
- package/blocks/company-intelligence/components/company-profile.tsx +68 -0
- package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
- package/blocks/company-intelligence/components/report-detail.tsx +189 -0
- package/blocks/company-intelligence/components/research-reports.tsx +136 -0
- package/blocks/company-intelligence/components/research-run.tsx +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/components/copilot-memory.tsx +6 -2
- package/blocks/copilot/components/copilot-panel.tsx +1 -1
- package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
- package/blocks/crm/README.md +6 -0
- package/blocks/crm/block.json +53 -4
- package/blocks/crm/components/companies-screen.tsx +23 -9
- package/blocks/crm/components/company-detail.tsx +2 -2
- package/blocks/crm/components/contact-detail.tsx +4 -4
- package/blocks/crm/components/contact-lookup-link.tsx +44 -0
- package/blocks/crm/components/contacts-screen.tsx +31 -13
- package/blocks/crm/components/dedupe-review.tsx +6 -1
- package/blocks/crm/components/import-wizard.tsx +3 -7
- package/blocks/crm/components/list-select.tsx +20 -0
- package/blocks/crm/components/record-engagement-tab.tsx +103 -0
- package/blocks/crm/components/record-form.tsx +1 -1
- package/blocks/crm/components/tasks-screen.tsx +35 -15
- package/blocks/crm/hooks/use-all-lists.ts +18 -0
- package/blocks/crm/hooks/use-page-query.ts +3 -1
- package/blocks/crm/lib/crm-types.ts +14 -0
- package/blocks/crm/lib/read-lists.ts +16 -0
- package/blocks/deals/README.md +24 -6
- package/blocks/deals/block.json +151 -2
- 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 +2 -2
- package/blocks/deals/components/deal-filters.tsx +65 -0
- package/blocks/deals/components/deal-line-items.tsx +120 -0
- package/blocks/deals/components/deal-notes-tab.tsx +97 -0
- package/blocks/deals/components/deal-related.tsx +30 -9
- package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
- package/blocks/deals/components/deals-screen.tsx +57 -22
- package/blocks/deals/components/forecast-view.tsx +4 -4
- package/blocks/deals/components/pipeline-board.tsx +8 -6
- package/blocks/deals/components/pipeline-editor.tsx +132 -0
- package/blocks/deals/components/record-deals-tab.tsx +62 -0
- package/blocks/deals/components/stage-editor.tsx +1 -1
- package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
- package/blocks/deals/lib/deal-extras.ts +156 -0
- package/blocks/dialer/README.md +14 -5
- package/blocks/dialer/block.json +17 -2
- package/blocks/dialer/components/call-contact-button.tsx +26 -9
- package/blocks/dialer/components/call-log-screen.tsx +4 -1
- package/blocks/dialer/components/call-log.tsx +16 -4
- package/blocks/dialer/components/call-queue.tsx +3 -3
- package/blocks/dialer/components/dialer-screen.tsx +69 -27
- package/blocks/dialer/components/disposition-panel.tsx +1 -1
- package/blocks/dialer/components/record-calls-tab.tsx +101 -0
- package/blocks/dialer/components/session-start-form.tsx +58 -11
- package/blocks/dialer/components/softphone.tsx +1 -1
- package/blocks/enrichment/README.md +16 -6
- package/blocks/enrichment/block.json +19 -7
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +76 -27
- package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
- package/blocks/enrichment/components/enrich-button.tsx +1 -1
- package/blocks/enrichment/components/enrichment-screen.tsx +70 -17
- package/blocks/enrichment/components/waterfall-config.tsx +27 -7
- package/blocks/enrichment/lib/enrichment-types.ts +39 -0
- package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
- package/blocks/forms/README.md +8 -0
- package/blocks/forms/block.json +107 -0
- package/blocks/forms/components/form-activity.tsx +71 -0
- package/blocks/forms/components/form-backfill.tsx +108 -0
- package/blocks/forms/components/form-builder.tsx +1 -1
- package/blocks/forms/components/form-embed.tsx +1 -1
- package/blocks/forms/components/form-groups.tsx +151 -0
- package/blocks/forms/components/form-mapping-editor.tsx +6 -9
- package/blocks/forms/components/form-route-history.tsx +72 -0
- package/blocks/forms/components/form-saved-views.tsx +81 -0
- package/blocks/forms/components/forms-screen.tsx +51 -17
- package/blocks/forms/components/more-rows.tsx +30 -0
- package/blocks/forms/components/submissions-export.tsx +67 -0
- package/blocks/forms/components/submissions-table.tsx +15 -6
- package/blocks/forms/lib/forms-extra.ts +67 -0
- package/blocks/inbox/README.md +5 -0
- package/blocks/inbox/block.json +15 -0
- package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
- package/blocks/inbox/components/thread-badge.tsx +2 -2
- package/blocks/inbox/components/thread-view.tsx +1 -1
- package/blocks/integrations/README.md +54 -1
- package/blocks/integrations/block.json +189 -15
- package/blocks/integrations/components/ad-accounts.tsx +82 -0
- package/blocks/integrations/components/connection-health.tsx +59 -0
- package/blocks/integrations/components/crm-detail.tsx +20 -1
- package/blocks/integrations/components/crm-records.tsx +103 -0
- package/blocks/integrations/components/field-mappings.tsx +61 -0
- package/blocks/integrations/components/integration-list.tsx +4 -4
- package/blocks/integrations/components/integrations-screen.tsx +20 -3
- package/blocks/integrations/components/migration-panel.tsx +133 -0
- package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
- package/blocks/integrations/components/sync-status.tsx +6 -6
- package/blocks/integrations/components/sync-triggers.tsx +134 -0
- package/blocks/integrations/components/webhook-detail.tsx +26 -7
- package/blocks/integrations/components/webhook-manager.tsx +30 -17
- package/blocks/integrations/components/workspace-tools.tsx +127 -0
- package/blocks/integrations/lib/crm-utils.ts +39 -0
- package/blocks/integrations/lib/facts.tsx +13 -0
- package/blocks/integrations/lib/integrations-utils.ts +5 -0
- package/blocks/integrations/lib/load-more.tsx +141 -0
- package/blocks/intent/components/account-intent-score.tsx +5 -5
- package/blocks/intent/components/intent-feed.tsx +8 -8
- package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
- package/blocks/intent/lib/intent-utils.ts +4 -4
- package/blocks/lifecycle/components/account-related.tsx +8 -1
- package/blocks/lifecycle/components/churn-list.tsx +5 -2
- package/blocks/lifecycle/components/customer-table.tsx +1 -1
- package/blocks/lists/README.md +7 -1
- package/blocks/lists/block.json +31 -3
- package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
- package/blocks/lists/components/add-to-list-button.tsx +17 -7
- package/blocks/lists/components/list-picker.tsx +13 -3
- package/blocks/lists/components/list-table.tsx +1 -1
- package/blocks/lists/components/lists-screen.tsx +4 -2
- package/blocks/lists/components/record-lists-tab.tsx +99 -0
- package/blocks/lists/components/suppression-manager.tsx +27 -6
- package/blocks/lists/components/suppressions-screen.tsx +21 -12
- package/blocks/mailboxes/block.json +4 -0
- package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
- package/blocks/mailboxes/components/domain-detail.tsx +10 -4
- package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
- package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
- package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
- package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
- package/blocks/mailboxes/components/warmup-health.tsx +20 -20
- package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
- package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
- package/blocks/newsletter/README.md +12 -0
- package/blocks/newsletter/block.json +117 -0
- package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
- package/blocks/newsletter/components/issue-versions.tsx +93 -0
- package/blocks/newsletter/components/more-rows.tsx +30 -0
- package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
- package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
- package/blocks/newsletter/components/newsletter-screen.tsx +36 -13
- package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
- package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
- package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
- package/blocks/newsletter/components/subscriber-table.tsx +5 -7
- package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
- package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
- package/blocks/nurture/components/nurture-builder.tsx +10 -2
- package/blocks/nurture/components/nurture-enroll.tsx +6 -7
- package/blocks/nurture/components/nurture-form.tsx +5 -8
- package/blocks/nurture/components/nurture-screen.tsx +16 -5
- package/blocks/nurture/components/nurture-stats.tsx +5 -4
- package/blocks/objects/README.md +1 -1
- package/blocks/objects/block.json +13 -5
- package/blocks/objects/components/attribute-field-input.tsx +14 -1
- package/blocks/objects/components/object-record-detail.tsx +8 -1
- package/blocks/objects/components/object-schema-editor.tsx +1 -1
- package/blocks/objects/components/record-roles-panel.tsx +15 -5
- package/blocks/pages/README.md +14 -0
- package/blocks/pages/block.json +113 -1
- package/blocks/pages/components/more-rows.tsx +30 -0
- package/blocks/pages/components/page-ai-tools.tsx +196 -0
- package/blocks/pages/components/page-analytics.tsx +176 -0
- package/blocks/pages/components/page-clone.tsx +99 -0
- package/blocks/pages/components/page-domains.tsx +167 -0
- package/blocks/pages/components/page-editor.tsx +1 -1
- package/blocks/pages/components/page-generation-status.tsx +48 -0
- package/blocks/pages/components/page-tracking.tsx +137 -0
- package/blocks/pages/components/pages-screen.tsx +57 -16
- package/blocks/pages/components/seo-report.tsx +6 -1
- package/blocks/pages/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 +48 -1
- package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
- 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 +3 -2
- package/blocks/quotes/components/quote-viewer.tsx +7 -2
- package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
- package/blocks/radar/components/competitor-battle-card.tsx +1 -1
- package/blocks/radar/components/radar-overview.tsx +4 -4
- package/blocks/radar/components/radar-screen.tsx +2 -2
- package/blocks/sequencer/README.md +6 -0
- package/blocks/sequencer/block.json +28 -1
- package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
- package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
- package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
- package/blocks/sequencer/components/sequence-builder.tsx +19 -20
- package/blocks/sequencer/components/sequence-detail.tsx +4 -2
- package/blocks/sequencer/components/sequence-enrollments.tsx +4 -2
- package/blocks/sequencer/components/sequence-list.tsx +1 -1
- package/blocks/sequencer/components/sequence-stats.tsx +8 -8
- package/blocks/sequencer/components/sequences-screen.tsx +3 -0
- package/blocks/sequencer/components/step-editor.tsx +11 -11
- package/blocks/sequencer/lib/sequencer-utils.ts +5 -5
- package/blocks/social-ads/README.md +8 -0
- package/blocks/social-ads/block.json +137 -4
- package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
- package/blocks/social-ads/components/audience-sync.tsx +6 -2
- package/blocks/social-ads/components/campaign-ads.tsx +166 -0
- package/blocks/social-ads/components/campaign-detail.tsx +6 -1
- package/blocks/social-ads/components/campaign-editor.tsx +134 -0
- package/blocks/social-ads/components/creative-concepts.tsx +74 -0
- package/blocks/social-ads/components/creative-detail.tsx +6 -1
- package/blocks/social-ads/components/creative-gallery.tsx +20 -2
- package/blocks/social-ads/components/creative-revisions.tsx +57 -0
- package/blocks/social-ads/components/creative-tools.tsx +176 -0
- package/blocks/social-ads/components/post-composer.tsx +4 -2
- package/blocks/social-ads/components/post-detail.tsx +3 -0
- package/blocks/social-ads/components/social-ideas.tsx +64 -0
- package/blocks/social-ads/components/social-performance.tsx +80 -0
- package/blocks/social-ads/components/social-screen.tsx +33 -6
- package/blocks/social-ads/lib/ads-extra.ts +67 -0
- package/blocks/surveys/README.md +91 -0
- package/blocks/surveys/block.json +299 -0
- package/blocks/surveys/components/account-nps.tsx +69 -0
- package/blocks/surveys/components/contact-survey-history.tsx +52 -0
- package/blocks/surveys/components/org-summary.tsx +82 -0
- package/blocks/surveys/components/sending-domains.tsx +113 -0
- package/blocks/surveys/components/survey-form.tsx +126 -0
- package/blocks/surveys/components/survey-responses.tsx +145 -0
- package/blocks/surveys/components/survey-results.tsx +72 -0
- package/blocks/surveys/components/survey-send-history.tsx +58 -0
- package/blocks/surveys/components/survey-send.tsx +162 -0
- package/blocks/surveys/components/survey-triggers.tsx +166 -0
- package/blocks/surveys/components/surveys-screen.tsx +234 -0
- package/blocks/surveys/lib/surveys-types.ts +228 -0
- package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
- package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
- package/blocks/surveys/recipes/survey-score-report.ts +27 -0
- package/blocks/visitors/components/snippet-install.tsx +18 -9
- package/blocks/visitors/components/traffic-chart.tsx +4 -4
- package/blocks/visitors/components/visitor-company-card.tsx +24 -14
- package/blocks/visitors/components/visitor-feed.tsx +5 -5
- package/blocks/webchat/components/chat-widget-config.tsx +1 -1
- package/blocks/webchat/components/conversation-list.tsx +1 -1
- package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
- package/blocks/workbench/components/run-progress.tsx +1 -1
- package/blocks/workbench/components/workbench-grid.tsx +1 -1
- package/blocks/workbench/components/workbench-screen.tsx +15 -5
- package/blocks/workbench/components/workbench-table-list.tsx +14 -2
- package/blocks/workflows/components/workflow-editor.tsx +1 -1
- package/dist/cli.js +536 -392
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +1 -1
- package/dist/react.browser.js.map +1 -1
- package/dist/react.browser.mjs +1 -1
- package/dist/react.browser.mjs.map +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/types/blocks/brand.d.ts +11 -2
- package/dist/types/blocks/create.d.ts +7 -3
- package/dist/types/blocks/doctor.d.ts +1 -0
- package/dist/types/blocks/flows.d.ts +7 -1
- package/dist/types/blocks/orgBrand.d.ts +34 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/version.d.ts +2 -2
- package/docs/api/desk.d.ts +2 -2
- package/docs/api/desk.md +3 -3
- package/package.json +1 -1
- 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 +17 -4
- package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
- package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
- package/stacks/apollo/pages/contacts.tsx.tmpl +29 -95
- package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
- package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/apollo/pages/lists.tsx.tmpl +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 +36 -8
- package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
- package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
- package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
- package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
- package/stacks/calendly.json +8 -4
- package/stacks/clay/pages/companies.tsx.tmpl +17 -1
- package/stacks/clay/pages/contacts.tsx.tmpl +12 -0
- package/stacks/clay/pages/lists.tsx.tmpl +12 -0
- package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
- package/stacks/clay.json +16 -3
- package/stacks/gong/pages/calls.tsx.tmpl +23 -0
- package/stacks/gong/pages/companies.tsx.tmpl +22 -0
- package/stacks/gong/pages/contacts.tsx.tmpl +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 +23 -6
- 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 +9 -4
- package/stacks/hubspot/pages/companies.tsx.tmpl +14 -35
- package/stacks/hubspot/pages/contacts.tsx.tmpl +29 -95
- package/stacks/hubspot/pages/deals.tsx.tmpl +10 -1
- package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
- package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
- package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
- package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
- package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
- package/stacks/hubspot.json +21 -5
- 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 +8 -4
- 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 +20 -5
- 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 +27 -66
- package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
- package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
- package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
- package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
- package/stacks/outreach.json +25 -7
- package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
- package/stacks/pipedrive/pages/contacts.tsx.tmpl +15 -35
- package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
- package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
- package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
- package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
- package/stacks/pipedrive.json +4 -2
- package/stacks/salesforce/pages/companies.tsx.tmpl +14 -35
- package/stacks/salesforce/pages/contacts.tsx.tmpl +20 -35
- package/stacks/salesforce/pages/deals.tsx.tmpl +10 -1
- package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
- package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
- package/stacks/salesforce.json +7 -3
- package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
- package/stacks/zoominfo/pages/contacts.tsx.tmpl +12 -0
- package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
- package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
- package/stacks/zoominfo.json +16 -3
- package/styles/styles.css +1 -1
- package/templates/next-app/app/globals.css.tmpl +349 -64
- package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
- package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
|
@@ -75,7 +75,7 @@ export declare function renderBrandConfig(f: BrandFields): string;
|
|
|
75
75
|
export declare function readBrandConfig(source: string): Partial<BrandFields> | null;
|
|
76
76
|
/** The config with its region rewritten, or null when the user owns the file (no markers). */
|
|
77
77
|
export declare function applyBrandConfig(source: string, f: BrandFields): string | null;
|
|
78
|
-
export declare const BRAND_USAGE = "Usage: npx @graph8/sdk brand [options]\n\nBrand an app made by `create` (run it in the app's root). Every option is optional; values you leave out stay.\n --name <text> the app's name (sidebar, page title)\n --brand-color <hex> primary colour, e.g. \"#0f62fe\": writes --brand, a 50-900 scale and --primary into\n app/globals.css, with button text checked for WCAG AA (adjusted when needed)\n --dark-brand-color <hex> the colour in dark mode (default: derived from --brand-color, lightened if too dark)\n --logo <path> an svg, png, jpg, webp or gif (max 512 KB), copied to public/brand/ and shown in the sidebar\n --app-url <url> white-label: where link-outs go (default https://app.graph8.com)\n --link-label <text> white-label: what the platform is called in copy and links (default graph8)\n --dir <path> the app's root (default: current directory)\n --dry-run print what would change, write nothing\nRunning it again with the same options changes nothing.";
|
|
78
|
+
export declare const BRAND_USAGE = "Usage: npx @graph8/sdk brand [options]\n\nBrand an app made by `create` (run it in the app's root). Every option is optional; values you leave out stay.\n --name <text> the app's name (sidebar, page title)\n --brand-color <hex> primary colour, e.g. \"#0f62fe\": writes --brand, a 50-900 scale and --primary into\n app/globals.css, with button text checked for WCAG AA (adjusted when needed)\n --dark-brand-color <hex> the colour in dark mode (default: derived from --brand-color, lightened if too dark)\n --logo <path> an svg, png, jpg, webp or gif (max 512 KB), copied to public/brand/ and shown in the sidebar\n --app-url <url> white-label: where link-outs go (default https://app.graph8.com)\n --link-label <text> white-label: what the platform is called in copy and links (default graph8)\n --from-org fill in what you did not pass from the organization's Brand Kit (needs G8_API_KEY in the\n environment or .env.local): name, primary colour, logo. Running brand with no options\n does the same when the kit has something. Values you pass always win; they replace what the app has now.\n --no-org-brand never read the Brand Kit\n --dir <path> the app's root (default: current directory)\n --dry-run print what would change, write nothing\nRunning it again with the same options changes nothing.";
|
|
79
79
|
export interface BrandOptions {
|
|
80
80
|
dir: string;
|
|
81
81
|
name?: string;
|
|
@@ -85,6 +85,10 @@ export interface BrandOptions {
|
|
|
85
85
|
appUrl?: string;
|
|
86
86
|
linkLabel?: string;
|
|
87
87
|
dryRun?: boolean;
|
|
88
|
+
/** `--from-org`, or a bare `brand`: fill the gaps from the org's Brand Kit. */
|
|
89
|
+
fromOrg?: boolean;
|
|
90
|
+
/** `--no-org-brand`. */
|
|
91
|
+
noOrgBrand?: boolean;
|
|
88
92
|
}
|
|
89
93
|
/** The brand flags (shared with `create`). Returns true when `argv[i]` was one; `next()` consumes its value. */
|
|
90
94
|
export declare function takeBrandFlag(o: Partial<BrandOptions>, flag: string, next: () => string): boolean;
|
|
@@ -92,6 +96,11 @@ export declare function hasBrandInput(o: Partial<BrandOptions>): boolean;
|
|
|
92
96
|
export declare function parseBrandArgs(argv: string[]): BrandOptions;
|
|
93
97
|
export declare const LOGO_TYPES: string[];
|
|
94
98
|
export declare const MAX_LOGO_BYTES: number;
|
|
99
|
+
export type OrgBrandValues = Pick<BrandOptions, "name" | "color" | "logo">;
|
|
100
|
+
/** Explicit options win per field; org values fill only what the user left out. */
|
|
101
|
+
export declare function mergeOrgBrand<T extends Partial<BrandOptions>>(explicit: T | undefined, org: OrgBrandValues, skip?: {
|
|
102
|
+
name?: boolean;
|
|
103
|
+
}): T;
|
|
95
104
|
export interface BrandIO {
|
|
96
105
|
out: (line: string) => void;
|
|
97
106
|
err: (line: string) => void;
|
|
@@ -109,5 +118,5 @@ export declare function planBrand(o: BrandOptions, root: string, cwd: string, fa
|
|
|
109
118
|
notes: string[];
|
|
110
119
|
};
|
|
111
120
|
/** Run `brand`. Returns the exit code. */
|
|
112
|
-
export declare function runBrand(
|
|
121
|
+
export declare function runBrand(given: BrandOptions, io: BrandIO, cwd: string, fallbackName?: string, org?: OrgBrandValues): number;
|
|
113
122
|
export {};
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* Node only. Reads `templates/next-app/**` (shipped in the package) and the
|
|
8
8
|
* block registry; writes only inside `<dir>`.
|
|
9
9
|
*/
|
|
10
|
-
import { type BrandOptions } from "./brand";
|
|
10
|
+
import { type OrgBrandValues, type BrandOptions } from "./brand";
|
|
11
11
|
import { type AddIO, type PackageManager } from "./add";
|
|
12
12
|
import { componentImportPath, navParts, navUsesExtras, pageFileFor, render } from "./navfile";
|
|
13
13
|
import { type Registry, type Stack, type StackNav } from "./registry";
|
|
14
|
-
export declare const CREATE_USAGE = "Usage: npx @graph8/sdk create <stack> <dir> [options]\n\nScaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and\ninstall the stack's blocks into it. Run `graph8-sdk list` to see the stacks.\n --package-manager <npm|pnpm|yarn|bun> default: the one running the command, else npm\n --no-install write the files and blocks, do not install dependencies\n --dry-run print what would be written, write and install nothing\n --force allow a non-empty <dir> (template files are overwritten, and graph8.json\n is reset: blocks you added to that app earlier are no longer recorded)\nThen: `npm run dev` in <dir>, open http://localhost:3000 and paste a graph8 API key (local preview), and run\n`npx @graph8/sdk doctor --stack <stack>` (add --production before going live).\n\nOr pick the features yourself, for any app (also: `create custom --blocks ...`):\n npx @graph8/sdk create --blocks <id,id,...> [--title \"Acme CRM\"] <dir> [options]\nEvery requested block's dependencies come with it. The app gets one route and one sidebar entry per finished\nScreen of the requested blocks, plus the user-facing Screens of their dependencies (asking for deals also gives\nyou crm's screens). `/` opens the analytics screen if you asked for one, else the first screen.\n --title <text> the app's name (default: graph8 app)\n --with-deps-screens also add the plain fallback page of dependency blocks that have no Screen\n --no-deps-screens add screens only for the blocks you named\nRun `graph8-sdk list` to see each block and which have Screens.\n\nBrand it (any stack or --blocks; the same as running `graph8-sdk brand` in the new app afterwards):\n --name <text> the app's name (with --blocks, the same as --title)\n --brand-color <hex> primary colour, AA-checked; --dark-brand-color <hex> for dark mode\n --logo <path> svg/png/jpg/webp/gif, shown in the sidebar\n --app-url <url> white-label: where \"Open in graph8\" links go; --link-label <text>: what they call it\nA directory literally named `custom` would read as the alias: pass `--blocks crm ./custom`.";
|
|
14
|
+
export declare const CREATE_USAGE = "Usage: npx @graph8/sdk create <stack> <dir> [options]\n\nScaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and\ninstall the stack's blocks into it. Run `graph8-sdk list` to see the stacks.\n --package-manager <npm|pnpm|yarn|bun> default: the one running the command, else npm\n --no-install write the files and blocks, do not install dependencies\n --dry-run print what would be written, write and install nothing\n --force allow a non-empty <dir> (template files are overwritten, and graph8.json\n is reset: blocks you added to that app earlier are no longer recorded)\nThen: `npm run dev` in <dir>, open http://localhost:3000 and paste a graph8 API key (local preview), and run\n`npx @graph8/sdk doctor --stack <stack>` (add --production before going live).\n\nOr pick the features yourself, for any app (also: `create custom --blocks ...`):\n npx @graph8/sdk create --blocks <id,id,...> [--title \"Acme CRM\"] <dir> [options]\nEvery requested block's dependencies come with it. The app gets one route and one sidebar entry per finished\nScreen of the requested blocks, plus the user-facing Screens of their dependencies (asking for deals also gives\nyou crm's screens). `/` opens the analytics screen if you asked for one, else the first screen.\n --title <text> the app's name (default: graph8 app)\n --with-deps-screens also add the plain fallback page of dependency blocks that have no Screen\n --no-deps-screens add screens only for the blocks you named\nRun `graph8-sdk list` to see each block and which have Screens.\n\nBrand it (any stack or --blocks; the same as running `graph8-sdk brand` in the new app afterwards):\n --name <text> the app's name (with --blocks, the same as --title)\n --brand-color <hex> primary colour, AA-checked; --dark-brand-color <hex> for dark mode\n --logo <path> svg/png/jpg/webp/gif, shown in the sidebar\n --app-url <url> white-label: where \"Open in graph8\" links go; --link-label <text>: what they call it\nWith a graph8 API key (G8_API_KEY in the environment or .env.local) the org's Brand Kit fills in what you did not\npass: the app name, the primary colour and the logo (downloaded into public/brand/). No key, no kit or an error:\nthe default look, one info line, never a failure. --no-org-brand skips it.\nA directory literally named `custom` would read as the alias: pass `--blocks crm ./custom`.";
|
|
15
15
|
export interface CreateOptions {
|
|
16
16
|
stack: string;
|
|
17
17
|
dir: string;
|
|
@@ -26,6 +26,8 @@ export interface CreateOptions {
|
|
|
26
26
|
depsScreens?: "default" | "with" | "none";
|
|
27
27
|
/** `--name`, `--brand-color`, `--logo`, ...: applied with `graph8-sdk brand` once the app is written. */
|
|
28
28
|
brand?: Omit<BrandOptions, "dir" | "dryRun">;
|
|
29
|
+
/** false with `--no-org-brand`: do not pull the org's Brand Kit. */
|
|
30
|
+
orgBrand?: false;
|
|
29
31
|
}
|
|
30
32
|
export declare function parseCreateArgs(argv: string[]): CreateOptions;
|
|
31
33
|
/** What `graph8.json` records about a custom app, so `add` and `doctor` know its routes. */
|
|
@@ -45,6 +47,8 @@ export interface CreateIO extends AddIO {
|
|
|
45
47
|
sdkPackageJson?: string;
|
|
46
48
|
/** npm_config_user_agent, to default the package manager. */
|
|
47
49
|
userAgent?: string;
|
|
50
|
+
/** Name, colour and logo pulled from the org's Brand Kit (see orgBrand.ts); explicit brand flags win. */
|
|
51
|
+
orgBrand?: OrgBrandValues;
|
|
48
52
|
}
|
|
49
53
|
export declare function packageManagerFromUserAgent(ua: string | undefined): PackageManager;
|
|
50
54
|
/** A valid npm package name from a directory name. */
|
|
@@ -59,5 +63,5 @@ export declare function stackProblem(reg: Registry, stack: Stack): string | null
|
|
|
59
63
|
export declare function stackPageFile(reg: Registry, stack: Stack, n: StackNav): string | null;
|
|
60
64
|
export declare function planCreate(reg: Registry, stack: Stack, appName: string, pm: PackageManager, io: CreateIO, app?: AppRecord): OutFile[];
|
|
61
65
|
/** Run `create`. Returns the process exit code. */
|
|
62
|
-
export declare function runCreate(
|
|
66
|
+
export declare function runCreate(given: CreateOptions, reg: Registry, io: CreateIO, cwd: string): number;
|
|
63
67
|
export { componentImportPath, navParts, navUsesExtras, pageFileFor, render };
|
|
@@ -25,6 +25,7 @@ export interface DoctorIO {
|
|
|
25
25
|
env: Record<string, string | undefined>;
|
|
26
26
|
fetch?: typeof fetch;
|
|
27
27
|
}
|
|
28
|
+
export declare function readSetting(name: string, root: string, env: DoctorIO["env"]): string | undefined;
|
|
28
29
|
/** Same rules as the server's `key_has_scope`. */
|
|
29
30
|
export declare function scopeGranted(granted: string[], required: string): boolean;
|
|
30
31
|
/** Run doctor. Returns the exit code: 0 when every check passes. */
|
|
@@ -14,7 +14,13 @@ export interface UnreachedComponent {
|
|
|
14
14
|
component: string;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Library blocks hold shared building blocks that other blocks import, not pages of their own: the foundation's shared
|
|
18
|
+
* UI and the pickers. A stack installs them whole but each screen uses only some, so they are never "unreached".
|
|
19
|
+
* Every other block is judged component by component.
|
|
20
|
+
*/
|
|
21
|
+
export declare const LIBRARY_BLOCKS: ReadonlySet<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Components of the stack's own blocks (library blocks are exempt) that no
|
|
18
24
|
* generated page reaches, directly or through a Screen that imports them.
|
|
19
25
|
*/
|
|
20
26
|
export declare function unreachedComponents(reg: Registry, stack: Stack): UnreachedComponent[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull the organization's Brand Kit into a new app: `create` and `brand --from-org`.
|
|
3
|
+
*
|
|
4
|
+
* Reads two Developer API operations with the project's key (the same resolution `doctor` uses: G8_API_KEY from
|
|
5
|
+
* the environment, then .env.local; G8_API_URL for a non-default API):
|
|
6
|
+
* - get_brand_kit_overview_brand_kit_get -> logo_url, design_tokens.colors, source_url (scope campaigns:read)
|
|
7
|
+
* - describe_current_key_me_get -> org_name
|
|
8
|
+
* The kit has ONE logo URL (no light/dark/icon variant), a measured colour palette and fonts, a tagline and voice
|
|
9
|
+
* traits. Only the name, the primary colour and the logo map onto the brand inputs; fonts, tagline and voice have no
|
|
10
|
+
* SDK brand input and are ignored.
|
|
11
|
+
*
|
|
12
|
+
* Never fails the caller: no key, a 4xx/5xx, a network error, a timeout or an empty kit all give `{}` plus one info
|
|
13
|
+
* line. The key is never printed or written. The logo is downloaded to a temp file (https or data: URL, an image type,
|
|
14
|
+
* at most 512 KB) so `brand` copies it into public/brand/ like any `--logo` file: nothing is hot-linked.
|
|
15
|
+
*/
|
|
16
|
+
import { mergeOrgBrand, type OrgBrandValues } from "./brand";
|
|
17
|
+
export { mergeOrgBrand, type OrgBrandValues };
|
|
18
|
+
export interface OrgBrandPull {
|
|
19
|
+
values: OrgBrandValues;
|
|
20
|
+
/** One line for the CLI to print. */
|
|
21
|
+
info: string;
|
|
22
|
+
/** Remove the temp logo file. */
|
|
23
|
+
cleanup: () => void;
|
|
24
|
+
}
|
|
25
|
+
export interface OrgBrandIO {
|
|
26
|
+
env: Record<string, string | undefined>;
|
|
27
|
+
fetch?: typeof fetch;
|
|
28
|
+
}
|
|
29
|
+
/** The primary colour of the kit's measured tokens, or null. */
|
|
30
|
+
export declare function pickColor(tokens: unknown): string | null;
|
|
31
|
+
/** A display name that is safe to write into generated files. */
|
|
32
|
+
export declare function cleanName(v: unknown): string | null;
|
|
33
|
+
/** Pull name, primary colour and logo from the org's Brand Kit. Never throws. */
|
|
34
|
+
export declare function pullOrgBrand(io: OrgBrandIO, root: string): Promise<OrgBrandPull>;
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Node only. Built as a separate entry (dist/cli.js), so nothing here reaches
|
|
11
11
|
* the browser bundle or the package's main exports.
|
|
12
12
|
*/
|
|
13
|
+
import type { OrgBrandValues } from "./blocks/brand";
|
|
13
14
|
export interface CliIO {
|
|
14
15
|
cwd: string;
|
|
15
16
|
out: (line: string) => void;
|
|
@@ -26,6 +27,8 @@ export interface CliIO {
|
|
|
26
27
|
env?: Record<string, string | undefined>;
|
|
27
28
|
/** fetch for `doctor`. Default: global fetch. */
|
|
28
29
|
fetch?: typeof fetch;
|
|
30
|
+
/** Name, colour and logo pulled from the org's Brand Kit by `runCliAsync` for `create` and `brand`. */
|
|
31
|
+
orgBrand?: OrgBrandValues;
|
|
29
32
|
}
|
|
30
33
|
export declare function runCli(argv: string[], io: CliIO): number;
|
|
31
34
|
/** Like `runCli`, plus the commands that talk to the network (`doctor`). */
|
package/dist/types/version.d.ts
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* both together (the version-parity gate already pins package.json, the
|
|
6
6
|
* lockfile and the CHANGELOG to each other).
|
|
7
7
|
*/
|
|
8
|
-
export declare const SDK_VERSION = "0.
|
|
8
|
+
export declare const SDK_VERSION = "0.373.0";
|
|
9
9
|
/** Value of the `X-G8-Client` header every SDK request carries (I-13). */
|
|
10
|
-
export declare const SDK_CLIENT_ID = "js-sdk/0.
|
|
10
|
+
export declare const SDK_CLIENT_ID = "js-sdk/0.373.0";
|
package/docs/api/desk.d.ts
CHANGED
|
@@ -155,7 +155,7 @@ export interface DeskOperations {
|
|
|
155
155
|
};
|
|
156
156
|
/**
|
|
157
157
|
* POST /desk/cards
|
|
158
|
-
*
|
|
158
|
+
* Put one planned outreach touch (an email, LinkedIn or call step for one contact) on a rep's desk with its why, facts and draft. The rep edits and sends it; nothing sends by itself. owner_id must be the caller's own email unless the key is an org or machine key: a personal key or an OAuth connection is a person and gets 403 owner_not_self for anyone else. Posting the same external_key again updates the open card instead of adding a second one. Needs deals:write and a key allowed to see the contact
|
|
159
159
|
* tier: write · scope: deals:write
|
|
160
160
|
*/
|
|
161
161
|
create_desk_card_desk_cards_post: {
|
|
@@ -1582,7 +1582,7 @@ export interface DeskCardCreateRequest {
|
|
|
1582
1582
|
external_key?: string | null;
|
|
1583
1583
|
list_id?: number | null;
|
|
1584
1584
|
origin?: DeskCardOrigin | null;
|
|
1585
|
-
/** Whose desk: a team member's email, id or user id. */
|
|
1585
|
+
/** Whose desk: a team member's email, id or user id. With a personal key or an OAuth connection this must be the caller's own email (the key is a person; anyone else is 403 owner_not_self). Only an org or machine key may name another rep. */
|
|
1586
1586
|
owner_id: string;
|
|
1587
1587
|
/** low, normal, high or urgent. */
|
|
1588
1588
|
priority?: string;
|
package/docs/api/desk.md
CHANGED
|
@@ -26,7 +26,7 @@ Exact TypeScript types for every operation on this page, and every schema they u
|
|
|
26
26
|
| [`askDesk`](#op-askdesk) | `POST /api/v1/desk/ask` | billable | `deals:run` | Ask Desk |
|
|
27
27
|
| [`checkCommitteePlay`](#op-checkcommitteeplay) | `GET /api/v1/desk/accounts/{account_id}/committee/plays/{action_id}/check` | read | `deals:read` | Check Committee Play |
|
|
28
28
|
| [`checkDeskPlanStageSweep`](#op-checkdeskplanstagesweep) | `GET /api/v1/desk/org-settings/plan-stages/check` | read | `deals:read` | Check Desk Plan Stage Sweep |
|
|
29
|
-
| [`createDeskCard`](#op-createdeskcard) | `POST /api/v1/desk/cards` | write | `deals:write` |
|
|
29
|
+
| [`createDeskCard`](#op-createdeskcard) | `POST /api/v1/desk/cards` | write | `deals:write` | Put one planned outreach touch (an email, LinkedIn or call step for one contact) on a rep's desk with its why, facts an… |
|
|
30
30
|
| [`createOrReplanAccountPlan`](#op-createorreplanaccountplan) | `POST /api/v1/desk/accounts/{account_id}/plan` | write | `deals:write` | Create Or Replan Account Plan |
|
|
31
31
|
| [`decideDeskPlayApproval`](#op-decidedeskplayapproval) | `POST /api/v1/desk/plays/approvals/{execution_id}/decide` | external | `deals:run` | Decide Desk Play Approval |
|
|
32
32
|
| [`draftAccountPlan`](#op-draftaccountplan) | `POST /api/v1/desk/accounts/{account_id}/plan/draft` | billable | `deals:run` | Draft Account Plan |
|
|
@@ -261,7 +261,7 @@ const result = await g8.api.desk.checkDeskPlanStageSweep();
|
|
|
261
261
|
|
|
262
262
|
### `g8.api.desk.createDeskCard(input)`
|
|
263
263
|
|
|
264
|
-
**
|
|
264
|
+
**Put one planned outreach touch (an email, LinkedIn or call step for one contact) on a rep's desk with its why, facts and draft. The rep edits and sends it; nothing sends by itself. owner_id must be t…**
|
|
265
265
|
|
|
266
266
|
- HTTP: `POST /api/v1/desk/cards`
|
|
267
267
|
- operationId: `create_desk_card_desk_cards_post` (also callable as `g8.api.call("create_desk_card_desk_cards_post", input)`)
|
|
@@ -297,7 +297,7 @@ Body fields (`DeskCardCreateRequest`):
|
|
|
297
297
|
|---|---|---|---|
|
|
298
298
|
| `body.channel` | `string` | yes | email, linkedin or call. min length 1, max length 32 |
|
|
299
299
|
| `body.contact_id` | `integer` | yes | The contact the touch is for. max 9223372036854776000, > 0 |
|
|
300
|
-
| `body.owner_id` | `string` | yes | Whose desk: a team member's email, id or user id. min length 1, max length 320 |
|
|
300
|
+
| `body.owner_id` | `string` | yes | Whose desk: a team member's email, id or user id. With a personal key or an OAuth connection this must be the caller's own email (the key is a person; anyone else is 403 owner_not_self). Only an org or machine key may name another rep. min length 1, max length 320 |
|
|
301
301
|
| `body.account_id` | `integer \| null` | no | The contact's company, when it is not the contact's own. max 9223372036854776000, > 0 |
|
|
302
302
|
| `body.context` | `DeskCardContext \| null` | no | |
|
|
303
303
|
| `body.draft` | `DeskCardDraft \| null` | no | |
|
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { CompaniesScreen } from "@/components/g8/crm/companies-screen";
|
|
6
|
+
import { CompanyIntelligenceTab } from "@/components/g8/company-intelligence/company-intelligence-tab";
|
|
7
|
+
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
8
|
+
import { useRouter } from "next/navigation";
|
|
9
|
+
|
|
10
|
+
export default function Page() {
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
return (
|
|
13
|
+
<CompaniesScreen
|
|
14
|
+
onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
|
|
15
|
+
extraDetailTabs={[
|
|
16
|
+
{ id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
|
|
17
|
+
{ id: "intelligence", label: "Intelligence", render: (c) => <CompanyIntelligenceTab company={c} onOpenIntelligence={() => router.push("/company-intelligence")} /> },
|
|
18
|
+
]}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
|
|
6
|
+
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
7
|
+
import { useRouter } from "next/navigation";
|
|
8
|
+
|
|
9
|
+
export default function Page() {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
return (
|
|
12
|
+
<ContactsScreen
|
|
13
|
+
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
14
|
+
extraDetailTabs={[
|
|
15
|
+
{ id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
|
|
16
|
+
]}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { TasksScreen } from "@/components/g8/crm/tasks-screen";
|
|
7
|
+
|
|
8
|
+
/** A task's linked record opens in the screen that owns it. */
|
|
9
|
+
export default function Page() {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
return (
|
|
12
|
+
<TasksScreen
|
|
13
|
+
onOpenRecord={(record) => {
|
|
14
|
+
if (record.entity_type === "contact") router.push(`/contacts?id=${encodeURIComponent(record.entity_id)}`);
|
|
15
|
+
if (record.entity_type === "company") router.push(`/companies?id=${encodeURIComponent(record.entity_id)}`);
|
|
16
|
+
}}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
package/stacks/6sense.json
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"crm",
|
|
10
10
|
"intent",
|
|
11
11
|
"visitors",
|
|
12
|
-
"radar"
|
|
12
|
+
"radar",
|
|
13
|
+
"company-intelligence"
|
|
13
14
|
],
|
|
14
15
|
"home": "/intent",
|
|
15
16
|
"nav": [
|
|
@@ -47,7 +48,17 @@
|
|
|
47
48
|
"component": "CompaniesScreen",
|
|
48
49
|
"icon": "Building2",
|
|
49
50
|
"group": "Prospect",
|
|
50
|
-
"description": "Accounts and the people at them"
|
|
51
|
+
"description": "Accounts and the people at them",
|
|
52
|
+
"page": "pages/companies.tsx.tmpl"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"label": "Company intelligence",
|
|
56
|
+
"path": "/company-intelligence",
|
|
57
|
+
"block": "company-intelligence",
|
|
58
|
+
"component": "CompanyIntelligenceScreen",
|
|
59
|
+
"icon": "Telescope",
|
|
60
|
+
"group": "Signals",
|
|
61
|
+
"description": "Research your website: profile, competitors, news, documents and reports"
|
|
51
62
|
},
|
|
52
63
|
{
|
|
53
64
|
"label": "Contacts",
|
|
@@ -56,7 +67,8 @@
|
|
|
56
67
|
"component": "ContactsScreen",
|
|
57
68
|
"icon": "Users",
|
|
58
69
|
"group": "Prospect",
|
|
59
|
-
"description": "People you sell to, with notes, tasks and activity"
|
|
70
|
+
"description": "People you sell to, with notes, tasks and activity",
|
|
71
|
+
"page": "pages/contacts.tsx.tmpl"
|
|
60
72
|
},
|
|
61
73
|
{
|
|
62
74
|
"label": "Tasks",
|
|
@@ -65,7 +77,8 @@
|
|
|
65
77
|
"component": "TasksScreen",
|
|
66
78
|
"icon": "CheckSquare",
|
|
67
79
|
"group": "Work",
|
|
68
|
-
"description": "Follow-ups due across your records"
|
|
80
|
+
"description": "Follow-ups due across your records",
|
|
81
|
+
"page": "pages/tasks.tsx.tmpl"
|
|
69
82
|
}
|
|
70
83
|
]
|
|
71
84
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { Button } from "@graph8/sdk/ui";
|
|
7
|
+
import { UserRound } from "lucide-react";
|
|
8
|
+
import { CallLogScreen } from "@/components/g8/dialer/call-log-screen";
|
|
9
|
+
|
|
10
|
+
/** A call opens the person it was made to. */
|
|
11
|
+
export default function Page() {
|
|
12
|
+
const router = useRouter();
|
|
13
|
+
return (
|
|
14
|
+
<CallLogScreen
|
|
15
|
+
renderDetailActions={(call) => call.contact_id ? (
|
|
16
|
+
<Button size="sm" variant="outline" onClick={() => router.push(`/contacts?id=${encodeURIComponent(call.contact_id as string)}`)}>
|
|
17
|
+
<UserRound className="mr-1 size-4" aria-hidden />
|
|
18
|
+
Open contact
|
|
19
|
+
</Button>
|
|
20
|
+
) : null}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -2,49 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
-
import Link from "next/link";
|
|
6
|
-
import { Skeleton } from "@graph8/sdk/ui";
|
|
7
|
-
import { Handshake } from "lucide-react";
|
|
8
|
-
import { ApiError } from "@/components/g8/_foundation/api-error";
|
|
9
|
-
import { EmptyState } from "@/components/g8/_foundation/empty-state";
|
|
10
|
-
import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
|
|
11
|
-
import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
|
|
12
5
|
import { CompaniesScreen } from "@/components/g8/crm/companies-screen";
|
|
6
|
+
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
7
|
+
import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
|
|
13
8
|
import { EnrichButton } from "@/components/g8/enrichment/enrich-button";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
|
|
18
|
-
|
|
19
|
-
/** The deals linked to one contact or company, each opening in the Deals page. */
|
|
20
|
-
function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
|
|
21
|
-
const q = useG8Query(async (signal) => {
|
|
22
|
-
const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
|
|
23
|
-
return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
|
|
24
|
-
}, [kind, id]);
|
|
25
|
-
if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
|
|
26
|
-
if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
|
|
27
|
-
if (q.data.length === 0) return <EmptyState compact icon={<Handshake className="size-8" />} title="No deals yet" description="Deals linked to this record show up here." />;
|
|
28
|
-
return (
|
|
29
|
-
<ul className="divide-y rounded-md border" aria-label="Deals">
|
|
30
|
-
{q.data.map((d) => (
|
|
31
|
-
<li key={d.deal_id}>
|
|
32
|
-
<Link href={`/deals?id=${encodeURIComponent(d.deal_id as string)}`} className="flex items-center justify-between gap-3 p-3 text-sm hover:bg-muted">
|
|
33
|
-
<span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
|
|
34
|
-
<span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
|
|
35
|
-
</Link>
|
|
36
|
-
</li>
|
|
37
|
-
))}
|
|
38
|
-
</ul>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
9
|
+
import { AddCompanyPeopleToListButton } from "@/components/g8/lists/add-company-people-to-list-button";
|
|
10
|
+
import { CompanyIntelligenceTab } from "@/components/g8/company-intelligence/company-intelligence-tab";
|
|
11
|
+
import { useRouter } from "next/navigation";
|
|
41
12
|
|
|
42
13
|
export default function Page() {
|
|
14
|
+
const router = useRouter();
|
|
43
15
|
return (
|
|
44
16
|
<CompaniesScreen
|
|
17
|
+
onOpenContact={(contact) => router.push(`/contacts?id=${contact.id}`)}
|
|
45
18
|
renderRowActions={(c, { reload }) => <EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" size="sm" variant="ghost" />}
|
|
46
|
-
|
|
47
|
-
|
|
19
|
+
renderBulkActions={({ ids, clear }) => <AddCompanyPeopleToListButton companyIds={ids} onAdded={clear} />}
|
|
20
|
+
renderDetailActions={(c, { reload }) => (
|
|
21
|
+
<>
|
|
22
|
+
<EnrichButton kind="company" lookup={{ domain: c.domain ?? undefined, name: c.name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />
|
|
23
|
+
<AddCompanyPeopleToListButton companyIds={[c.id]} />
|
|
24
|
+
</>
|
|
25
|
+
)}
|
|
26
|
+
extraDetailTabs={[
|
|
27
|
+
{ id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="companies" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
|
|
28
|
+
{ id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
|
|
29
|
+
{ id: "intelligence", label: "Intelligence", render: (c) => <CompanyIntelligenceTab company={c} onOpenIntelligence={() => router.push("/company-intelligence")} /> },
|
|
30
|
+
]}
|
|
48
31
|
/>
|
|
49
32
|
);
|
|
50
33
|
}
|
|
@@ -2,116 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
-
import {
|
|
6
|
-
import Link from "next/link";
|
|
7
|
-
import { Button, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Skeleton } from "@graph8/sdk/ui";
|
|
8
|
-
import { Handshake, ListPlus } from "lucide-react";
|
|
9
|
-
import { ApiError } from "@/components/g8/_foundation/api-error";
|
|
10
|
-
import { EmptyState } from "@/components/g8/_foundation/empty-state";
|
|
11
|
-
import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
|
|
12
|
-
import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
|
|
5
|
+
import { RecordMeetingsTab } from "@/components/g8/appointments/record-meetings-tab";
|
|
13
6
|
import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
|
|
7
|
+
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
8
|
+
import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
|
|
14
9
|
import { CallContactButton } from "@/components/g8/dialer/call-contact-button";
|
|
10
|
+
import { RecordCallsTab } from "@/components/g8/dialer/record-calls-tab";
|
|
15
11
|
import { EnrichButton } from "@/components/g8/enrichment/enrich-button";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const out: SequenceRow[] = [];
|
|
23
|
-
for (let page = 1; page <= 50; page++) {
|
|
24
|
-
const env = await g8Get("/sequences", { page, limit: 100 }, { signal });
|
|
25
|
-
const rows = listOf<SequenceRow>(env);
|
|
26
|
-
out.push(...rows.filter((s) => statusGroup(s.status) === "live"));
|
|
27
|
-
const more = (env as { pagination?: { has_next?: boolean } }).pagination?.has_next;
|
|
28
|
-
if (!more || rows.length === 0) break;
|
|
29
|
-
}
|
|
30
|
-
return out;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Pick a sequence, then enroll the contacts in it. The enroll dialog shows the sending preflight before anything is sent. */
|
|
34
|
-
function AddToSequence({ contactIds, onEnrolled, variant = "outline" }: { contactIds: number[]; onEnrolled?: () => void; variant?: "outline" | "default" }) {
|
|
35
|
-
const [step, setStep] = useState<"closed" | "pick" | "enroll">("closed");
|
|
36
|
-
const [sequenceId, setSequenceId] = useState("");
|
|
37
|
-
const sequences = useG8Query(activeSequences, [], { enabled: step === "pick" });
|
|
38
|
-
const chosen = (sequences.data ?? []).find((s) => s.id === sequenceId);
|
|
39
|
-
return (
|
|
40
|
-
<>
|
|
41
|
-
<Button size="sm" variant={variant} disabled={contactIds.length === 0} onClick={() => { setSequenceId(""); setStep("pick"); }}>
|
|
42
|
-
<ListPlus className="mr-1 size-4" aria-hidden />
|
|
43
|
-
Add to sequence
|
|
44
|
-
</Button>
|
|
45
|
-
<Sheet open={step === "pick"} onOpenChange={(o) => { if (!o) setStep("closed"); }}>
|
|
46
|
-
<SheetContent className="w-full sm:max-w-md">
|
|
47
|
-
<SheetHeader>
|
|
48
|
-
<SheetTitle>Add to sequence</SheetTitle>
|
|
49
|
-
<SheetDescription>{contactIds.length === 1 ? "1 contact" : `${contactIds.length} contacts`} will be enrolled in the sequence you pick.</SheetDescription>
|
|
50
|
-
</SheetHeader>
|
|
51
|
-
<div className="space-y-3 p-4">
|
|
52
|
-
{sequences.error ? <ApiError error={sequences.error} title="Could not load sequences" onRetry={() => void sequences.refetch()} /> : null}
|
|
53
|
-
{sequences.loading && !sequences.data ? <Skeleton className="h-9 w-full" aria-label="Loading sequences" /> : null}
|
|
54
|
-
{sequences.data && sequences.data.length === 0 ? <EmptyState compact title="No active sequences" description="Launch a sequence on the Sequences page first." /> : null}
|
|
55
|
-
{sequences.data && sequences.data.length > 0 ? (
|
|
56
|
-
<>
|
|
57
|
-
<label htmlFor="enroll-sequence" className="text-sm font-medium">Sequence</label>
|
|
58
|
-
<select id="enroll-sequence" className="h-9 w-full rounded-md border bg-background px-2 text-sm" value={sequenceId} onChange={(e) => setSequenceId(e.target.value)}>
|
|
59
|
-
<option value="">Choose a sequence</option>
|
|
60
|
-
{sequences.data.map((s) => <option key={s.id} value={s.id}>{s.name || s.id}</option>)}
|
|
61
|
-
</select>
|
|
62
|
-
<Button disabled={!sequenceId} onClick={() => setStep("enroll")}>Continue</Button>
|
|
63
|
-
</>
|
|
64
|
-
) : null}
|
|
65
|
-
</div>
|
|
66
|
-
</SheetContent>
|
|
67
|
-
</Sheet>
|
|
68
|
-
{step === "enroll" && chosen ? (
|
|
69
|
-
<EnrollDialog open sequenceId={chosen.id} sequenceName={chosen.name ?? undefined} contactIds={contactIds} onClose={() => setStep("closed")} onEnrolled={() => { setStep("closed"); onEnrolled?.(); }} />
|
|
70
|
-
) : null}
|
|
71
|
-
</>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** One row of GET /contacts/{id}/deals and GET /companies/{id}/deals (the API's ContactDealResponse). */
|
|
76
|
-
interface LinkedDeal { deal_id?: string | null; name?: string | null; stage?: string | null; value?: number | null; currency?: string | null; role?: string | null }
|
|
77
|
-
|
|
78
|
-
/** The deals linked to one contact or company, each opening in the Deals page. */
|
|
79
|
-
function LinkedDeals({ kind, id }: { kind: "contacts" | "companies"; id: number }) {
|
|
80
|
-
const q = useG8Query(async (signal) => {
|
|
81
|
-
const inner = dataOf<{ items?: LinkedDeal[] } | LinkedDeal[]>(await g8Get(`/${kind}/${id}/deals`, undefined, { signal }));
|
|
82
|
-
return (Array.isArray(inner) ? inner : (inner.items ?? [])).filter((d) => d.deal_id);
|
|
83
|
-
}, [kind, id]);
|
|
84
|
-
if (q.error) return <ApiError error={q.error} title="Could not load deals" onRetry={() => void q.refetch()} />;
|
|
85
|
-
if (!q.data) return <Skeleton className="h-24 w-full" aria-label="Loading deals" />;
|
|
86
|
-
if (q.data.length === 0) return <EmptyState compact icon={<Handshake className="size-8" />} title="No deals yet" description="Deals linked to this record show up here." />;
|
|
87
|
-
return (
|
|
88
|
-
<ul className="divide-y rounded-md border" aria-label="Deals">
|
|
89
|
-
{q.data.map((d) => (
|
|
90
|
-
<li key={d.deal_id}>
|
|
91
|
-
<Link href={`/deals?id=${encodeURIComponent(d.deal_id as string)}`} className="flex items-center justify-between gap-3 p-3 text-sm hover:bg-muted">
|
|
92
|
-
<span className="min-w-0 truncate font-medium">{d.name || "Untitled deal"}</span>
|
|
93
|
-
<span className="shrink-0 text-xs text-muted-foreground">{[d.stage, d.value != null ? formatMoney(d.value, d.currency) : null].filter(Boolean).join(" · ")}</span>
|
|
94
|
-
</Link>
|
|
95
|
-
</li>
|
|
96
|
-
))}
|
|
97
|
-
</ul>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
12
|
+
import { RecordConversationsTab } from "@/components/g8/inbox/record-conversations-tab";
|
|
13
|
+
import { AddToListButton } from "@/components/g8/lists/add-to-list-button";
|
|
14
|
+
import { RecordListsTab } from "@/components/g8/lists/record-lists-tab";
|
|
15
|
+
import { AddToSequenceButton } from "@/components/g8/sequencer/add-to-sequence-button";
|
|
16
|
+
import { RecordSequencesTab } from "@/components/g8/sequencer/record-sequences-tab";
|
|
17
|
+
import { useRouter } from "next/navigation";
|
|
100
18
|
|
|
101
19
|
export default function Page() {
|
|
20
|
+
const router = useRouter();
|
|
102
21
|
return (
|
|
103
22
|
<ContactsScreen
|
|
23
|
+
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
104
24
|
renderRowActions={(c, { reload }) => <EnrichButton kind="person" lookup={{ email: c.work_email ?? undefined, linkedin_url: c.linkedin_url ?? undefined, first_name: c.first_name ?? undefined, last_name: c.last_name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" size="sm" variant="ghost" />}
|
|
105
|
-
renderBulkActions={({ ids, clear }) =>
|
|
25
|
+
renderBulkActions={({ ids, clear }) => (
|
|
26
|
+
<>
|
|
27
|
+
<AddToSequenceButton contactIds={ids} onEnrolled={clear} />
|
|
28
|
+
<AddToListButton contactIds={ids} onAdded={clear} />
|
|
29
|
+
</>
|
|
30
|
+
)}
|
|
106
31
|
renderDetailActions={(c, { reload }) => (
|
|
107
32
|
<>
|
|
108
33
|
<EnrichButton kind="person" lookup={{ email: c.work_email ?? undefined, linkedin_url: c.linkedin_url ?? undefined, first_name: c.first_name ?? undefined, last_name: c.last_name ?? undefined }} record={{ ...c }} onApplied={reload} label="Enrich" />
|
|
109
|
-
<
|
|
34
|
+
<AddToSequenceButton contactIds={[c.id]} />
|
|
35
|
+
<AddToListButton contactIds={[c.id]} />
|
|
110
36
|
{/* One call to this contact through the dialer; confirmed, and your softphone joins it. */}
|
|
111
37
|
<CallContactButton contact={{ id: c.id, first_name: c.first_name, last_name: c.last_name, full_name: c.full_name, email: c.work_email, company: c.company_name, mobile_phone: c.mobile_phone, direct_phone: c.direct_phone }} />
|
|
112
38
|
</>
|
|
113
39
|
)}
|
|
114
|
-
extraDetailTabs={[
|
|
40
|
+
extraDetailTabs={[
|
|
41
|
+
{ id: "lists", label: "Lists", render: (c) => <RecordListsTab contactId={c.id} onOpenList={(id) => router.push(`/lists?id=${id}`)} /> },
|
|
42
|
+
{ id: "sequences", label: "Sequences", render: (c) => <RecordSequencesTab contactId={c.id} onOpenSequence={(id) => router.push(`/sequences?id=${encodeURIComponent(id)}`)} /> },
|
|
43
|
+
{ id: "meetings", label: "Meetings", render: (c) => <RecordMeetingsTab contactId={c.id} onOpenMeeting={(sel) => router.push(`/meetings?sel=${encodeURIComponent(sel)}`)} /> },
|
|
44
|
+
{ id: "calls", label: "Calls", render: (c) => <RecordCallsTab contact={{ id: c.id, first_name: c.first_name, last_name: c.last_name, full_name: c.full_name, email: c.work_email, company: c.company_name, mobile_phone: c.mobile_phone, direct_phone: c.direct_phone }} onOpenCall={(room) => router.push(`/calls?id=${encodeURIComponent(room)}`)} /> },
|
|
45
|
+
{ id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="contacts" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
|
|
46
|
+
{ id: "conversations", label: "Conversations", render: (c) => <RecordConversationsTab email={c.work_email} alsoEmails={[c.personal_emails]} onOpenThread={(sel) => router.push(`/inbox?id=${encodeURIComponent(sel)}`)} /> },
|
|
47
|
+
{ id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
|
|
48
|
+
]}
|
|
115
49
|
/>
|
|
116
50
|
);
|
|
117
51
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { DealsScreen } from "@/components/g8/deals/deals-screen";
|
|
7
|
+
|
|
8
|
+
/** A deal opens its quotes, its contacts and (from a contact) their company. */
|
|
9
|
+
export default function Page() {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
return (
|
|
12
|
+
<DealsScreen
|
|
13
|
+
onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
|
|
14
|
+
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
|
|
7
|
+
import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
|
|
8
|
+
|
|
9
|
+
/** A conversation opens the sender when they are a contact in your CRM. */
|
|
10
|
+
export default function Page() {
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
return <InboxScreen renderDetailActions={(thread) => <ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />} />;
|
|
13
|
+
}
|