@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
|
@@ -25,6 +25,7 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
|
25
25
|
import { ApiError } from "@/g8/components/api-error";
|
|
26
26
|
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
27
27
|
import { DEFAULT_MAILBOX_CREDITS, GRAPH8_APP_MAILBOXES_PATH, listOf, newIdempotencyKey, objectOf } from "../lib/mailboxes-utils";
|
|
28
|
+
import { useProviderDomains } from "../lib/use-provider-domains";
|
|
28
29
|
import type { DomainRow } from "../lib/mailboxes-utils";
|
|
29
30
|
import { plural, pluralWord } from "@/g8/lib/plural";
|
|
30
31
|
import { brandUrl, useBrand } from "@/g8/hooks/use-brand";
|
|
@@ -135,7 +136,7 @@ function ImapForm({ onConnected }: { onConnected?: (id: number | string) => void
|
|
|
135
136
|
</Button>
|
|
136
137
|
{connect.error ? <ApiError error={connect.error} onRetry={connect.reset} /> : null}
|
|
137
138
|
{result ? (
|
|
138
|
-
<p role="status" className="text-sm text-
|
|
139
|
+
<p role="status" className="text-sm text-foreground">
|
|
139
140
|
{result}
|
|
140
141
|
</p>
|
|
141
142
|
) : null}
|
|
@@ -145,7 +146,7 @@ function ImapForm({ onConnected }: { onConnected?: (id: number | string) => void
|
|
|
145
146
|
|
|
146
147
|
function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id: number | string) => void; domainId?: number | string }) {
|
|
147
148
|
const brand = useBrand();
|
|
148
|
-
const domains =
|
|
149
|
+
const domains = useProviderDomains([]);
|
|
149
150
|
const pricing = useG8Query((signal) => g8Get("/provider-domains/pricing", undefined, { signal }), []);
|
|
150
151
|
const [domainId, setDomainId] = useState(initialDomainId != null ? String(initialDomainId) : "");
|
|
151
152
|
const [usernames, setUsernames] = useState("");
|
|
@@ -170,19 +171,19 @@ function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id
|
|
|
170
171
|
setDone(`Accepted ${plural(Number(res.count ?? names.length), "mailbox", "mailboxes")}; provisioning continues in the background.`);
|
|
171
172
|
onPurchased?.(domainId);
|
|
172
173
|
});
|
|
173
|
-
const connected =
|
|
174
|
+
const connected = domains.items.filter((d) => d.provider_connected);
|
|
174
175
|
const ready = domainId && names.length > 0 && person.first_name.trim() && person.last_name.trim();
|
|
175
176
|
|
|
176
177
|
return (
|
|
177
178
|
<div className="space-y-3">
|
|
178
179
|
{domains.error ? <ApiError error={domains.error} onRetry={() => void domains.refetch()} /> : null}
|
|
179
180
|
{!domains.loading && connected.length === 0 ? (
|
|
180
|
-
<p className="text-sm text-
|
|
181
|
+
<p className="text-sm text-muted-foreground">No connected domain yet. Buy and connect one with DomainPurchase first.</p>
|
|
181
182
|
) : (
|
|
182
183
|
<div className="grid gap-2 sm:grid-cols-2">
|
|
183
184
|
<select
|
|
184
185
|
aria-label="Domain"
|
|
185
|
-
className="h-9 rounded-md border border-
|
|
186
|
+
className="h-9 rounded-md border border-input px-2 text-sm"
|
|
186
187
|
value={domainId}
|
|
187
188
|
onChange={(e) => setDomainId(e.target.value)}
|
|
188
189
|
>
|
|
@@ -195,7 +196,7 @@ function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id
|
|
|
195
196
|
</select>
|
|
196
197
|
<select
|
|
197
198
|
aria-label="Platform"
|
|
198
|
-
className="h-9 rounded-md border border-
|
|
199
|
+
className="h-9 rounded-md border border-input px-2 text-sm"
|
|
199
200
|
value={platform}
|
|
200
201
|
onChange={(e) => setPlatform(e.target.value as "GOOGLE" | "MICROSOFT")}
|
|
201
202
|
>
|
|
@@ -231,7 +232,7 @@ function BuyForm({ onPurchased, domainId: initialDomainId }: { onPurchased?: (id
|
|
|
231
232
|
</Button>
|
|
232
233
|
{buy.error ? <ApiError error={buy.error} onRetry={buy.reset} /> : null}
|
|
233
234
|
{done ? (
|
|
234
|
-
<p role="status" className="text-sm text-
|
|
235
|
+
<p role="status" className="text-sm text-positive-foreground">
|
|
235
236
|
{done}
|
|
236
237
|
</p>
|
|
237
238
|
) : null}
|
|
@@ -276,7 +277,7 @@ export function ConnectMailbox({ appUrl: appUrlProp, onConnected, onPurchased, i
|
|
|
276
277
|
</div>
|
|
277
278
|
{mode === "imap" ? <ImapForm onConnected={onConnected} /> : null}
|
|
278
279
|
{mode === "oauth" ? (
|
|
279
|
-
<div className="space-y-2 rounded border border-
|
|
280
|
+
<div className="space-y-2 rounded border border-border p-3 text-sm">
|
|
280
281
|
<p>
|
|
281
282
|
Google and Microsoft mailboxes connect through a sign-in screen that only runs inside the {brand.linkLabel} app. Connect it there, then
|
|
282
283
|
come back: it appears in your mailbox list.
|
|
@@ -18,6 +18,7 @@ import { useState } from "react";
|
|
|
18
18
|
import type { ReactNode } from "react";
|
|
19
19
|
import { Button, Skeleton } from "@graph8/sdk/ui";
|
|
20
20
|
import { ApiError } from "@/g8/components/api-error";
|
|
21
|
+
import { FirstNotice } from "@/g8/components/paging-notices";
|
|
21
22
|
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
22
23
|
import { DetailPanel } from "@/g8/components/screen/detail-panel";
|
|
23
24
|
import { KeyValueList } from "@/g8/components/screen/key-value-list";
|
|
@@ -27,6 +28,7 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
|
27
28
|
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
28
29
|
import { g8Get, g8Post } from "@/g8/lib/g8-client";
|
|
29
30
|
import { fieldItems, listOf, objectOf } from "../lib/mailboxes-utils";
|
|
31
|
+
import { useProviderDomains } from "../lib/use-provider-domains";
|
|
30
32
|
import type { DomainRow, MailboxRow } from "../lib/mailboxes-utils";
|
|
31
33
|
|
|
32
34
|
export interface DomainDetailProps {
|
|
@@ -46,13 +48,16 @@ export interface DomainDetailProps {
|
|
|
46
48
|
|
|
47
49
|
type Pending = "connect" | "propagation";
|
|
48
50
|
|
|
51
|
+
/** The mailboxes API has no paging: a full read is announced where the mailboxes are listed. */
|
|
52
|
+
const MAILBOX_LIMIT = 200;
|
|
53
|
+
|
|
49
54
|
export function DomainDetail({ domainId, onClose, onExpand, onCollapse, onChanged, onBuyMailboxes, onOpenMailbox, renderActions, className }: DomainDetailProps) {
|
|
50
55
|
const [pending, setPending] = useState<Pending | null>(null);
|
|
51
56
|
const [note, setNote] = useState<string | null>(null);
|
|
52
|
-
const domains =
|
|
53
|
-
const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit:
|
|
57
|
+
const domains = useProviderDomains([domainId]);
|
|
58
|
+
const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: MAILBOX_LIMIT }, { signal }), [domainId]);
|
|
54
59
|
|
|
55
|
-
const row =
|
|
60
|
+
const row = domains.items.find((d) => String(d.id) === String(domainId));
|
|
56
61
|
const run = useG8Mutation(async (p: Pending) => {
|
|
57
62
|
if (p === "connect") {
|
|
58
63
|
await g8Post(`/provider-domains/${domainId}/connect-provider`, {});
|
|
@@ -67,7 +72,7 @@ export function DomainDetail({ domainId, onClose, onExpand, onCollapse, onChange
|
|
|
67
72
|
}, { onError: () => setPending(null) });
|
|
68
73
|
|
|
69
74
|
if (domains.error) return <ApiError error={domains.error} title="Could not load the domain" onRetry={() => void domains.refetch()} className={className} />;
|
|
70
|
-
if (domains.loading && !domains.
|
|
75
|
+
if (domains.loading && !domains.loaded) return <Skeleton className={`h-48 w-full ${className ?? ""}`} />;
|
|
71
76
|
if (!row) return <p className={`p-4 text-sm ${className ?? ""}`}>This domain no longer exists.</p>;
|
|
72
77
|
|
|
73
78
|
const onDomain = listOf<MailboxRow>(mailboxes.data).filter((m) => (m.email ?? "").toLowerCase().endsWith(`@${row.domain.toLowerCase()}`));
|
|
@@ -133,6 +138,7 @@ export function DomainDetail({ domainId, onClose, onExpand, onCollapse, onChange
|
|
|
133
138
|
))}
|
|
134
139
|
</ul>
|
|
135
140
|
)}
|
|
141
|
+
<FirstNotice shown={listOf<MailboxRow>(mailboxes.data).length} limit={MAILBOX_LIMIT} noun="mailboxes" />
|
|
136
142
|
</SectionCard>
|
|
137
143
|
),
|
|
138
144
|
},
|
|
@@ -24,9 +24,11 @@ import { g8Get, g8Post } from "@/g8/lib/g8-client";
|
|
|
24
24
|
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
25
25
|
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
26
26
|
import { ApiError } from "@/g8/components/api-error";
|
|
27
|
+
import { FirstNotice } from "@/g8/components/paging-notices";
|
|
27
28
|
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
28
29
|
import { EmptyState } from "@/g8/components/empty-state";
|
|
29
30
|
import { DEFAULT_DOMAIN_CREDITS, listOf, newIdempotencyKey, objectOf, statusClass } from "../lib/mailboxes-utils";
|
|
31
|
+
import { useProviderDomains } from "../lib/use-provider-domains";
|
|
30
32
|
import type { DomainRow } from "../lib/mailboxes-utils";
|
|
31
33
|
import { plural } from "@/g8/lib/plural";
|
|
32
34
|
import { useBrand } from "@/g8/hooks/use-brand";
|
|
@@ -66,7 +68,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
66
68
|
const [note, setNote] = useState<string | null>(null);
|
|
67
69
|
const idemKey = useRef<{ sig: string; key: string } | null>(null);
|
|
68
70
|
|
|
69
|
-
const domains =
|
|
71
|
+
const domains = useProviderDomains([]);
|
|
70
72
|
const suggest = useG8Mutation(async () => {
|
|
71
73
|
setNames(suggestionsOf(await g8Post("/provider-domains/generate-domains", { keyword: keyword.trim(), count: 10 })));
|
|
72
74
|
setSelected([]);
|
|
@@ -106,7 +108,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
106
108
|
const toggle = (d: string) => setSelected((s) => (s.includes(d) ? s.filter((x) => x !== d) : [...s, d]));
|
|
107
109
|
const buyable = selected.filter((d) => avail[d]?.available);
|
|
108
110
|
const contactOk = contact.first_name.trim() && contact.last_name.trim() && /\S+@\S+/.test(contact.email);
|
|
109
|
-
const rows =
|
|
111
|
+
const rows = domains.items;
|
|
110
112
|
|
|
111
113
|
const dialog = (() => {
|
|
112
114
|
if (!pending) return null;
|
|
@@ -141,8 +143,8 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
141
143
|
|
|
142
144
|
return (
|
|
143
145
|
<div className={`space-y-5 ${className ?? ""}`}>
|
|
144
|
-
<section className="space-y-2 rounded border border-
|
|
145
|
-
<h3 className="text-sm font-
|
|
146
|
+
<section className="space-y-2 rounded border border-border p-3">
|
|
147
|
+
<h3 className="text-sm font-medium">Find a domain</h3>
|
|
146
148
|
<form
|
|
147
149
|
className="flex gap-2"
|
|
148
150
|
onSubmit={(e) => {
|
|
@@ -165,7 +167,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
165
167
|
{d}
|
|
166
168
|
{avail[d] ? (
|
|
167
169
|
<span
|
|
168
|
-
className={`rounded px-1.5 text-xs ${avail[d].available ? "bg-
|
|
170
|
+
className={`rounded px-1.5 text-xs ${avail[d].available ? "bg-positive text-positive-foreground" : "bg-error text-error-foreground"}`}
|
|
169
171
|
>
|
|
170
172
|
{avail[d].available
|
|
171
173
|
? `available${avail[d].price != null ? ` ${avail[d].price}` : ""}`
|
|
@@ -189,8 +191,8 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
189
191
|
</Button>
|
|
190
192
|
</section>
|
|
191
193
|
|
|
192
|
-
<section className="space-y-2 rounded border border-
|
|
193
|
-
<h3 className="text-sm font-
|
|
194
|
+
<section className="space-y-2 rounded border border-border p-3">
|
|
195
|
+
<h3 className="text-sm font-medium">Registrant</h3>
|
|
194
196
|
<div className="grid gap-2 sm:grid-cols-3">
|
|
195
197
|
<Input
|
|
196
198
|
aria-label="First name"
|
|
@@ -217,7 +219,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
217
219
|
</section>
|
|
218
220
|
|
|
219
221
|
{note ? (
|
|
220
|
-
<p role="status" className="text-sm text-
|
|
222
|
+
<p role="status" className="text-sm text-foreground">
|
|
221
223
|
{note}
|
|
222
224
|
</p>
|
|
223
225
|
) : null}
|
|
@@ -226,18 +228,18 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
226
228
|
<section aria-label="Your domains">
|
|
227
229
|
{domains.error ? (
|
|
228
230
|
<ApiError error={domains.error} onRetry={() => void domains.refetch()} />
|
|
229
|
-
) : domains.loading && !domains.
|
|
231
|
+
) : domains.loading && !domains.loaded ? (
|
|
230
232
|
<Skeleton className="h-16 w-full" />
|
|
231
233
|
) : rows.length === 0 ? (
|
|
232
234
|
<EmptyState title="No sending domains yet" description={`Buy one above, or add a domain you already own in the ${brand.linkLabel} app.`} />
|
|
233
235
|
) : (
|
|
234
|
-
<ul className="divide-y rounded border border-
|
|
236
|
+
<ul className="divide-y rounded border border-border">
|
|
235
237
|
{rows.map((d) => (
|
|
236
238
|
<li key={d.id} className="flex flex-wrap items-center justify-between gap-2 px-3 py-2 text-sm">
|
|
237
239
|
<span>
|
|
238
240
|
<span className="font-medium">{d.domain}</span>{" "}
|
|
239
241
|
<span className={`rounded px-1.5 text-xs ${statusClass(d.status)}`}>{d.status ?? "unknown"}</span>{" "}
|
|
240
|
-
<span className="text-xs text-
|
|
242
|
+
<span className="text-xs text-muted-foreground">{d.mailbox_count ?? 0} mailboxes</span>
|
|
241
243
|
</span>
|
|
242
244
|
<span className="flex gap-1">
|
|
243
245
|
{!d.provider_connected && !d.is_external ? (
|
|
@@ -254,6 +256,7 @@ export function DomainPurchase({ onPurchased, className }: DomainPurchaseProps)
|
|
|
254
256
|
))}
|
|
255
257
|
</ul>
|
|
256
258
|
)}
|
|
259
|
+
<FirstNotice shown={rows.length} limit={rows.length} force={domains.truncated} noun="domains" />
|
|
257
260
|
</section>
|
|
258
261
|
|
|
259
262
|
{pending && dialog ? (
|
|
@@ -29,6 +29,7 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
|
29
29
|
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
30
30
|
import { g8Delete, g8Get, g8Post } from "@/g8/lib/g8-client";
|
|
31
31
|
import { fieldItems, listOf, objectOf } from "../lib/mailboxes-utils";
|
|
32
|
+
import { useProviderDomains } from "../lib/use-provider-domains";
|
|
32
33
|
import type { DomainRow, MailboxRow } from "../lib/mailboxes-utils";
|
|
33
34
|
import { WarmupHealth } from "./warmup-health";
|
|
34
35
|
import { useBrand } from "@/g8/hooks/use-brand";
|
|
@@ -59,7 +60,7 @@ export function MailboxDetail({ mailboxId, tab, onTabChange, onClose, onExpand,
|
|
|
59
60
|
const [pending, setPending] = useState<Pending | null>(null);
|
|
60
61
|
const [testNote, setTestNote] = useState<string | null>(null);
|
|
61
62
|
const q = useG8Query((signal) => g8Get(`/mailboxes/${mailboxId}`, undefined, { signal }), [mailboxId]);
|
|
62
|
-
const domains =
|
|
63
|
+
const domains = useProviderDomains([]);
|
|
63
64
|
|
|
64
65
|
const test = useG8Mutation(async () => {
|
|
65
66
|
const r = objectOf(await g8Post(`/mailboxes/${mailboxId}/test-connection`, {})) as TestResult;
|
|
@@ -84,7 +85,7 @@ export function MailboxDetail({ mailboxId, tab, onTabChange, onClose, onExpand,
|
|
|
84
85
|
const raw = objectOf(q.data);
|
|
85
86
|
const m = { id: String(mailboxId), email: null, ...raw } as MailboxRow;
|
|
86
87
|
const domain = (m.email ?? "").split("@")[1]?.toLowerCase();
|
|
87
|
-
const domainRow =
|
|
88
|
+
const domainRow = domains.items.find((d) => d.domain?.toLowerCase() === domain);
|
|
88
89
|
const status = m.connection_status ?? "unknown";
|
|
89
90
|
const name = m.email ?? `Mailbox ${mailboxId}`;
|
|
90
91
|
|
|
@@ -22,9 +22,13 @@ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
|
22
22
|
import { ApiError } from "@/g8/components/api-error";
|
|
23
23
|
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
24
24
|
import { EmptyState } from "@/g8/components/empty-state";
|
|
25
|
+
import { FirstNotice } from "@/g8/components/paging-notices";
|
|
25
26
|
import { listOf, objectOf, statusClass } from "../lib/mailboxes-utils";
|
|
26
27
|
import type { MailboxRow } from "../lib/mailboxes-utils";
|
|
27
28
|
|
|
29
|
+
/** The read cap. The mailboxes API has no paging, so a full read is announced ("Showing the first 200"). */
|
|
30
|
+
const MAILBOX_LIMIT = 200;
|
|
31
|
+
|
|
28
32
|
export interface MailboxListProps {
|
|
29
33
|
includeArchived?: boolean;
|
|
30
34
|
/** Called when a row is opened (for example to show WarmupHealth for it). */
|
|
@@ -47,7 +51,7 @@ export function MailboxList({ includeArchived = false, onSelect, emptyAction, cl
|
|
|
47
51
|
const [tests, setTests] = useState<Record<string, string>>({});
|
|
48
52
|
const [testing, setTesting] = useState<ReadonlySet<string>>(new Set());
|
|
49
53
|
const list = useG8Query(
|
|
50
|
-
(signal) => g8Get("/mailboxes", { limit:
|
|
54
|
+
(signal) => g8Get("/mailboxes", { limit: MAILBOX_LIMIT, include_archived: includeArchived }, { signal }),
|
|
51
55
|
[includeArchived],
|
|
52
56
|
);
|
|
53
57
|
|
|
@@ -114,13 +118,13 @@ export function MailboxList({ includeArchived = false, onSelect, emptyAction, cl
|
|
|
114
118
|
<button type="button" className="text-left font-medium hover:underline" onClick={() => onSelect?.(m)}>
|
|
115
119
|
{m.email ?? `Mailbox ${m.id}`}
|
|
116
120
|
</button>
|
|
117
|
-
<div className="text-xs text-
|
|
121
|
+
<div className="text-xs text-muted-foreground">
|
|
118
122
|
{m.display_name ?? ""}
|
|
119
123
|
{m.provider ? ` - ${m.provider}` : ""}
|
|
120
124
|
{m.is_archived ? " - archived" : ""}
|
|
121
125
|
</div>
|
|
122
126
|
{tests[m.id] ? (
|
|
123
|
-
<div role="status" className="text-xs text-
|
|
127
|
+
<div role="status" className="text-xs text-foreground">
|
|
124
128
|
{tests[m.id]}
|
|
125
129
|
</div>
|
|
126
130
|
) : null}
|
|
@@ -163,6 +167,7 @@ export function MailboxList({ includeArchived = false, onSelect, emptyAction, cl
|
|
|
163
167
|
))}
|
|
164
168
|
</TableBody>
|
|
165
169
|
</Table>
|
|
170
|
+
<FirstNotice shown={rows.length} limit={MAILBOX_LIMIT} noun="mailboxes" />
|
|
166
171
|
{pending ? (
|
|
167
172
|
<ConfirmSpendDialog
|
|
168
173
|
open
|
|
@@ -38,7 +38,9 @@ import { useUrlState } from "@/g8/hooks/use-url-state";
|
|
|
38
38
|
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
39
39
|
import { g8Get } from "@/g8/lib/g8-client";
|
|
40
40
|
import { listOf, objectOf } from "../lib/mailboxes-utils";
|
|
41
|
+
import { useProviderDomains } from "../lib/use-provider-domains";
|
|
41
42
|
import type { DomainRow, MailboxRow } from "../lib/mailboxes-utils";
|
|
43
|
+
import { FirstNotice } from "@/g8/components/paging-notices";
|
|
42
44
|
import { ConnectMailbox } from "./connect-mailbox";
|
|
43
45
|
import { DomainPurchase } from "./domain-purchase";
|
|
44
46
|
import { DomainDetail } from "./domain-detail";
|
|
@@ -83,11 +85,12 @@ export function fleetStats(summary: Record<string, unknown>): StatItem[] {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
function DomainList({ version, selectedId, onOpen }: { version: number; selectedId: string; onOpen: (d: DomainRow) => void }) {
|
|
86
|
-
const q =
|
|
87
|
-
const rows =
|
|
88
|
-
if (q.loading && !q.
|
|
88
|
+
const q = useProviderDomains([version]);
|
|
89
|
+
const rows = q.items;
|
|
90
|
+
if (q.loading && !q.loaded) return <Skeleton className="h-24 w-full" />;
|
|
89
91
|
if (rows.length === 0) return null;
|
|
90
92
|
return (
|
|
93
|
+
<>
|
|
91
94
|
<ul aria-label="Your domains" className="divide-y rounded-md border">
|
|
92
95
|
{rows.map((d) => (
|
|
93
96
|
<li key={d.id} className={`flex items-center justify-between px-3 py-2 text-sm ${String(d.id) === selectedId ? "bg-muted" : ""}`}>
|
|
@@ -99,6 +102,8 @@ function DomainList({ version, selectedId, onOpen }: { version: number; selected
|
|
|
99
102
|
</li>
|
|
100
103
|
))}
|
|
101
104
|
</ul>
|
|
105
|
+
<FirstNotice shown={rows.length} limit={rows.length} force={q.truncated} noun="domains" />
|
|
106
|
+
</>
|
|
102
107
|
);
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -81,29 +81,29 @@ function Fleet() {
|
|
|
81
81
|
{safe === false ? "Not safe to send right now" : safe === true ? "Safe to send" : "Send status unknown"}
|
|
82
82
|
</p>
|
|
83
83
|
{verdict.error ? (
|
|
84
|
-
<p className="text-sm text-
|
|
84
|
+
<p className="text-sm text-warning-foreground">
|
|
85
85
|
The deliverability check failed, so sending is not confirmed safe. Retry before you send.
|
|
86
86
|
</p>
|
|
87
87
|
) : null}
|
|
88
|
-
{typeof v.reason === "string" ? <p className="text-sm text-
|
|
88
|
+
{typeof v.reason === "string" ? <p className="text-sm text-foreground">{v.reason}</p> : null}
|
|
89
89
|
</div>
|
|
90
90
|
<dl className="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
|
91
91
|
{scalars(objectOf(summary.data))
|
|
92
92
|
.filter(([k]) => k !== "safe to send")
|
|
93
93
|
.map(([k, val]) => (
|
|
94
|
-
<div key={k} className="rounded border border-
|
|
95
|
-
<dt className="text-xs capitalize text-
|
|
96
|
-
<dd className="text-lg font-
|
|
94
|
+
<div key={k} className="rounded border border-border p-2">
|
|
95
|
+
<dt className="text-xs capitalize text-muted-foreground">{k}</dt>
|
|
96
|
+
<dd className="text-lg font-medium">{val}</dd>
|
|
97
97
|
</div>
|
|
98
98
|
))}
|
|
99
99
|
</dl>
|
|
100
100
|
{rows.length > 0 ? (
|
|
101
|
-
<ul className="divide-y rounded border border-
|
|
101
|
+
<ul className="divide-y rounded border border-border" aria-label="Degraded mailboxes">
|
|
102
102
|
{rows.map((r, i) => (
|
|
103
103
|
<li key={String(r.id ?? i)} className="flex items-center justify-between px-3 py-2 text-sm">
|
|
104
104
|
<span>
|
|
105
105
|
{r.email ?? r.mailbox_email ?? `Mailbox ${r.id}`}
|
|
106
|
-
<span className="ml-2 text-xs text-
|
|
106
|
+
<span className="ml-2 text-xs text-muted-foreground">{r.reason ?? r.reasons?.join(", ")}</span>
|
|
107
107
|
</span>
|
|
108
108
|
<span className={`rounded px-2 py-0.5 text-xs ${statusClass(r.severity ?? r.status)}`}>
|
|
109
109
|
{r.severity ?? r.status ?? "watch"}
|
|
@@ -134,7 +134,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
|
|
|
134
134
|
const w = warm.data?.data;
|
|
135
135
|
const a = w?.analytics;
|
|
136
136
|
return (
|
|
137
|
-
<section className="space-y-3 rounded border border-
|
|
137
|
+
<section className="space-y-3 rounded border border-border p-3" aria-label="Warmup">
|
|
138
138
|
<div className="flex items-center justify-between">
|
|
139
139
|
<h3 className="font-medium">
|
|
140
140
|
Warmup {w?.warmup_enabled ? "is on" : "is off"}
|
|
@@ -155,7 +155,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
|
|
|
155
155
|
</div>
|
|
156
156
|
</div>
|
|
157
157
|
{w?.error ? (
|
|
158
|
-
<p role="alert" className="text-sm text-
|
|
158
|
+
<p role="alert" className="text-sm text-warning-foreground">
|
|
159
159
|
The warmup provider did not answer: {w.error}
|
|
160
160
|
</p>
|
|
161
161
|
) : null}
|
|
@@ -163,22 +163,22 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
|
|
|
163
163
|
<>
|
|
164
164
|
<dl className="grid grid-cols-2 gap-2 sm:grid-cols-4 text-sm">
|
|
165
165
|
<div>
|
|
166
|
-
<dt className="text-xs text-
|
|
167
|
-
<dd className="font-
|
|
166
|
+
<dt className="text-xs text-muted-foreground">Sent</dt>
|
|
167
|
+
<dd className="font-medium">{a.total_sent ?? 0}</dd>
|
|
168
168
|
</div>
|
|
169
169
|
<div>
|
|
170
|
-
<dt className="text-xs text-
|
|
171
|
-
<dd className="font-
|
|
170
|
+
<dt className="text-xs text-muted-foreground">Inbox rate</dt>
|
|
171
|
+
<dd className="font-medium">
|
|
172
172
|
{a.inbox_rate != null ? `${Math.round(a.inbox_rate <= 1 ? a.inbox_rate * 100 : a.inbox_rate)}%` : "-"}
|
|
173
173
|
</dd>
|
|
174
174
|
</div>
|
|
175
175
|
<div>
|
|
176
|
-
<dt className="text-xs text-
|
|
177
|
-
<dd className="font-
|
|
176
|
+
<dt className="text-xs text-muted-foreground">Spam</dt>
|
|
177
|
+
<dd className="font-medium">{a.total_spam ?? 0}</dd>
|
|
178
178
|
</div>
|
|
179
179
|
<div>
|
|
180
|
-
<dt className="text-xs text-
|
|
181
|
-
<dd className="font-
|
|
180
|
+
<dt className="text-xs text-muted-foreground">Reputation</dt>
|
|
181
|
+
<dd className="font-medium">{a.reputation ?? "-"}</dd>
|
|
182
182
|
</div>
|
|
183
183
|
</dl>
|
|
184
184
|
{(a.daily_stats ?? []).length > 0 ? (
|
|
@@ -187,7 +187,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
|
|
|
187
187
|
<span
|
|
188
188
|
key={d.date}
|
|
189
189
|
title={`${d.date}: ${d.sent} sent, ${d.inbox} inbox`}
|
|
190
|
-
className="w-2 rounded-t bg-
|
|
190
|
+
className="w-2 rounded-t bg-foreground/70"
|
|
191
191
|
style={{ height: `${Math.max(4, Math.min(100, d.sent * 4))}%` }}
|
|
192
192
|
/>
|
|
193
193
|
))}
|
|
@@ -195,7 +195,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
|
|
|
195
195
|
) : null}
|
|
196
196
|
</>
|
|
197
197
|
) : (
|
|
198
|
-
<p className="text-sm text-
|
|
198
|
+
<p className="text-sm text-muted-foreground">No warmup analytics yet.</p>
|
|
199
199
|
)}
|
|
200
200
|
<Button size="sm" variant="outline" onClick={() => setConfirm(true)}>
|
|
201
201
|
Run placement test
|
|
@@ -203,7 +203,7 @@ function MailboxWarmup({ mailboxId }: { mailboxId: string | number }) {
|
|
|
203
203
|
{toggle.error ? <ApiError error={toggle.error} onRetry={toggle.reset} /> : null}
|
|
204
204
|
{placement.error ? <ApiError error={placement.error} onRetry={placement.reset} /> : null}
|
|
205
205
|
{note ? (
|
|
206
|
-
<p role="status" className="text-sm text-
|
|
206
|
+
<p role="status" className="text-sm text-positive-foreground">
|
|
207
207
|
{note}
|
|
208
208
|
</p>
|
|
209
209
|
) : null}
|
|
@@ -53,10 +53,10 @@ export function objectOf(envelope: unknown): Record<string, unknown> {
|
|
|
53
53
|
/** Tailwind classes for a connection or health state. */
|
|
54
54
|
export function statusClass(status: string | null | undefined): string {
|
|
55
55
|
const s = (status ?? "").toLowerCase();
|
|
56
|
-
if (["active", "connected", "healthy", "completed", "receiving"].includes(s)) return "bg-
|
|
57
|
-
if (["paused", "watch", "warming", "pending", "connecting", "provisioning"].includes(s)) return "bg-
|
|
58
|
-
if (s) return "bg-
|
|
59
|
-
return "bg-
|
|
56
|
+
if (["active", "connected", "healthy", "completed", "receiving"].includes(s)) return "bg-positive text-positive-foreground";
|
|
57
|
+
if (["paused", "watch", "warming", "pending", "connecting", "provisioning"].includes(s)) return "bg-warning text-warning-foreground";
|
|
58
|
+
if (s) return "bg-error text-error-foreground";
|
|
59
|
+
return "bg-muted text-foreground";
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Scalar fields of an open-shaped object, for a summary grid. */
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* useProviderDomains: every sending domain of the workspace. The domains API pages (`page`, `limit`), so this reads page
|
|
5
|
+
* after page (100 per page, up to 20 pages) instead of one capped call; `truncated` says the page cap was reached.
|
|
6
|
+
*
|
|
7
|
+
* SDK operations: GET /provider-domains?page&limit (providerDomains.listProviderDomains) read
|
|
8
|
+
*/
|
|
9
|
+
import { g8Get } from "@/g8/lib/g8-client";
|
|
10
|
+
import { useAllPages } from "@/g8/hooks/use-all-pages";
|
|
11
|
+
import { hasNextOf } from "@/g8/lib/paging";
|
|
12
|
+
import { listOf } from "./mailboxes-utils";
|
|
13
|
+
import type { DomainRow } from "./mailboxes-utils";
|
|
14
|
+
|
|
15
|
+
export const DOMAIN_PAGE = 100;
|
|
16
|
+
|
|
17
|
+
export function useProviderDomains(deps: readonly unknown[] = [], enabled = true) {
|
|
18
|
+
return useAllPages<DomainRow>(async (page, signal) => {
|
|
19
|
+
const env = await g8Get("/provider-domains", { page, limit: DOMAIN_PAGE }, { signal });
|
|
20
|
+
const rows = listOf<DomainRow>(env);
|
|
21
|
+
return { rows, hasNext: hasNextOf(env, DOMAIN_PAGE, rows.length) };
|
|
22
|
+
}, deps, { enabled });
|
|
23
|
+
}
|
|
@@ -133,3 +133,15 @@ Tiers: `read` and `write` need no confirmation; `billable`, `external` and `dest
|
|
|
133
133
|
| `newsletter.unsubscribeSubscriber` | `PATCH /api/v1/newsletters/{newsletter_id}/subscribers/{subscriber_id}/unsubscribe` | external | `newsletter:run` | SubscriberTable |
|
|
134
134
|
| `newsletter.deleteSubscriber` | `DELETE /api/v1/newsletters/{newsletter_id}/subscribers/{subscriber_id}` | destructive | `newsletter:delete` | SubscriberTable |
|
|
135
135
|
| `newsletter.getIssueAnalyticsKpis` | `GET /api/v1/newsletters/{newsletter_id}/issues/{issue_id}/analytics/kpis` | read | `newsletter:read` | NewsletterStats |
|
|
136
|
+
|
|
137
|
+
## Depth added (C7c)
|
|
138
|
+
|
|
139
|
+
New tabs on `NewsletterScreen`: **Templates**, **Lists**, **Settings**; issues gain a **History** tab; a subscriber shows **Activity on an issue**.
|
|
140
|
+
|
|
141
|
+
- `NewsletterSettings`: rename and goal, sender and opt-in policy (only changed fields are sent), sending domain (start verification is an external confirm, DNS records are listed, status check), resume a paused newsletter (external confirm), mark completed.
|
|
142
|
+
- `NewsletterListSyncs`: turn a list sync on (the consent statement is shown and sent), run it now, stop it (destructive). Lists page with Load more.
|
|
143
|
+
- `NewsletterTemplates`: use a template for a draft issue, approve or archive, rename, delete (destructive), write one with AI (billable confirm).
|
|
144
|
+
- `IssueVersions`: version history with restore (drafts only, second click) and Duplicate (external confirm).
|
|
145
|
+
- `SubscriberTimeline`: delivered, opened and clicked for one subscriber on one sent issue.
|
|
146
|
+
|
|
147
|
+
Not covered (and why): `rewriteIssueText`, `setIssueText`, `setIssueMediaEdit`, `setIssueButtonAction` and the template equivalents need element ids the HTML editor does not expose; `linkNewsletterCampaign` needs a campaign picker (owned by the pickers PR); `cloneTemplate`, `createTemplateFromLayout` and `batchTemplateGenerationStatus` are template-gallery flows; `getNewsletterSubscriberVerification` status is not shown (verify still runs and reports through the existing flow); `getNewsletterIssueConversation` is the hosted editor's chat. The subscriber and list pickers keep their existing paging.
|