@graph8/sdk 0.362.0 → 0.373.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -10
- package/blocks/_foundation/README.md +78 -1
- package/blocks/_foundation/block.json +123 -6
- package/blocks/_foundation/components/api-error.tsx +9 -7
- package/blocks/_foundation/components/brand-provider.tsx +23 -0
- package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
- package/blocks/_foundation/components/confirmed-action.tsx +90 -0
- package/blocks/_foundation/components/connect-graph8.tsx +280 -0
- package/blocks/_foundation/components/empty-state.tsx +5 -2
- package/blocks/_foundation/components/list-select.tsx +112 -0
- package/blocks/_foundation/components/paging-notices.tsx +55 -0
- package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
- package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
- package/blocks/_foundation/components/screen/index.ts +2 -0
- package/blocks/_foundation/components/screen/record-link.tsx +76 -0
- package/blocks/_foundation/components/screen/split-view.tsx +1 -1
- package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
- package/blocks/_foundation/components/screen/time-series-chart.tsx +15 -97
- package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
- package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
- package/blocks/_foundation/hooks/use-brand.ts +52 -0
- package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
- package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
- package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
- package/blocks/_foundation/hooks/use-paginated.ts +120 -0
- package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
- package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
- package/blocks/_foundation/lib/g8-cache.ts +454 -0
- package/blocks/_foundation/lib/g8-client.ts +1 -1
- package/blocks/_foundation/lib/paging.ts +57 -0
- package/blocks/_foundation/lib/plural.ts +23 -0
- package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
- package/blocks/_foundation/server/connect-route.ts +15 -0
- package/blocks/_foundation/server/connect.ts +127 -0
- package/blocks/_foundation/server/local-key.ts +70 -0
- package/blocks/_foundation/server/proxy.ts +103 -3
- package/blocks/_foundation/server/route.ts +16 -4
- package/blocks/agents/README.md +93 -2
- package/blocks/agents/block.json +343 -2
- package/blocks/agents/components/agent-activity.tsx +112 -0
- package/blocks/agents/components/agent-approvals.tsx +135 -0
- package/blocks/agents/components/agent-builder.tsx +29 -10
- package/blocks/agents/components/agent-conversations.tsx +143 -0
- package/blocks/agents/components/agent-knowledge-panel.tsx +4 -3
- package/blocks/agents/components/agent-operators.tsx +164 -0
- package/blocks/agents/components/agent-run-detail.tsx +119 -0
- package/blocks/agents/components/agent-runs.tsx +19 -10
- package/blocks/agents/components/agent-skills-panel.tsx +7 -7
- package/blocks/agents/components/agent-test-history.tsx +121 -0
- package/blocks/agents/components/agents-screen.tsx +226 -0
- package/blocks/agents/components/assistant-sessions.tsx +150 -0
- package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
- package/blocks/agents/components/from-template-button.tsx +65 -0
- package/blocks/agents/components/knowledge-browser.tsx +107 -0
- package/blocks/agents/components/knowledge-detail.tsx +156 -0
- package/blocks/agents/components/knowledge-form.tsx +69 -0
- package/blocks/agents/components/name-dialog.tsx +32 -0
- package/blocks/agents/components/receptionist-button.tsx +46 -0
- package/blocks/agents/components/save-as-template-button.tsx +32 -0
- package/blocks/agents/components/skill-editor.tsx +147 -0
- package/blocks/agents/components/skill-library.tsx +111 -0
- package/blocks/agents/lib/agent-types.ts +7 -1
- package/blocks/agents/lib/facts.tsx +13 -0
- package/blocks/agents/lib/knowledge-utils.ts +49 -0
- package/blocks/agents/lib/load-more.tsx +141 -0
- package/blocks/agents/lib/skill-utils.ts +91 -0
- package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
- package/blocks/analytics/README.md +13 -0
- package/blocks/analytics/block.json +45 -1
- package/blocks/analytics/components/analytics-screen.tsx +222 -0
- package/blocks/analytics/components/kpi-tiles.tsx +1 -1
- package/blocks/analytics/components/leaderboard.tsx +14 -7
- package/blocks/analytics/components/report-builder.tsx +38 -11
- package/blocks/analytics/components/report-detail.tsx +84 -0
- package/blocks/analytics/components/trend-chart.tsx +62 -0
- package/blocks/appointments/README.md +6 -0
- package/blocks/appointments/block.json +33 -2
- package/blocks/appointments/components/availability-editor.tsx +17 -5
- package/blocks/appointments/components/booking-page.tsx +2 -2
- package/blocks/appointments/components/event-type-editor.tsx +2 -1
- package/blocks/appointments/components/event-type-related.tsx +4 -2
- package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
- package/blocks/appointments/components/meeting-detail.tsx +2 -2
- package/blocks/appointments/components/meeting-list.tsx +38 -18
- package/blocks/appointments/components/meetings-screen.tsx +7 -4
- package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
- package/blocks/appointments/components/scheduling-screen.tsx +2 -1
- package/blocks/block.schema.json +18 -0
- package/blocks/block.types.ts +17 -0
- package/blocks/campaigns/README.md +41 -11
- package/blocks/campaigns/block.json +105 -42
- package/blocks/campaigns/components/campaign-assets.tsx +17 -6
- package/blocks/campaigns/components/campaign-launch-step.tsx +114 -26
- package/blocks/campaigns/components/campaign-list.tsx +7 -3
- package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
- package/blocks/campaigns/components/campaign-wizard.tsx +52 -14
- package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
- package/blocks/campaigns/components/icp-detail.tsx +163 -0
- package/blocks/campaigns/components/icp-editor.tsx +22 -17
- package/blocks/campaigns/components/persona-detail.tsx +181 -0
- package/blocks/campaigns/components/persona-editor.tsx +19 -11
- package/blocks/campaigns/lib/campaigns-utils.ts +138 -5
- package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
- package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
- package/blocks/coach/README.md +10 -0
- package/blocks/coach/block.json +16 -1
- package/blocks/coach/components/call-scorecard.tsx +7 -7
- package/blocks/coach/components/coach-screen.tsx +222 -0
- package/blocks/company-intelligence/README.md +132 -0
- package/blocks/company-intelligence/block.json +318 -0
- package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
- package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
- package/blocks/company-intelligence/components/company-profile.tsx +68 -0
- package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
- package/blocks/company-intelligence/components/report-detail.tsx +189 -0
- package/blocks/company-intelligence/components/research-reports.tsx +136 -0
- package/blocks/company-intelligence/components/research-run.tsx +161 -0
- package/blocks/company-intelligence/components/source-detail.tsx +117 -0
- package/blocks/company-intelligence/components/source-view.tsx +274 -0
- package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
- package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
- package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
- package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
- package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
- package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
- package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
- package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
- package/blocks/copilot/README.md +13 -0
- package/blocks/copilot/block.json +14 -1
- package/blocks/copilot/components/copilot-memory.tsx +6 -2
- package/blocks/copilot/components/copilot-panel.tsx +14 -5
- package/blocks/copilot/components/copilot-screen.tsx +148 -0
- package/blocks/copilot/components/copilot-team-chat.tsx +13 -6
- package/blocks/crm/README.md +34 -0
- package/blocks/crm/block.json +107 -2
- package/blocks/crm/components/companies-screen.tsx +359 -0
- package/blocks/crm/components/company-detail.tsx +10 -8
- package/blocks/crm/components/company-table.tsx +11 -4
- package/blocks/crm/components/contact-detail.tsx +10 -7
- package/blocks/crm/components/contact-lookup-link.tsx +44 -0
- package/blocks/crm/components/contact-table.tsx +10 -3
- package/blocks/crm/components/contacts-screen.tsx +377 -0
- package/blocks/crm/components/dedupe-review.tsx +13 -6
- package/blocks/crm/components/import-wizard.tsx +7 -9
- package/blocks/crm/components/list-select.tsx +20 -0
- package/blocks/crm/components/notes-panel.tsx +4 -4
- package/blocks/crm/components/record-engagement-tab.tsx +103 -0
- package/blocks/crm/components/record-form.tsx +6 -4
- package/blocks/crm/components/task-list.tsx +4 -4
- package/blocks/crm/components/tasks-screen.tsx +399 -0
- package/blocks/crm/hooks/use-all-lists.ts +18 -0
- package/blocks/crm/hooks/use-custom-fields.ts +6 -1
- package/blocks/crm/hooks/use-page-query.ts +9 -1
- package/blocks/crm/lib/crm-types.ts +26 -2
- package/blocks/crm/lib/read-lists.ts +16 -0
- package/blocks/crm/lib/screen-parts.tsx +105 -0
- package/blocks/deals/README.md +24 -6
- package/blocks/deals/block.json +160 -5
- package/blocks/deals/components/close-deal-dialog.tsx +169 -0
- package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
- package/blocks/deals/components/deal-detail.tsx +25 -13
- package/blocks/deals/components/deal-filters.tsx +65 -0
- package/blocks/deals/components/deal-line-items.tsx +120 -0
- package/blocks/deals/components/deal-notes-tab.tsx +97 -0
- package/blocks/deals/components/deal-related.tsx +31 -10
- package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
- package/blocks/deals/components/deals-screen.tsx +66 -26
- package/blocks/deals/components/forecast-view.tsx +4 -4
- package/blocks/deals/components/pipeline-board.tsx +8 -6
- package/blocks/deals/components/pipeline-editor.tsx +132 -0
- package/blocks/deals/components/record-deals-tab.tsx +62 -0
- package/blocks/deals/components/stage-editor.tsx +5 -5
- package/blocks/deals/hooks/use-pipeline-deals.ts +21 -4
- package/blocks/deals/hooks/use-pipelines.ts +1 -1
- package/blocks/deals/lib/deal-extras.ts +156 -0
- package/blocks/dialer/README.md +45 -4
- package/blocks/dialer/block.json +84 -3
- package/blocks/dialer/components/call-contact-button.tsx +269 -0
- package/blocks/dialer/components/call-log-screen.tsx +208 -0
- package/blocks/dialer/components/call-log.tsx +23 -7
- package/blocks/dialer/components/call-queue.tsx +4 -4
- package/blocks/dialer/components/dialer-screen.tsx +361 -0
- package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
- package/blocks/dialer/components/disposition-panel.tsx +1 -1
- package/blocks/dialer/components/live-calls-table.tsx +7 -3
- package/blocks/dialer/components/record-calls-tab.tsx +101 -0
- package/blocks/dialer/components/session-start-form.tsx +61 -12
- package/blocks/dialer/components/softphone.tsx +4 -2
- package/blocks/enrichment/README.md +29 -7
- package/blocks/enrichment/block.json +49 -9
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +83 -29
- package/blocks/enrichment/components/email-verify-badge.tsx +31 -11
- package/blocks/enrichment/components/enrich-button.tsx +32 -9
- package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
- package/blocks/enrichment/components/enrichment-screen.tsx +341 -0
- package/blocks/enrichment/components/waterfall-config.tsx +66 -17
- package/blocks/enrichment/lib/enrichment-types.ts +39 -0
- package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
- package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
- package/blocks/forms/README.md +8 -0
- package/blocks/forms/block.json +139 -18
- package/blocks/forms/components/form-activity.tsx +71 -0
- package/blocks/forms/components/form-backfill.tsx +108 -0
- package/blocks/forms/components/form-builder.tsx +4 -2
- package/blocks/forms/components/form-embed.tsx +4 -2
- package/blocks/forms/components/form-groups.tsx +151 -0
- package/blocks/forms/components/form-mapping-editor.tsx +6 -9
- package/blocks/forms/components/form-route-history.tsx +72 -0
- package/blocks/forms/components/form-saved-views.tsx +81 -0
- package/blocks/forms/components/forms-screen.tsx +56 -19
- package/blocks/forms/components/more-rows.tsx +30 -0
- package/blocks/forms/components/submissions-export.tsx +67 -0
- package/blocks/forms/components/submissions-table.tsx +17 -6
- package/blocks/forms/lib/forms-extra.ts +67 -0
- package/blocks/inbox/README.md +32 -3
- package/blocks/inbox/block.json +29 -2
- package/blocks/inbox/components/inbox-list.tsx +7 -3
- package/blocks/inbox/components/inbox-screen.tsx +294 -27
- package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
- package/blocks/inbox/components/reply-composer.tsx +65 -9
- package/blocks/inbox/components/tag-and-assign.tsx +4 -2
- package/blocks/inbox/components/thread-badge.tsx +2 -2
- package/blocks/inbox/components/thread-view.tsx +1 -1
- package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
- package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
- package/blocks/inbox/lib/inbox-types.ts +47 -1
- package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
- package/blocks/integrations/README.md +63 -1
- package/blocks/integrations/block.json +234 -15
- package/blocks/integrations/components/ad-accounts.tsx +82 -0
- package/blocks/integrations/components/connection-health.tsx +59 -0
- package/blocks/integrations/components/crm-detail.tsx +119 -0
- package/blocks/integrations/components/crm-records.tsx +103 -0
- package/blocks/integrations/components/field-mappings.tsx +61 -0
- package/blocks/integrations/components/integration-list.tsx +13 -8
- package/blocks/integrations/components/integrations-screen.tsx +152 -0
- package/blocks/integrations/components/migration-panel.tsx +133 -0
- package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
- package/blocks/integrations/components/sync-status.tsx +11 -9
- package/blocks/integrations/components/sync-triggers.tsx +134 -0
- package/blocks/integrations/components/webhook-detail.tsx +179 -0
- package/blocks/integrations/components/webhook-manager.tsx +43 -25
- package/blocks/integrations/components/workspace-tools.tsx +127 -0
- package/blocks/integrations/lib/crm-utils.ts +39 -0
- package/blocks/integrations/lib/facts.tsx +13 -0
- package/blocks/integrations/lib/integrations-utils.ts +11 -1
- package/blocks/integrations/lib/load-more.tsx +141 -0
- package/blocks/intent/README.md +14 -0
- package/blocks/intent/block.json +27 -1
- package/blocks/intent/components/account-intent-score.tsx +5 -5
- package/blocks/intent/components/intent-feed.tsx +13 -10
- package/blocks/intent/components/intent-keyword-manager.tsx +16 -12
- package/blocks/intent/components/intent-screen.tsx +265 -0
- package/blocks/intent/components/keyword-detail.tsx +124 -0
- package/blocks/intent/lib/intent-utils.ts +4 -4
- package/blocks/lifecycle/block.json +13 -8
- package/blocks/lifecycle/components/account-related.tsx +8 -1
- package/blocks/lifecycle/components/churn-list.tsx +5 -2
- package/blocks/lifecycle/components/customer-table.tsx +1 -1
- package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
- package/blocks/lifecycle/components/lifecycle-screen.tsx +3 -1
- package/blocks/lifecycle/lib/lifecycle.ts +1 -1
- package/blocks/lists/README.md +20 -1
- package/blocks/lists/block.json +69 -3
- package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
- package/blocks/lists/components/add-to-list-button.tsx +20 -9
- package/blocks/lists/components/list-picker.tsx +17 -3
- package/blocks/lists/components/list-table.tsx +12 -6
- package/blocks/lists/components/lists-screen.tsx +346 -0
- package/blocks/lists/components/record-lists-tab.tsx +99 -0
- package/blocks/lists/components/suppression-manager.tsx +33 -8
- package/blocks/lists/components/suppressions-screen.tsx +264 -0
- package/blocks/mailboxes/README.md +17 -0
- package/blocks/mailboxes/block.json +69 -17
- package/blocks/mailboxes/components/connect-mailbox.tsx +32 -21
- package/blocks/mailboxes/components/domain-detail.tsx +148 -0
- package/blocks/mailboxes/components/domain-purchase.tsx +20 -14
- package/blocks/mailboxes/components/mailbox-detail.tsx +157 -0
- package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
- package/blocks/mailboxes/components/mailboxes-screen.tsx +228 -0
- package/blocks/mailboxes/components/warmup-health.tsx +24 -22
- package/blocks/mailboxes/lib/mailboxes-utils.ts +26 -5
- package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
- package/blocks/newsletter/README.md +12 -0
- package/blocks/newsletter/block.json +157 -24
- package/blocks/newsletter/components/issue-scheduler.tsx +4 -3
- package/blocks/newsletter/components/issue-versions.tsx +93 -0
- package/blocks/newsletter/components/more-rows.tsx +30 -0
- package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
- package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
- package/blocks/newsletter/components/newsletter-screen.tsx +39 -14
- package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
- package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
- package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
- package/blocks/newsletter/components/subscriber-table.tsx +5 -7
- package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
- package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
- package/blocks/nurture/block.json +28 -14
- package/blocks/nurture/components/nurture-builder.tsx +13 -3
- package/blocks/nurture/components/nurture-enroll.tsx +12 -10
- package/blocks/nurture/components/nurture-form.tsx +27 -10
- package/blocks/nurture/components/nurture-screen.tsx +26 -11
- package/blocks/nurture/components/nurture-stats.tsx +5 -4
- package/blocks/nurture/lib/nurture-types.ts +1 -1
- package/blocks/objects/README.md +18 -1
- package/blocks/objects/block.json +42 -21
- package/blocks/objects/components/attribute-field-input.tsx +14 -1
- package/blocks/objects/components/object-not-found.tsx +3 -1
- package/blocks/objects/components/object-record-detail.tsx +10 -2
- package/blocks/objects/components/object-schema-editor.tsx +4 -2
- package/blocks/objects/components/object-table.tsx +13 -3
- package/blocks/objects/components/objects-screen.tsx +147 -0
- package/blocks/objects/components/record-roles-panel.tsx +15 -5
- package/blocks/objects/lib/object-types.ts +1 -1
- package/blocks/pages/README.md +14 -0
- package/blocks/pages/block.json +140 -23
- package/blocks/pages/components/brand-kit-editor.tsx +5 -2
- package/blocks/pages/components/content-library.tsx +9 -4
- package/blocks/pages/components/more-rows.tsx +30 -0
- package/blocks/pages/components/page-ai-tools.tsx +196 -0
- package/blocks/pages/components/page-analytics.tsx +176 -0
- package/blocks/pages/components/page-clone.tsx +99 -0
- package/blocks/pages/components/page-domains.tsx +167 -0
- package/blocks/pages/components/page-editor.tsx +1 -1
- package/blocks/pages/components/page-generation-status.tsx +48 -0
- package/blocks/pages/components/page-list.tsx +2 -1
- package/blocks/pages/components/page-tracking.tsx +137 -0
- package/blocks/pages/components/pages-screen.tsx +57 -16
- package/blocks/pages/components/seo-report.tsx +6 -1
- package/blocks/pages/lib/studio.ts +7 -0
- package/blocks/phone-numbers/README.md +74 -0
- package/blocks/phone-numbers/block.json +262 -0
- package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
- package/blocks/phone-numbers/components/assign-number.tsx +104 -0
- package/blocks/phone-numbers/components/number-search.tsx +239 -0
- package/blocks/phone-numbers/components/number-settings.tsx +134 -0
- package/blocks/phone-numbers/components/number-usage.tsx +97 -0
- package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
- package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
- package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
- package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
- package/blocks/phone-numbers/components/release-number.tsx +126 -0
- package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
- package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
- package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
- package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
- package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
- package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
- package/blocks/pickers/README.md +23 -0
- package/blocks/pickers/block.json +103 -0
- package/blocks/pickers/components/company-picker.tsx +90 -0
- package/blocks/pickers/components/contact-picker.tsx +131 -0
- package/blocks/pickers/components/member-picker.tsx +99 -0
- package/blocks/pickers/components/search-picker.tsx +98 -0
- package/blocks/pickers/hooks/use-current-member.ts +104 -0
- package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
- package/blocks/pickers/lib/use-debounced.ts +13 -0
- package/blocks/quotes/README.md +13 -0
- package/blocks/quotes/block.json +65 -13
- package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
- package/blocks/quotes/components/product-picker.tsx +3 -1
- package/blocks/quotes/components/quote-builder.tsx +1 -1
- package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
- package/blocks/quotes/components/quote-list.tsx +5 -3
- package/blocks/quotes/components/quote-related.tsx +8 -4
- package/blocks/quotes/components/quote-viewer.tsx +7 -2
- package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
- package/blocks/radar/README.md +10 -0
- package/blocks/radar/block.json +41 -1
- package/blocks/radar/components/competitor-battle-card.tsx +1 -1
- package/blocks/radar/components/competitor-detail.tsx +117 -0
- package/blocks/radar/components/competitor-tracker.tsx +3 -1
- package/blocks/radar/components/radar-overview.tsx +4 -4
- package/blocks/radar/components/radar-report.tsx +4 -2
- package/blocks/radar/components/radar-screen.tsx +214 -0
- package/blocks/radar/components/report-detail.tsx +104 -0
- package/blocks/search/README.md +10 -0
- package/blocks/search/block.json +16 -1
- package/blocks/search/components/filter-builder.tsx +3 -3
- package/blocks/search/components/prospect-results.tsx +5 -3
- package/blocks/search/components/prospect-search-screen.tsx +235 -0
- package/blocks/search/components/saved-searches.tsx +4 -1
- package/blocks/search/hooks/use-filter-options.ts +20 -6
- package/blocks/search/lib/search-types.ts +2 -0
- package/blocks/sequencer/README.md +55 -9
- package/blocks/sequencer/block.json +126 -19
- package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
- package/blocks/sequencer/components/enroll-dialog.tsx +11 -5
- package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
- package/blocks/sequencer/components/sequence-builder.tsx +224 -60
- package/blocks/sequencer/components/sequence-detail.tsx +261 -0
- package/blocks/sequencer/components/sequence-enrollments.tsx +116 -0
- package/blocks/sequencer/components/sequence-list.tsx +34 -9
- package/blocks/sequencer/components/sequence-stats.tsx +11 -10
- package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
- package/blocks/sequencer/components/sequences-screen.tsx +162 -0
- package/blocks/sequencer/components/step-editor.tsx +14 -12
- package/blocks/sequencer/lib/sequencer-utils.ts +133 -10
- package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
- package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
- package/blocks/social-ads/README.md +19 -0
- package/blocks/social-ads/block.json +233 -1
- package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
- package/blocks/social-ads/components/ads-screen.tsx +152 -0
- package/blocks/social-ads/components/audience-sync-detail.tsx +194 -0
- package/blocks/social-ads/components/audience-sync.tsx +17 -9
- package/blocks/social-ads/components/campaign-ads.tsx +166 -0
- package/blocks/social-ads/components/campaign-detail.tsx +138 -0
- package/blocks/social-ads/components/campaign-editor.tsx +134 -0
- package/blocks/social-ads/components/creative-concepts.tsx +74 -0
- package/blocks/social-ads/components/creative-detail.tsx +155 -0
- package/blocks/social-ads/components/creative-gallery.tsx +35 -7
- package/blocks/social-ads/components/creative-revisions.tsx +57 -0
- package/blocks/social-ads/components/creative-tools.tsx +176 -0
- package/blocks/social-ads/components/post-composer.tsx +7 -3
- package/blocks/social-ads/components/post-detail.tsx +163 -0
- package/blocks/social-ads/components/social-ideas.tsx +64 -0
- package/blocks/social-ads/components/social-performance.tsx +80 -0
- package/blocks/social-ads/components/social-post-list.tsx +11 -3
- package/blocks/social-ads/components/social-screen.tsx +187 -0
- package/blocks/social-ads/lib/ads-extra.ts +67 -0
- package/blocks/surveys/README.md +91 -0
- package/blocks/surveys/block.json +299 -0
- package/blocks/surveys/components/account-nps.tsx +69 -0
- package/blocks/surveys/components/contact-survey-history.tsx +52 -0
- package/blocks/surveys/components/org-summary.tsx +82 -0
- package/blocks/surveys/components/sending-domains.tsx +113 -0
- package/blocks/surveys/components/survey-form.tsx +126 -0
- package/blocks/surveys/components/survey-responses.tsx +145 -0
- package/blocks/surveys/components/survey-results.tsx +72 -0
- package/blocks/surveys/components/survey-send-history.tsx +58 -0
- package/blocks/surveys/components/survey-send.tsx +162 -0
- package/blocks/surveys/components/survey-triggers.tsx +166 -0
- package/blocks/surveys/components/surveys-screen.tsx +234 -0
- package/blocks/surveys/lib/surveys-types.ts +228 -0
- package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
- package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
- package/blocks/surveys/recipes/survey-score-report.ts +27 -0
- package/blocks/visitors/README.md +10 -0
- package/blocks/visitors/block.json +25 -1
- package/blocks/visitors/components/snippet-install.tsx +21 -10
- package/blocks/visitors/components/traffic-chart.tsx +4 -4
- package/blocks/visitors/components/visitor-company-card.tsx +26 -16
- package/blocks/visitors/components/visitor-detail.tsx +105 -0
- package/blocks/visitors/components/visitor-feed.tsx +16 -7
- package/blocks/visitors/components/visitors-screen.tsx +166 -0
- package/blocks/webchat/README.md +10 -0
- package/blocks/webchat/block.json +21 -7
- package/blocks/webchat/components/chat-widget-config.tsx +8 -5
- package/blocks/webchat/components/conversation-list.tsx +8 -4
- package/blocks/webchat/components/webchat-screen.tsx +245 -0
- package/blocks/workbench/README.md +11 -0
- package/blocks/workbench/block.json +48 -1
- package/blocks/workbench/components/column-recipe-editor.tsx +5 -3
- package/blocks/workbench/components/run-progress.tsx +1 -1
- package/blocks/workbench/components/workbench-grid.tsx +10 -4
- package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
- package/blocks/workbench/components/workbench-screen.tsx +202 -0
- package/blocks/workbench/components/workbench-sources.tsx +194 -0
- package/blocks/workbench/components/workbench-table-list.tsx +14 -2
- package/blocks/workflows/README.md +13 -0
- package/blocks/workflows/block.json +28 -9
- package/blocks/workflows/components/run-history.tsx +31 -9
- package/blocks/workflows/components/trigger-config.tsx +3 -1
- package/blocks/workflows/components/workflow-editor.tsx +4 -2
- package/blocks/workflows/components/workflow-list.tsx +13 -6
- package/blocks/workflows/components/workflows-screen.tsx +193 -0
- package/dist/cli.js +3109 -1977
- package/dist/errors.d.mts +34 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.js +46 -0
- package/dist/errors.js.map +1 -0
- package/dist/errors.mjs +21 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.browser.js +11 -5
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +11 -5
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +164 -25
- package/dist/index.d.ts +164 -25
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -5
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +10 -4
- package/dist/react.browser.js.map +1 -1
- package/dist/react.browser.mjs +10 -4
- package/dist/react.browser.mjs.map +1 -1
- package/dist/react.d.mts +142 -5
- package/dist/react.d.ts +142 -5
- package/dist/react.js +10 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +10 -4
- package/dist/react.mjs.map +1 -1
- package/dist/types/blocks/add.d.ts +11 -4
- package/dist/types/blocks/brand.d.ts +122 -0
- package/dist/types/blocks/compose.d.ts +53 -0
- package/dist/types/blocks/create.d.ts +27 -20
- package/dist/types/blocks/doctor.d.ts +6 -1
- package/dist/types/blocks/flows.d.ts +7 -1
- package/dist/types/blocks/navfile.d.ts +35 -0
- package/dist/types/blocks/orgBrand.d.ts +34 -0
- package/dist/types/blocks/paths.d.ts +4 -0
- package/dist/types/blocks/prefixes.d.ts +37 -0
- package/dist/types/blocks/registry.d.ts +17 -0
- package/dist/types/blocks/screens.d.ts +53 -0
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/errors.d.ts +33 -0
- package/dist/types/http.d.ts +2 -18
- package/dist/types/version.d.ts +2 -2
- package/docs/agents.md +15 -4
- package/docs/api/README.md +2 -2
- package/docs/api/by-path.md +1 -0
- package/docs/api/desk.d.ts +98 -2
- package/docs/api/desk.md +123 -1
- package/docs/api/inbox.d.ts +10 -2
- package/docs/api/inbox.md +10 -2
- package/docs/api/seo.d.ts +19 -1
- package/docs/api/seo.md +24 -1
- package/package.json +16 -2
- package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
- package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
- package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
- package/stacks/6sense.json +55 -17
- package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
- package/stacks/apollo/pages/companies.tsx.tmpl +33 -0
- package/stacks/apollo/pages/contacts.tsx.tmpl +51 -0
- package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
- package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
- package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
- package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
- package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
- package/stacks/apollo.json +147 -37
- package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
- package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
- package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
- package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
- package/stacks/calendly.json +64 -0
- package/stacks/clay/pages/companies.tsx.tmpl +31 -0
- package/stacks/clay/pages/contacts.tsx.tmpl +31 -0
- package/stacks/clay/pages/lists.tsx.tmpl +12 -0
- package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
- package/stacks/clay.json +73 -27
- package/stacks/gong/pages/calls.tsx.tmpl +23 -0
- package/stacks/gong/pages/companies.tsx.tmpl +22 -0
- package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
- package/stacks/gong/pages/lists.tsx.tmpl +12 -0
- package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
- package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
- package/stacks/gong.json +98 -35
- package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
- package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
- package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
- package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
- package/stacks/hootsuite.json +104 -0
- package/stacks/hubspot/pages/companies.tsx.tmpl +26 -0
- package/stacks/hubspot/pages/contacts.tsx.tmpl +45 -0
- package/stacks/hubspot/pages/deals.tsx.tmpl +20 -0
- package/stacks/hubspot/pages/inbox.tsx.tmpl +103 -0
- package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
- package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
- package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
- package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
- package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
- package/stacks/hubspot.json +181 -64
- package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
- package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
- package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
- package/stacks/intercom.json +52 -20
- package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
- package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
- package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
- package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
- package/stacks/mailchimp.json +68 -34
- package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
- package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
- package/stacks/outreach/pages/contacts.tsx.tmpl +46 -0
- package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
- package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
- package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
- package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
- package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
- package/stacks/outreach.json +106 -25
- package/stacks/pipedrive/pages/companies.tsx.tmpl +23 -0
- package/stacks/pipedrive/pages/contacts.tsx.tmpl +27 -0
- package/stacks/pipedrive/pages/deals.tsx.tmpl +20 -0
- package/stacks/pipedrive/pages/inbox.tsx.tmpl +103 -0
- package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
- package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
- package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
- package/stacks/pipedrive.json +117 -0
- package/stacks/salesforce/pages/companies.tsx.tmpl +26 -0
- package/stacks/salesforce/pages/contacts.tsx.tmpl +32 -0
- package/stacks/salesforce/pages/deals.tsx.tmpl +20 -0
- package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
- package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
- package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
- package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
- package/stacks/salesforce.json +85 -31
- package/stacks/zoominfo/pages/companies.tsx.tmpl +31 -0
- package/stacks/zoominfo/pages/contacts.tsx.tmpl +31 -0
- package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
- package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
- package/stacks/zoominfo.json +115 -0
- package/styles/styles.css +1 -1
- package/templates/next-app/README.md.tmpl +40 -9
- package/templates/next-app/app/globals.css.tmpl +349 -64
- package/templates/next-app/app/layout.tsx.tmpl +7 -2
- package/templates/next-app/components/app-shell.tsx.tmpl +104 -88
- package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
- package/templates/next-app/dot-gitignore.tmpl +2 -0
- package/templates/next-app/package.json.tmpl +1 -1
package/blocks/pages/block.json
CHANGED
|
@@ -14,43 +14,67 @@
|
|
|
14
14
|
"foundation"
|
|
15
15
|
],
|
|
16
16
|
"operations": [
|
|
17
|
-
"
|
|
17
|
+
"apply_landing_page_review_findings_landing_pages__landing_page_id__ai_review_apply_post",
|
|
18
|
+
"clear_landing_page_consent_override_landing_pages__landing_page_id__tracking_consent_delete",
|
|
19
|
+
"clone_html_to_landing_page_landing_pages_clone_html_post",
|
|
20
|
+
"clone_url_to_landing_page_landing_pages_clone_url_post",
|
|
21
|
+
"create_content_page_content_pages_post",
|
|
18
22
|
"create_landing_page_landing_pages_post",
|
|
19
|
-
"publish_landing_page_landing_pages__landing_page_id__publish_post",
|
|
20
|
-
"unpublish_landing_page_landing_pages__landing_page_id__unpublish_post",
|
|
21
|
-
"delete_landing_page_landing_pages__landing_page_id__delete",
|
|
22
|
-
"get_landing_page_landing_pages__landing_page_id__get",
|
|
23
|
-
"update_landing_page_landing_pages__landing_page_id__patch",
|
|
24
|
-
"update_landing_page_section_landing_pages__landing_page_id__sections__section_idx__patch",
|
|
25
23
|
"create_landing_page_preview_link_landing_pages__landing_page_id__preview_get",
|
|
26
|
-
"list_landing_page_versions_landing_pages__landing_page_id__versions_get",
|
|
27
|
-
"restore_landing_page_version_landing_pages__landing_page_id__versions__version_num__restore_post",
|
|
28
|
-
"list_content_pages_content_pages_get",
|
|
29
|
-
"create_content_page_content_pages_post",
|
|
30
|
-
"get_content_page_content_pages__page_id__get",
|
|
31
|
-
"update_content_page_content_pages__page_id__patch",
|
|
32
|
-
"enrich_content_page_content_pages__page_id__enrich_seo_post",
|
|
33
24
|
"delete_content_page_content_pages__page_id__delete",
|
|
25
|
+
"delete_landing_page_landing_pages__landing_page_id__delete",
|
|
26
|
+
"edit_landing_page_with_chat_landing_pages__landing_page_id__edit_post",
|
|
27
|
+
"enrich_content_page_content_pages__page_id__enrich_seo_post",
|
|
28
|
+
"export_landing_page_landing_pages__landing_page_id__export_get",
|
|
29
|
+
"fix_landing_page_finding_with_ai_landing_pages__landing_page_id__ai_review_fix_with_ai_post",
|
|
30
|
+
"generate_landing_page_seo_metadata_landing_pages__landing_page_id__generate_seo_metadata_post",
|
|
34
31
|
"get_brand_kit_overview_brand_kit_get",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
32
|
+
"get_content_page_content_pages__page_id__get",
|
|
33
|
+
"get_landing_page_landing_pages__landing_page_id__get",
|
|
34
|
+
"get_landing_page_reporting_overview_landing_pages_reporting_overview_get",
|
|
35
|
+
"get_landing_page_status_landing_pages__landing_page_id__status_get",
|
|
36
|
+
"get_landing_page_tracking_landing_pages__landing_page_id__tracking_get",
|
|
37
37
|
"get_layout_selections_brand_kit_layout_selections_get",
|
|
38
|
-
"save_layout_selections_brand_kit_layout_selections_put",
|
|
39
|
-
"list_seo_audits_seo_audits_get",
|
|
40
|
-
"start_seo_audit_seo_audits_post",
|
|
41
|
-
"get_seo_audit_seo_audits__audit_id__get",
|
|
42
38
|
"get_seo_audit_issues_seo_audits__audit_id__issues_get",
|
|
39
|
+
"get_seo_audit_seo_audits__audit_id__get",
|
|
40
|
+
"list_archetypes_brand_kit_archetypes_get",
|
|
41
|
+
"list_content_pages_content_pages_get",
|
|
42
|
+
"list_landing_page_bindings_landing_pages__landing_page_id__bindings_get",
|
|
43
|
+
"list_landing_page_domains_landing_pages_domains_get",
|
|
44
|
+
"list_landing_page_reporting_companies_landing_pages_reporting_companies_get",
|
|
45
|
+
"list_landing_page_reporting_people_landing_pages_reporting_people_get",
|
|
46
|
+
"list_landing_page_versions_landing_pages__landing_page_id__versions_get",
|
|
47
|
+
"list_landing_pages_landing_pages_get",
|
|
48
|
+
"list_seo_audits_seo_audits_get",
|
|
43
49
|
"list_seo_tracked_keywords_seo_rank_tracking_get",
|
|
50
|
+
"provision_landing_page_tracking_landing_pages__landing_page_id__tracking_provision_post",
|
|
51
|
+
"publish_landing_page_landing_pages__landing_page_id__publish_post",
|
|
52
|
+
"remove_landing_page_binding_landing_pages__landing_page_id__bindings_delete",
|
|
53
|
+
"remove_seo_tracked_keywords_seo_rank_tracking_keywords_remove_post",
|
|
54
|
+
"restore_landing_page_version_landing_pages__landing_page_id__versions__version_num__restore_post",
|
|
55
|
+
"review_landing_page_landing_pages__landing_page_id__ai_review_post",
|
|
56
|
+
"save_layout_selections_brand_kit_layout_selections_put",
|
|
44
57
|
"save_seo_tracked_keywords_seo_rank_tracking_keywords_post",
|
|
45
|
-
"
|
|
58
|
+
"start_seo_audit_seo_audits_post",
|
|
59
|
+
"trigger_brand_extraction_brand_kit_extract_post",
|
|
60
|
+
"unpublish_landing_page_landing_pages__landing_page_id__unpublish_post",
|
|
61
|
+
"update_content_page_content_pages__page_id__patch",
|
|
62
|
+
"update_landing_page_landing_pages__landing_page_id__patch",
|
|
63
|
+
"update_landing_page_section_landing_pages__landing_page_id__sections__section_idx__patch",
|
|
64
|
+
"update_landing_page_tracking_landing_pages__landing_page_id__tracking_patch"
|
|
46
65
|
],
|
|
47
66
|
"components": [
|
|
48
67
|
{
|
|
49
68
|
"name": "PagesScreen",
|
|
50
69
|
"file": "components/pages-screen.tsx",
|
|
51
70
|
"purpose": "Finished marketing-site workspace: landing pages with beside-the-list editor, content library, brand kit and SEO tabs.",
|
|
52
|
-
"operations": [
|
|
53
|
-
|
|
71
|
+
"operations": [
|
|
72
|
+
"get_landing_page_landing_pages__landing_page_id__get",
|
|
73
|
+
"list_landing_pages_landing_pages_get"
|
|
74
|
+
],
|
|
75
|
+
"tiers": [
|
|
76
|
+
"read"
|
|
77
|
+
]
|
|
54
78
|
},
|
|
55
79
|
{
|
|
56
80
|
"name": "PageList",
|
|
@@ -143,6 +167,99 @@
|
|
|
143
167
|
"write",
|
|
144
168
|
"billable"
|
|
145
169
|
]
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"name": "MoreRows",
|
|
173
|
+
"file": "components/more-rows.tsx",
|
|
174
|
+
"purpose": "Footer for paged lists: how many rows are shown and a Load more button.",
|
|
175
|
+
"operations": [],
|
|
176
|
+
"tiers": []
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"name": "PageAiTools",
|
|
180
|
+
"file": "components/page-ai-tools.tsx",
|
|
181
|
+
"purpose": "AI help for a page: edit by instruction, review and apply fixes, write search metadata (each AI pass confirmed).",
|
|
182
|
+
"operations": [
|
|
183
|
+
"edit_landing_page_with_chat_landing_pages__landing_page_id__edit_post",
|
|
184
|
+
"apply_landing_page_review_findings_landing_pages__landing_page_id__ai_review_apply_post",
|
|
185
|
+
"review_landing_page_landing_pages__landing_page_id__ai_review_post",
|
|
186
|
+
"fix_landing_page_finding_with_ai_landing_pages__landing_page_id__ai_review_fix_with_ai_post",
|
|
187
|
+
"update_landing_page_landing_pages__landing_page_id__patch",
|
|
188
|
+
"generate_landing_page_seo_metadata_landing_pages__landing_page_id__generate_seo_metadata_post"
|
|
189
|
+
],
|
|
190
|
+
"tiers": [
|
|
191
|
+
"write",
|
|
192
|
+
"billable"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"name": "PageAnalytics",
|
|
197
|
+
"file": "components/page-analytics.tsx",
|
|
198
|
+
"purpose": "Landing page performance: visits, identified people and companies, form submissions and sources, for all pages or one.",
|
|
199
|
+
"operations": [
|
|
200
|
+
"list_landing_page_reporting_people_landing_pages_reporting_people_get",
|
|
201
|
+
"list_landing_page_reporting_companies_landing_pages_reporting_companies_get",
|
|
202
|
+
"get_landing_page_reporting_overview_landing_pages_reporting_overview_get"
|
|
203
|
+
],
|
|
204
|
+
"tiers": [
|
|
205
|
+
"read"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "PageClone",
|
|
210
|
+
"file": "components/page-clone.tsx",
|
|
211
|
+
"purpose": "Rebuild a URL or pasted HTML as an editable landing page (confirmed AI spend).",
|
|
212
|
+
"operations": [
|
|
213
|
+
"clone_url_to_landing_page_landing_pages_clone_url_post",
|
|
214
|
+
"clone_html_to_landing_page_landing_pages_clone_html_post"
|
|
215
|
+
],
|
|
216
|
+
"tiers": [
|
|
217
|
+
"billable"
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "PageDomains",
|
|
222
|
+
"file": "components/page-domains.tsx",
|
|
223
|
+
"purpose": "Where one page is live: its URLs, publish to a connected domain, take a URL down, download the HTML.",
|
|
224
|
+
"operations": [
|
|
225
|
+
"list_landing_page_bindings_landing_pages__landing_page_id__bindings_get",
|
|
226
|
+
"list_landing_page_domains_landing_pages_domains_get",
|
|
227
|
+
"export_landing_page_landing_pages__landing_page_id__export_get",
|
|
228
|
+
"publish_landing_page_landing_pages__landing_page_id__publish_post",
|
|
229
|
+
"remove_landing_page_binding_landing_pages__landing_page_id__bindings_delete"
|
|
230
|
+
],
|
|
231
|
+
"tiers": [
|
|
232
|
+
"read",
|
|
233
|
+
"external",
|
|
234
|
+
"destructive"
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"name": "PageGenerationStatus",
|
|
239
|
+
"file": "components/page-generation-status.tsx",
|
|
240
|
+
"purpose": "Progress of an AI-built page while it is generating.",
|
|
241
|
+
"operations": [
|
|
242
|
+
"get_landing_page_status_landing_pages__landing_page_id__status_get"
|
|
243
|
+
],
|
|
244
|
+
"tiers": [
|
|
245
|
+
"read"
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "PageTracking",
|
|
250
|
+
"file": "components/page-tracking.tsx",
|
|
251
|
+
"purpose": "How one page is measured: tracking mode, form label, chat widget and conversion goal; own stream and consent reset.",
|
|
252
|
+
"operations": [
|
|
253
|
+
"update_landing_page_tracking_landing_pages__landing_page_id__tracking_patch",
|
|
254
|
+
"provision_landing_page_tracking_landing_pages__landing_page_id__tracking_provision_post",
|
|
255
|
+
"clear_landing_page_consent_override_landing_pages__landing_page_id__tracking_consent_delete",
|
|
256
|
+
"get_landing_page_tracking_landing_pages__landing_page_id__tracking_get"
|
|
257
|
+
],
|
|
258
|
+
"tiers": [
|
|
259
|
+
"read",
|
|
260
|
+
"write",
|
|
261
|
+
"destructive"
|
|
262
|
+
]
|
|
146
263
|
}
|
|
147
264
|
],
|
|
148
265
|
"hooks": [],
|
|
@@ -99,7 +99,9 @@ function Layouts() {
|
|
|
99
99
|
const picked = edit ?? saved;
|
|
100
100
|
const save = useG8Mutation(() => g8Put("/brand-kit/layout-selections", { archetype_ids: picked }), { onSuccess: () => { setEdit(null); void selections.refetch(); } });
|
|
101
101
|
const rows = rowsOf<Rec>(archetypes.data?.data);
|
|
102
|
-
|
|
102
|
+
// The saved selection must be known before anything is ticked or saved: ticking first would replace it with just that box.
|
|
103
|
+
const savedKnown = selections.data !== undefined && !selections.error;
|
|
104
|
+
const dirty = savedKnown && edit !== null && JSON.stringify([...picked].sort()) !== JSON.stringify([...saved].sort());
|
|
103
105
|
const toggle = (id: string) => setEdit(picked.includes(id) ? picked.filter((p) => p !== id) : [...picked, id]);
|
|
104
106
|
return (
|
|
105
107
|
<Card>
|
|
@@ -107,13 +109,14 @@ function Layouts() {
|
|
|
107
109
|
<CardContent className="space-y-3">
|
|
108
110
|
<p className="text-muted-foreground text-sm">New landing pages are built from the layouts you tick.</p>
|
|
109
111
|
{archetypes.error ? <ApiError error={archetypes.error} onRetry={() => void archetypes.refetch()} /> : null}
|
|
112
|
+
{selections.error ? <ApiError error={selections.error} title="Could not load your saved layouts" onRetry={() => void selections.refetch()} /> : null}
|
|
110
113
|
{archetypes.loading && !archetypes.data ? <Skeleton className="h-24 w-full" /> : null}
|
|
111
114
|
<div className="grid gap-2 sm:grid-cols-2">
|
|
112
115
|
{rows.map((a) => {
|
|
113
116
|
const id = text(a.id);
|
|
114
117
|
return (
|
|
115
118
|
<label key={id} className="flex cursor-pointer items-start gap-2 rounded-md border p-3 text-sm">
|
|
116
|
-
<input type="checkbox" checked={picked.includes(id)} onChange={() => toggle(id)} aria-label={text(a.name)} />
|
|
119
|
+
<input type="checkbox" checked={picked.includes(id)} disabled={!savedKnown} onChange={() => toggle(id)} aria-label={text(a.name)} />
|
|
117
120
|
<span><span className="font-medium">{text(a.name)}</span><br /><span className="text-muted-foreground">{text(a.description)}</span></span>
|
|
118
121
|
</label>
|
|
119
122
|
);
|
|
@@ -25,6 +25,7 @@ import { EmptyState } from "@/g8/components/empty-state";
|
|
|
25
25
|
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
26
26
|
import { asRec, num, rowsOf, shortDate, text } from "../lib/studio";
|
|
27
27
|
import type { ContentPageRow, Rec } from "../lib/studio";
|
|
28
|
+
import { plural } from "@/g8/lib/plural";
|
|
28
29
|
|
|
29
30
|
export interface ContentLibraryProps {
|
|
30
31
|
pageSize?: number;
|
|
@@ -70,7 +71,10 @@ function EditSheet({ pageId, onClose, onSaved, onAction }: { pageId: string; onC
|
|
|
70
71
|
useEffect(() => {
|
|
71
72
|
setForm({ title: text(d.title), primary_keyword: text(d.primary_keyword), content_status: text(d.content_status, "idea"), body_content: text(d.body_content) });
|
|
72
73
|
}, [detail.data]);
|
|
73
|
-
|
|
74
|
+
// Only the fields the person changed are sent, so nothing they did not touch can be blanked.
|
|
75
|
+
const original = { title: text(d.title), primary_keyword: text(d.primary_keyword), content_status: text(d.content_status, "idea"), body_content: text(d.body_content) };
|
|
76
|
+
const changed = Object.fromEntries(Object.entries(form).filter(([k, v]) => v !== original[k as keyof typeof original]));
|
|
77
|
+
const save = useG8Mutation(() => g8Patch(`/content/pages/${encodeURIComponent(pageId)}`, changed), { onSuccess: () => { onSaved(); onClose(); } });
|
|
74
78
|
return (
|
|
75
79
|
<Sheet open onOpenChange={(open) => { if (!open) onClose(); }}>
|
|
76
80
|
<SheetContent className="w-full overflow-y-auto sm:max-w-xl">
|
|
@@ -80,7 +84,8 @@ function EditSheet({ pageId, onClose, onSaved, onAction }: { pageId: string; onC
|
|
|
80
84
|
</SheetHeader>
|
|
81
85
|
<div className="space-y-3 px-4 pb-4">
|
|
82
86
|
{detail.error ? <ApiError error={detail.error} onRetry={() => void detail.refetch()} /> : null}
|
|
83
|
-
{
|
|
87
|
+
{/* The form (and Save) exist only once the page has loaded: after a failed load there is nothing to edit over. */}
|
|
88
|
+
{!detail.data ? (detail.error ? null : <Skeleton className="h-32 w-full" />) : (
|
|
84
89
|
<>
|
|
85
90
|
<dl className="grid grid-cols-2 gap-2 text-sm">
|
|
86
91
|
{contentFacts(d).map(([k, v]) => (
|
|
@@ -96,7 +101,7 @@ function EditSheet({ pageId, onClose, onSaved, onAction }: { pageId: string; onC
|
|
|
96
101
|
<div className="space-y-1"><Label htmlFor="cl-status">Status</Label><Select id="cl-status" value={form.content_status} options={STATUSES.slice(1)} onChange={(v) => setForm({ ...form, content_status: v })} /></div>
|
|
97
102
|
<div className="space-y-1"><Label htmlFor="cl-body">Body</Label><Textarea id="cl-body" rows={10} value={form.body_content} onChange={(e) => setForm({ ...form, body_content: e.target.value })} /></div>
|
|
98
103
|
{save.error ? <ApiError error={save.error} title="Could not save" /> : null}
|
|
99
|
-
<Button loading={save.loading} onClick={() => void save.mutate(undefined)}>Save</Button>
|
|
104
|
+
<Button loading={save.loading} disabled={Object.keys(changed).length === 0} onClick={() => void save.mutate(undefined)}>Save</Button>
|
|
100
105
|
</>
|
|
101
106
|
)}
|
|
102
107
|
</div>
|
|
@@ -173,7 +178,7 @@ export function ContentLibrary({ pageSize = 25, className, selectedId, onSelecte
|
|
|
173
178
|
</Table>
|
|
174
179
|
)}
|
|
175
180
|
<div className="mt-3 flex items-center justify-between text-sm">
|
|
176
|
-
<span className="text-muted-foreground">{total
|
|
181
|
+
<span className="text-muted-foreground">{plural(total, "item")}</span>
|
|
177
182
|
<div className="flex gap-2">
|
|
178
183
|
<Button size="sm" variant="outline" disabled={offset === 0} onClick={() => setOffset(Math.max(0, offset - pageSize))}>Previous</Button>
|
|
179
184
|
<Button size="sm" variant="outline" disabled={offset + pageSize >= total} onClick={() => setOffset(offset + pageSize)}>Next</Button>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MoreRows: the honest end of a list that the API pages. Shows how many rows are on screen and, while the API has
|
|
5
|
+
* another page, a "Load more" button. A list never ends silently: either every row is reachable or this says there are more.
|
|
6
|
+
* (Same contract as the foundation's LoadMore; kept local so the pages block does not wait on it.)
|
|
7
|
+
*
|
|
8
|
+
* SDK operations: none.
|
|
9
|
+
*/
|
|
10
|
+
import { Button } from "@graph8/sdk/ui";
|
|
11
|
+
|
|
12
|
+
export interface MoreRowsProps {
|
|
13
|
+
/** Rows on screen. */
|
|
14
|
+
shown: number;
|
|
15
|
+
/** What the rows are, plural: "people". */
|
|
16
|
+
noun: string;
|
|
17
|
+
hasMore: boolean;
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
onMore: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function MoreRows({ shown, noun, hasMore, loading, onMore }: MoreRowsProps) {
|
|
23
|
+
if (!hasMore) return null;
|
|
24
|
+
return (
|
|
25
|
+
<div className="text-muted-foreground flex items-center justify-between gap-3 py-2 text-sm">
|
|
26
|
+
<span role="status">Showing {shown.toLocaleString()} {noun}</span>
|
|
27
|
+
<Button type="button" variant="outline" size="sm" loading={loading} onClick={onMore}>Load more</Button>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PageAiTools: AI help for one landing page. Describe a change in plain words and the page is edited; run a review that
|
|
5
|
+
* lists what could be better and apply the exact fixes for free; write search title, description and keywords.
|
|
6
|
+
*
|
|
7
|
+
* SDK operations, in the order the screen uses them:
|
|
8
|
+
* 1. editLandingPageWithChat POST /landing-pages/{id}/edit { instruction, section_idx? } (BILLABLE, confirmed; changes the draft only)
|
|
9
|
+
* 2. reviewLandingPage POST /landing-pages/{id}/ai-review { deep } (BILLABLE, confirmed; changes nothing)
|
|
10
|
+
* 3. applyLandingPageReviewFindings POST /landing-pages/{id}/ai-review/apply { finding_ids } (write, free, no model runs)
|
|
11
|
+
* 4. fixLandingPageFindingWithAi POST /landing-pages/{id}/ai-review/fix-with-ai { finding } (BILLABLE per finding, confirmed)
|
|
12
|
+
* 5. generateLandingPageSeoMetadata POST /landing-pages/{id}/generate-seo-metadata (BILLABLE, confirmed)
|
|
13
|
+
* 6. updateLandingPage PATCH /landing-pages/{id} { description } (write; "Use as description")
|
|
14
|
+
*
|
|
15
|
+
* The API quotes no price in advance and the balance is not checked here, so every dialog says what runs and how many
|
|
16
|
+
* AI passes it is (a "quick check" runs no model: 0 credits). Edits change the draft; the live page changes only when
|
|
17
|
+
* it is published again. Nothing here publishes.
|
|
18
|
+
*/
|
|
19
|
+
import { useState } from "react";
|
|
20
|
+
import { Button, Label, Textarea } from "@graph8/sdk/ui";
|
|
21
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
22
|
+
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
23
|
+
import { useConfirmAction } from "@/g8/components/confirmed-action";
|
|
24
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
25
|
+
import { KeyValueList } from "@/g8/components/screen/key-value-list";
|
|
26
|
+
import { SectionCard } from "@/g8/components/screen/section-card";
|
|
27
|
+
import { StatusPill } from "@/g8/components/screen/status-pill";
|
|
28
|
+
import type { Tone } from "@/g8/components/screen/status-pill";
|
|
29
|
+
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
30
|
+
import { g8Patch, g8Post } from "@/g8/lib/g8-client";
|
|
31
|
+
import { asRec, num, rowsOf, text } from "../lib/studio";
|
|
32
|
+
import type { Rec } from "../lib/studio";
|
|
33
|
+
|
|
34
|
+
export interface PageAiToolsProps {
|
|
35
|
+
pageId: string;
|
|
36
|
+
/** Called after the page draft changed (an edit, applied fixes, a new description). */
|
|
37
|
+
onChanged?: () => void;
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const SEVERITY: Record<string, Tone> = { error: "negative", critical: "negative", high: "negative", warning: "warning", medium: "warning", info: "neutral", low: "neutral" };
|
|
42
|
+
|
|
43
|
+
/** Findings the review returned. The id is required: a finding without one cannot be applied. */
|
|
44
|
+
export function findingsOf(data: unknown): Rec[] {
|
|
45
|
+
return rowsOf<Rec>(data, "findings").filter((f) => text(f.id) !== "");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function ChatEdit({ pageId, onChanged }: PageAiToolsProps) {
|
|
49
|
+
const { ask, dialog, failure } = useConfirmAction();
|
|
50
|
+
const [instruction, setInstruction] = useState("");
|
|
51
|
+
const [section, setSection] = useState("");
|
|
52
|
+
const [done, setDone] = useState("");
|
|
53
|
+
const sectionIdx = section.trim() === "" ? undefined : Number(section) - 1;
|
|
54
|
+
const sectionOk = sectionIdx === undefined || (Number.isInteger(sectionIdx) && sectionIdx >= 0);
|
|
55
|
+
const go = (): void => {
|
|
56
|
+
const body = { instruction: instruction.trim(), ...(sectionIdx === undefined ? {} : { section_idx: sectionIdx }) };
|
|
57
|
+
ask({
|
|
58
|
+
tier: "billable",
|
|
59
|
+
title: sectionIdx === undefined ? "Edit the whole page with AI" : `Edit section ${sectionIdx + 1} with AI`,
|
|
60
|
+
confirmLabel: "Edit with AI",
|
|
61
|
+
description: `One AI pass applies your instruction to the draft. Uses credits; the API gives no price in advance and your balance is not checked here. The live page does not change until you publish.`,
|
|
62
|
+
run: async () => { await g8Post(`/landing-pages/${encodeURIComponent(pageId)}/edit`, body); setDone("Edit applied to the draft."); setInstruction(""); },
|
|
63
|
+
onDone: () => onChanged?.(),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
return (
|
|
67
|
+
<SectionCard title="Edit with AI" description="Say what to change, for example: make the headline shorter and add a second call to action.">
|
|
68
|
+
<form className="grid gap-3" onSubmit={(e) => { e.preventDefault(); if (instruction.trim() && sectionOk) go(); }}>
|
|
69
|
+
<div>
|
|
70
|
+
<Label htmlFor="ai-instruction" className="mb-1">What should change</Label>
|
|
71
|
+
<Textarea id="ai-instruction" rows={3} value={instruction} onChange={(e) => { setInstruction(e.target.value); setDone(""); }} />
|
|
72
|
+
</div>
|
|
73
|
+
<div className="max-w-40">
|
|
74
|
+
<Label htmlFor="ai-section" className="mb-1">Section number (optional)</Label>
|
|
75
|
+
<input id="ai-section" inputMode="numeric" className="border-input bg-background h-9 w-full rounded-md border px-2 text-sm" value={section} onChange={(e) => setSection(e.target.value.replace(/\D/g, ""))} />
|
|
76
|
+
</div>
|
|
77
|
+
<div><Button type="submit" disabled={!instruction.trim() || !sectionOk}>Edit with AI</Button></div>
|
|
78
|
+
</form>
|
|
79
|
+
{done ? <p role="status" className="mt-2 text-sm">{done}</p> : null}
|
|
80
|
+
{failure ? <ApiError error={failure} title="The edit did not work" /> : null}
|
|
81
|
+
<ConfirmSpendDialog {...dialog} />
|
|
82
|
+
</SectionCard>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function Review({ pageId, onChanged }: PageAiToolsProps) {
|
|
87
|
+
const { ask, dialog, failure } = useConfirmAction();
|
|
88
|
+
const base = `/landing-pages/${encodeURIComponent(pageId)}`;
|
|
89
|
+
const [findings, setFindings] = useState<Rec[] | null>(null);
|
|
90
|
+
const [picked, setPicked] = useState<string[]>([]);
|
|
91
|
+
const [note, setNote] = useState("");
|
|
92
|
+
const apply = useG8Mutation(() => g8Post(`${base}/ai-review/apply`, { finding_ids: picked }), {
|
|
93
|
+
onSuccess: (res) => {
|
|
94
|
+
// The API answers with the findings that remain (id, title, severity only): keep our full copies of those.
|
|
95
|
+
const d = asRec(res.data);
|
|
96
|
+
const applied = num(d.applied) ?? 0;
|
|
97
|
+
// remaining_findings comes from a rule-only re-review, so it never lists AI findings: drop only what could be applied (had an exact fix).
|
|
98
|
+
const done = new Set((findings ?? []).filter((f) => picked.includes(text(f.id)) && f.suggested_fix).map((f) => text(f.id)));
|
|
99
|
+
setFindings((cur) => (cur ?? []).filter((f) => !done.has(text(f.id))));
|
|
100
|
+
setNote(applied === picked.length ? `Applied ${applied} fix${applied === 1 ? "" : "es"}.` : `Applied ${applied} of ${picked.length} selected. The rest have no exact fix: use Fix with AI.`);
|
|
101
|
+
setPicked([]);
|
|
102
|
+
if (applied > 0) onChanged?.();
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const review = (deep: boolean): void => ask({
|
|
106
|
+
tier: "billable",
|
|
107
|
+
title: deep ? "Review this page with AI" : "Quick check of this page",
|
|
108
|
+
confirmLabel: deep ? "Review with AI" : "Run quick check",
|
|
109
|
+
estimatedCredits: deep ? undefined : 0,
|
|
110
|
+
description: deep ? "One AI pass reads the page and lists what to improve. Uses credits; the API gives no price in advance. Nothing on the page changes." : "Runs the built-in checks only, no AI model: 0 credits. Nothing on the page changes.",
|
|
111
|
+
run: async () => { const res = await g8Post(`${base}/ai-review`, { deep }); setFindings(findingsOf(res.data)); setPicked([]); setNote(""); },
|
|
112
|
+
});
|
|
113
|
+
const fix = (f: Rec): void => ask({
|
|
114
|
+
tier: "billable",
|
|
115
|
+
title: `Fix "${text(f.title, "this finding")}" with AI`,
|
|
116
|
+
confirmLabel: "Fix with AI",
|
|
117
|
+
description: "One AI pass rewrites the affected part of the draft. Uses credits for this finding only.",
|
|
118
|
+
run: async () => { await g8Post(`${base}/ai-review/fix-with-ai`, { finding: f }); setFindings((cur) => (cur ?? []).filter((x) => text(x.id) !== text(f.id))); },
|
|
119
|
+
onDone: () => onChanged?.(),
|
|
120
|
+
});
|
|
121
|
+
const toggle = (id: string): void => setPicked((cur) => (cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id]));
|
|
122
|
+
return (
|
|
123
|
+
<SectionCard title="Review" description="Find wording, brand and clarity problems, then apply the exact fixes."
|
|
124
|
+
actions={<div className="flex gap-1"><Button size="sm" variant="outline" onClick={() => review(false)}>Quick check</Button><Button size="sm" onClick={() => review(true)}>Review with AI</Button></div>}>
|
|
125
|
+
{findings === null ? <EmptyState compact title="No review yet" description="Run a review to see what could be better on this page." />
|
|
126
|
+
: findings.length === 0 ? <EmptyState compact title="Nothing to fix" description="The review found no remaining problems." />
|
|
127
|
+
: (
|
|
128
|
+
<div className="grid gap-2">
|
|
129
|
+
<ul className="grid gap-2">
|
|
130
|
+
{findings.map((f) => {
|
|
131
|
+
const id = text(f.id);
|
|
132
|
+
return (
|
|
133
|
+
<li key={id} className="flex items-start gap-3 rounded-md border p-3">
|
|
134
|
+
<input type="checkbox" aria-label={`Select ${text(f.title, id)}`} className="mt-1" checked={picked.includes(id)} onChange={() => toggle(id)} />
|
|
135
|
+
<div className="min-w-0 flex-1">
|
|
136
|
+
<div className="flex items-center gap-2"><span className="text-sm font-medium">{text(f.title, id)}</span><StatusPill tone={SEVERITY[text(f.severity).toLowerCase()] ?? "neutral"}>{text(f.severity, "note")}</StatusPill></div>
|
|
137
|
+
{text(f.description) ? <p className="text-muted-foreground text-xs">{text(f.description)}</p> : null}
|
|
138
|
+
{text(f.suggested_fix) ? <p className="text-xs">Suggested: {typeof f.suggested_fix === "string" ? f.suggested_fix : JSON.stringify(f.suggested_fix)}</p> : null}
|
|
139
|
+
</div>
|
|
140
|
+
<Button size="sm" variant="ghost" aria-label={`Fix ${text(f.title, id)} with AI`} onClick={() => fix(f)}>Fix with AI</Button>
|
|
141
|
+
</li>
|
|
142
|
+
);
|
|
143
|
+
})}
|
|
144
|
+
</ul>
|
|
145
|
+
<div className="flex items-center gap-2">
|
|
146
|
+
<Button size="sm" loading={apply.loading} disabled={picked.length === 0} onClick={() => void apply.mutate(undefined)}>Apply {picked.length || ""} selected (free)</Button>
|
|
147
|
+
<Button size="sm" variant="ghost" onClick={() => setPicked(findings.map((f) => text(f.id)))}>Select all</Button>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
{note ? <p role="status" className="mt-2 text-sm">{note}</p> : null}
|
|
152
|
+
{apply.error ? <ApiError error={apply.error} title="Could not apply the fixes" /> : null}
|
|
153
|
+
{failure ? <ApiError error={failure} title="That did not work" /> : null}
|
|
154
|
+
<ConfirmSpendDialog {...dialog} />
|
|
155
|
+
</SectionCard>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function SeoMetadata({ pageId, onChanged }: PageAiToolsProps) {
|
|
160
|
+
const { ask, dialog, failure } = useConfirmAction();
|
|
161
|
+
const [meta, setMeta] = useState<Rec | null>(null);
|
|
162
|
+
const use = useG8Mutation((description: string) => g8Patch(`/landing-pages/${encodeURIComponent(pageId)}`, { description }), { onSuccess: () => onChanged?.() });
|
|
163
|
+
const scalars = Object.entries(meta ?? {}).filter(([, v]) => typeof v === "string" || typeof v === "number" || (Array.isArray(v) && v.every((x) => typeof x === "string")));
|
|
164
|
+
const description = text(meta?.description) || text(meta?.meta_description);
|
|
165
|
+
return (
|
|
166
|
+
<SectionCard title="Search title and description" description="Write the title, description and keywords search engines show."
|
|
167
|
+
actions={<Button size="sm" onClick={() => ask({
|
|
168
|
+
tier: "billable", title: "Write search metadata with AI", confirmLabel: "Write metadata",
|
|
169
|
+
description: "One AI pass reads the page and proposes a title, description and keywords. Uses credits; the API gives no price in advance. Nothing is saved until you choose to.",
|
|
170
|
+
run: async () => { setMeta(asRec((await g8Post(`/landing-pages/${encodeURIComponent(pageId)}/generate-seo-metadata`)).data)); },
|
|
171
|
+
})}>Write with AI</Button>}>
|
|
172
|
+
{!meta ? <EmptyState compact title="Not generated yet" description="Generate a proposal, read it, then keep what you like." /> : (
|
|
173
|
+
<div className="grid gap-3">
|
|
174
|
+
<KeyValueList columns={1} items={scalars.map(([k, v]) => ({ label: k.replace(/_/g, " "), value: Array.isArray(v) ? v.join(", ") : String(v), wide: true }))} />
|
|
175
|
+
{description ? <div><Button size="sm" variant="outline" loading={use.loading} onClick={() => void use.mutate(description)}>Use as the page description</Button></div> : null}
|
|
176
|
+
{use.data ? <p role="status" className="text-sm">Description saved.</p> : null}
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
{use.error ? <ApiError error={use.error} title="Could not save the description" /> : null}
|
|
180
|
+
{failure ? <ApiError error={failure} title="That did not work" /> : null}
|
|
181
|
+
<ConfirmSpendDialog {...dialog} />
|
|
182
|
+
</SectionCard>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function PageAiTools({ pageId, onChanged, className }: PageAiToolsProps) {
|
|
187
|
+
return (
|
|
188
|
+
<div className={className}>
|
|
189
|
+
<div className="grid gap-4">
|
|
190
|
+
<ChatEdit pageId={pageId} onChanged={onChanged} />
|
|
191
|
+
<Review pageId={pageId} onChanged={onChanged} />
|
|
192
|
+
<SeoMetadata pageId={pageId} onChanged={onChanged} />
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
}
|