@graph8/sdk 0.361.0 → 0.370.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 +14 -10
- package/blocks/_foundation/README.md +172 -1
- package/blocks/_foundation/block.json +234 -13
- package/blocks/_foundation/components/api-error.tsx +71 -25
- package/blocks/_foundation/components/brand-provider.tsx +23 -0
- 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 +39 -7
- package/blocks/_foundation/components/screen/bar-list.tsx +71 -0
- package/blocks/_foundation/components/screen/detail-panel.tsx +114 -0
- package/blocks/_foundation/components/screen/entity-avatar.tsx +56 -0
- package/blocks/_foundation/components/screen/form-sheet.tsx +73 -0
- package/blocks/_foundation/components/screen/index.ts +22 -0
- package/blocks/_foundation/components/screen/key-value-list.tsx +65 -0
- package/blocks/_foundation/components/screen/record-link.tsx +76 -0
- package/blocks/_foundation/components/screen/screen-layout.tsx +124 -0
- package/blocks/_foundation/components/screen/section-card.tsx +47 -0
- package/blocks/_foundation/components/screen/sparkline.tsx +77 -0
- package/blocks/_foundation/components/screen/split-view.tsx +125 -0
- package/blocks/_foundation/components/screen/stat-strip.tsx +121 -0
- package/blocks/_foundation/components/screen/status-pill.tsx +104 -0
- package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
- package/blocks/_foundation/components/screen/time-series-chart.tsx +32 -0
- package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
- package/blocks/_foundation/components/screen/toolbar.tsx +144 -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-paginated.ts +120 -0
- package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
- package/blocks/_foundation/hooks/use-url-state.ts +74 -0
- package/blocks/_foundation/lib/g8-cache.ts +454 -0
- package/blocks/_foundation/lib/g8-client.ts +1 -1
- package/blocks/_foundation/lib/plural.ts +23 -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 +14 -0
- package/blocks/agents/block.json +33 -1
- package/blocks/agents/components/agent-builder.tsx +13 -8
- package/blocks/agents/components/agent-knowledge-panel.tsx +2 -1
- package/blocks/agents/components/agent-run-detail.tsx +102 -0
- package/blocks/agents/components/agent-runs.tsx +16 -7
- package/blocks/agents/components/agents-screen.tsx +184 -0
- package/blocks/analytics/README.md +13 -0
- package/blocks/analytics/block.json +45 -1
- package/blocks/analytics/components/analytics-screen.tsx +209 -0
- package/blocks/analytics/components/leaderboard.tsx +14 -7
- package/blocks/analytics/components/report-builder.tsx +23 -9
- package/blocks/analytics/components/report-detail.tsx +84 -0
- package/blocks/analytics/components/trend-chart.tsx +62 -0
- package/blocks/appointments/README.md +21 -1
- package/blocks/appointments/block.json +43 -2
- package/blocks/appointments/components/availability-editor.tsx +17 -5
- package/blocks/appointments/components/event-type-editor.tsx +2 -1
- package/blocks/appointments/components/event-type-related.tsx +77 -0
- package/blocks/appointments/components/meeting-list.tsx +14 -6
- package/blocks/appointments/components/meetings-screen.tsx +157 -0
- package/blocks/appointments/components/scheduling-screen.tsx +122 -0
- package/blocks/appointments/hooks/use-event-types.ts +17 -0
- package/blocks/block.schema.json +18 -0
- package/blocks/block.types.ts +26 -0
- package/blocks/campaigns/README.md +41 -11
- package/blocks/campaigns/block.json +105 -42
- package/blocks/campaigns/components/campaign-assets.tsx +16 -5
- package/blocks/campaigns/components/campaign-launch-step.tsx +102 -19
- package/blocks/campaigns/components/campaign-list.tsx +7 -3
- package/blocks/campaigns/components/campaign-wizard.tsx +43 -10
- package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
- package/blocks/campaigns/components/icp-detail.tsx +154 -0
- package/blocks/campaigns/components/icp-editor.tsx +15 -15
- package/blocks/campaigns/components/persona-detail.tsx +172 -0
- package/blocks/campaigns/components/persona-editor.tsx +14 -11
- package/blocks/campaigns/lib/campaigns-utils.ts +134 -1
- 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/coach-screen.tsx +222 -0
- package/blocks/copilot/README.md +13 -0
- package/blocks/copilot/block.json +14 -1
- 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 +7 -5
- package/blocks/crm/README.md +28 -0
- package/blocks/crm/block.json +58 -2
- package/blocks/crm/components/companies-screen.tsx +345 -0
- package/blocks/crm/components/company-detail.tsx +8 -6
- package/blocks/crm/components/company-table.tsx +11 -4
- package/blocks/crm/components/contact-detail.tsx +6 -3
- package/blocks/crm/components/contact-table.tsx +10 -3
- package/blocks/crm/components/contacts-screen.tsx +359 -0
- package/blocks/crm/components/dedupe-review.tsx +7 -5
- package/blocks/crm/components/import-wizard.tsx +4 -2
- package/blocks/crm/components/notes-panel.tsx +4 -4
- package/blocks/crm/components/record-form.tsx +5 -3
- package/blocks/crm/components/task-list.tsx +4 -4
- package/blocks/crm/components/tasks-screen.tsx +379 -0
- package/blocks/crm/hooks/use-custom-fields.ts +6 -1
- package/blocks/crm/hooks/use-page-query.ts +7 -1
- package/blocks/crm/lib/crm-types.ts +12 -2
- package/blocks/crm/lib/screen-parts.tsx +105 -0
- package/blocks/deals/README.md +34 -3
- package/blocks/deals/block.json +45 -4
- package/blocks/deals/components/deal-detail.tsx +27 -13
- package/blocks/deals/components/deal-related.tsx +87 -0
- package/blocks/deals/components/deals-screen.tsx +298 -0
- package/blocks/deals/components/pipeline-board.tsx +4 -2
- package/blocks/deals/components/stage-editor.tsx +4 -4
- package/blocks/deals/hooks/use-default-owner.ts +60 -0
- package/blocks/deals/hooks/use-pipeline-deals.ts +5 -1
- package/blocks/deals/hooks/use-pipelines.ts +1 -1
- package/blocks/deals/lib/deals-types.ts +14 -0
- package/blocks/dialer/README.md +34 -2
- package/blocks/dialer/block.json +67 -1
- package/blocks/dialer/components/call-contact-button.tsx +252 -0
- package/blocks/dialer/components/call-log-screen.tsx +205 -0
- package/blocks/dialer/components/call-log.tsx +7 -3
- package/blocks/dialer/components/call-queue.tsx +1 -1
- package/blocks/dialer/components/dialer-screen.tsx +319 -0
- package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
- package/blocks/dialer/components/live-calls-table.tsx +7 -3
- package/blocks/dialer/components/session-start-form.tsx +4 -2
- package/blocks/dialer/components/softphone.tsx +3 -1
- package/blocks/enrichment/README.md +14 -2
- package/blocks/enrichment/block.json +32 -4
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +10 -5
- package/blocks/enrichment/components/email-verify-badge.tsx +28 -8
- 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 +288 -0
- package/blocks/enrichment/components/waterfall-config.tsx +39 -10
- package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
- package/blocks/forms/README.md +12 -0
- package/blocks/forms/block.json +39 -17
- package/blocks/forms/components/form-builder.tsx +3 -1
- package/blocks/forms/components/form-embed.tsx +3 -1
- package/blocks/forms/components/forms-screen.tsx +353 -0
- package/blocks/forms/components/submissions-table.tsx +16 -6
- package/blocks/inbox/README.md +27 -3
- package/blocks/inbox/block.json +14 -2
- package/blocks/inbox/components/inbox-list.tsx +7 -3
- package/blocks/inbox/components/inbox-screen.tsx +294 -27
- package/blocks/inbox/components/reply-composer.tsx +65 -9
- package/blocks/inbox/components/tag-and-assign.tsx +4 -2
- 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 +9 -0
- package/blocks/integrations/block.json +46 -1
- package/blocks/integrations/components/crm-detail.tsx +100 -0
- package/blocks/integrations/components/integration-list.tsx +9 -4
- package/blocks/integrations/components/integrations-screen.tsx +135 -0
- package/blocks/integrations/components/sync-status.tsx +5 -3
- package/blocks/integrations/components/webhook-detail.tsx +160 -0
- package/blocks/integrations/components/webhook-manager.tsx +13 -8
- package/blocks/integrations/lib/integrations-utils.ts +6 -1
- package/blocks/intent/README.md +14 -0
- package/blocks/intent/block.json +27 -1
- package/blocks/intent/components/intent-feed.tsx +5 -2
- package/blocks/intent/components/intent-keyword-manager.tsx +8 -4
- package/blocks/intent/components/intent-screen.tsx +265 -0
- package/blocks/intent/components/keyword-detail.tsx +124 -0
- package/blocks/lifecycle/README.md +16 -0
- package/blocks/lifecycle/block.json +34 -7
- package/blocks/lifecycle/components/account-related.tsx +66 -0
- package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
- package/blocks/lifecycle/components/lifecycle-screen.tsx +149 -0
- package/blocks/lifecycle/lib/lifecycle.ts +1 -1
- package/blocks/lists/README.md +13 -0
- package/blocks/lists/block.json +39 -1
- package/blocks/lists/components/add-to-list-button.tsx +3 -2
- package/blocks/lists/components/list-picker.tsx +5 -1
- package/blocks/lists/components/list-table.tsx +11 -5
- package/blocks/lists/components/lists-screen.tsx +344 -0
- package/blocks/lists/components/suppression-manager.tsx +6 -2
- package/blocks/lists/components/suppressions-screen.tsx +255 -0
- package/blocks/mailboxes/README.md +17 -0
- package/blocks/mailboxes/block.json +65 -17
- package/blocks/mailboxes/components/connect-mailbox.tsx +23 -13
- package/blocks/mailboxes/components/domain-detail.tsx +142 -0
- package/blocks/mailboxes/components/domain-purchase.tsx +6 -3
- package/blocks/mailboxes/components/mailbox-detail.tsx +156 -0
- package/blocks/mailboxes/components/mailboxes-screen.tsx +223 -0
- package/blocks/mailboxes/components/warmup-health.tsx +4 -2
- package/blocks/mailboxes/lib/mailboxes-utils.ts +22 -1
- package/blocks/newsletter/README.md +13 -0
- package/blocks/newsletter/block.json +47 -23
- package/blocks/newsletter/components/issue-scheduler.tsx +2 -1
- package/blocks/newsletter/components/newsletter-screen.tsx +404 -0
- package/blocks/newsletter/components/subscriber-table.tsx +12 -6
- package/blocks/nurture/README.md +12 -0
- package/blocks/nurture/block.json +35 -13
- package/blocks/nurture/components/nurture-builder.tsx +3 -1
- package/blocks/nurture/components/nurture-enroll.tsx +6 -3
- package/blocks/nurture/components/nurture-form.tsx +23 -3
- package/blocks/nurture/components/nurture-screen.tsx +283 -0
- package/blocks/nurture/lib/nurture-types.ts +1 -1
- package/blocks/objects/README.md +17 -0
- package/blocks/objects/block.json +29 -16
- package/blocks/objects/components/object-not-found.tsx +3 -1
- package/blocks/objects/components/object-record-detail.tsx +2 -1
- package/blocks/objects/components/object-schema-editor.tsx +3 -1
- package/blocks/objects/components/object-table.tsx +13 -3
- package/blocks/objects/components/objects-screen.tsx +147 -0
- package/blocks/objects/lib/object-types.ts +1 -1
- package/blocks/pages/README.md +12 -0
- package/blocks/pages/block.json +35 -22
- package/blocks/pages/components/brand-kit-editor.tsx +5 -2
- package/blocks/pages/components/content-library.tsx +46 -11
- package/blocks/pages/components/page-list.tsx +6 -3
- package/blocks/pages/components/pages-screen.tsx +154 -0
- package/blocks/quotes/README.md +17 -0
- package/blocks/quotes/block.json +38 -9
- package/blocks/quotes/components/product-picker.tsx +3 -1
- package/blocks/quotes/components/quote-list.tsx +7 -3
- package/blocks/quotes/components/quote-related.tsx +123 -0
- package/blocks/quotes/components/quotes-screen.tsx +169 -0
- package/blocks/radar/README.md +10 -0
- package/blocks/radar/block.json +41 -1
- package/blocks/radar/components/competitor-detail.tsx +117 -0
- package/blocks/radar/components/competitor-tracker.tsx +3 -1
- 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 +49 -9
- package/blocks/sequencer/block.json +99 -19
- package/blocks/sequencer/components/enroll-dialog.tsx +9 -3
- package/blocks/sequencer/components/sequence-builder.tsx +211 -46
- package/blocks/sequencer/components/sequence-detail.tsx +259 -0
- package/blocks/sequencer/components/sequence-enrollments.tsx +114 -0
- package/blocks/sequencer/components/sequence-list.tsx +33 -8
- package/blocks/sequencer/components/sequence-stats.tsx +3 -2
- package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
- package/blocks/sequencer/components/sequences-screen.tsx +159 -0
- package/blocks/sequencer/components/step-editor.tsx +3 -1
- package/blocks/sequencer/lib/sequencer-utils.ts +128 -5
- 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 +11 -0
- package/blocks/social-ads/block.json +101 -2
- 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 +188 -0
- package/blocks/social-ads/components/audience-sync.tsx +11 -7
- package/blocks/social-ads/components/campaign-detail.tsx +133 -0
- package/blocks/social-ads/components/creative-detail.tsx +150 -0
- package/blocks/social-ads/components/creative-gallery.tsx +15 -5
- package/blocks/social-ads/components/post-composer.tsx +3 -1
- package/blocks/social-ads/components/post-detail.tsx +160 -0
- package/blocks/social-ads/components/social-post-list.tsx +11 -3
- package/blocks/social-ads/components/social-screen.tsx +160 -0
- package/blocks/visitors/README.md +10 -0
- package/blocks/visitors/block.json +25 -1
- package/blocks/visitors/components/snippet-install.tsx +3 -1
- package/blocks/visitors/components/visitor-company-card.tsx +2 -2
- package/blocks/visitors/components/visitor-detail.tsx +105 -0
- package/blocks/visitors/components/visitor-feed.tsx +11 -2
- 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 +7 -4
- package/blocks/webchat/components/conversation-list.tsx +7 -3
- 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 +4 -2
- package/blocks/workbench/components/workbench-grid.tsx +9 -3
- package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
- package/blocks/workbench/components/workbench-screen.tsx +192 -0
- package/blocks/workbench/components/workbench-sources.tsx +194 -0
- 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 +3 -1
- package/blocks/workflows/components/workflow-list.tsx +13 -6
- package/blocks/workflows/components/workflows-screen.tsx +193 -0
- package/dist/cli.js +2785 -1637
- 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 +113 -0
- package/dist/types/blocks/compose.d.ts +53 -0
- package/dist/types/blocks/create.d.ts +25 -8
- package/dist/types/blocks/doctor.d.ts +6 -1
- package/dist/types/blocks/flows.d.ts +20 -0
- package/dist/types/blocks/navfile.d.ts +35 -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 +32 -0
- package/dist/types/blocks/screens.d.ts +53 -0
- package/dist/types/cli.d.ts +2 -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.json +41 -16
- package/stacks/apollo/pages/companies.tsx.tmpl +50 -0
- package/stacks/apollo/pages/contacts.tsx.tmpl +117 -0
- package/stacks/apollo.json +118 -36
- package/stacks/calendly.json +60 -0
- package/stacks/clay/pages/companies.tsx.tmpl +15 -0
- package/stacks/clay/pages/contacts.tsx.tmpl +19 -0
- package/stacks/clay.json +59 -26
- package/stacks/gong.json +81 -35
- package/stacks/hootsuite.json +99 -0
- package/stacks/hubspot/pages/companies.tsx.tmpl +47 -0
- package/stacks/hubspot/pages/contacts.tsx.tmpl +111 -0
- package/stacks/hubspot/pages/deals.tsx.tmpl +11 -0
- package/stacks/hubspot/pages/inbox.tsx.tmpl +92 -0
- package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
- package/stacks/hubspot.json +167 -66
- package/stacks/intercom.json +48 -20
- package/stacks/mailchimp.json +52 -33
- package/stacks/outreach/pages/contacts.tsx.tmpl +85 -0
- package/stacks/outreach.json +88 -25
- package/stacks/pipedrive/pages/companies.tsx.tmpl +47 -0
- package/stacks/pipedrive/pages/contacts.tsx.tmpl +47 -0
- package/stacks/pipedrive/pages/deals.tsx.tmpl +11 -0
- package/stacks/pipedrive/pages/inbox.tsx.tmpl +92 -0
- package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
- package/stacks/pipedrive.json +115 -0
- package/stacks/salesforce/pages/companies.tsx.tmpl +47 -0
- package/stacks/salesforce/pages/contacts.tsx.tmpl +47 -0
- package/stacks/salesforce/pages/deals.tsx.tmpl +11 -0
- package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
- package/stacks/salesforce.json +81 -31
- package/stacks/stack.schema.json +6 -1
- package/stacks/zoominfo/pages/companies.tsx.tmpl +15 -0
- package/stacks/zoominfo/pages/contacts.tsx.tmpl +19 -0
- package/stacks/zoominfo.json +102 -0
- package/styles/styles.css +1 -1
- package/templates/next-app/README.md.tmpl +55 -8
- package/templates/next-app/app/globals.css.tmpl +105 -0
- package/templates/next-app/app/layout.tsx.tmpl +18 -4
- package/templates/next-app/components/app-shell.tsx.tmpl +248 -23
- package/templates/next-app/components/shell/connection-status.tsx.tmpl +109 -0
- package/templates/next-app/components/shell/nav-model.ts.tmpl +65 -0
- package/templates/next-app/components/shell/quick-jump.tsx.tmpl +131 -0
- package/templates/next-app/components/shell/theme-toggle.tsx.tmpl +40 -0
- package/templates/next-app/dot-gitignore.tmpl +2 -0
- package/templates/next-app/lib/nav.ts.tmpl +1 -1
- package/templates/next-app/package.json.tmpl +1 -1
|
@@ -26,8 +26,14 @@ import { EmptyState } from "@/g8/components/empty-state";
|
|
|
26
26
|
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
27
27
|
import { blankConfig, payload, str } from "../lib/graph";
|
|
28
28
|
import type { Rec, WorkflowRow } from "../lib/graph";
|
|
29
|
+
import { rowActivationProps } from "@/g8/hooks/use-row-activation";
|
|
30
|
+
import { RecordLink } from "@/g8/components/screen/record-link";
|
|
31
|
+
import { pluralWord } from "@/g8/lib/plural";
|
|
32
|
+
import { useBrand } from "@/g8/hooks/use-brand";
|
|
29
33
|
|
|
30
34
|
export interface WorkflowListProps {
|
|
35
|
+
/** URL param the host screen reads for this record type. With it the name is a real link (Cmd-click opens it in a new tab); without it a plain button. */
|
|
36
|
+
linkParam?: string;
|
|
31
37
|
/** Open a workflow in your WorkflowEditor route. */
|
|
32
38
|
onOpen?: (actionId: string) => void;
|
|
33
39
|
/** Called with `(actionId, executionId)` after a run starts, so you can show RunHistory. */
|
|
@@ -55,7 +61,8 @@ function planFacts(plan: Rec): { credits?: number; audience?: number; steps: num
|
|
|
55
61
|
};
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
function RunPlan({ actionId, input, setInput }: { actionId: string; input: string; setInput: (v: string) => void }) {
|
|
64
|
+
export function RunPlan({ actionId, input, setInput }: { actionId: string; input: string; setInput: (v: string) => void }) {
|
|
65
|
+
const brand = useBrand();
|
|
59
66
|
const plan = useG8Query((signal) => g8Post(`/workflows/${encodeURIComponent(actionId)}/plan`, {}, { signal }), [actionId]);
|
|
60
67
|
const facts = planFacts(payload(plan.data));
|
|
61
68
|
let valid = true;
|
|
@@ -64,9 +71,9 @@ function RunPlan({ actionId, input, setInput }: { actionId: string; input: strin
|
|
|
64
71
|
<div className="space-y-2 text-left">
|
|
65
72
|
{plan.loading ? <Skeleton className="h-4 w-48" /> : plan.error ? <p className="text-xs">The preview is unavailable; the run still works.</p> : (
|
|
66
73
|
<p className="text-xs" data-testid="run-plan">
|
|
67
|
-
{facts.steps} steps{facts.audience != null ? `, ${facts.audience.toLocaleString()}
|
|
74
|
+
{facts.steps} {pluralWord(facts.steps, "step")}{facts.audience != null ? `, ${facts.audience.toLocaleString()} ${pluralWord(facts.audience, "contact")} in scope` : ""}
|
|
68
75
|
{facts.credits != null ? `, about ${Math.round(facts.credits).toLocaleString()} credits (advisory)` : ""}
|
|
69
|
-
{facts.approval ? ", waits for a human approval" : ""}{facts.external ?
|
|
76
|
+
{facts.approval ? ", waits for a human approval" : ""}{facts.external ? `, contacts people or systems outside ${brand.linkLabel}` : ""}.
|
|
70
77
|
</p>
|
|
71
78
|
)}
|
|
72
79
|
<label className="block text-xs font-medium" htmlFor="run-input">Run input (JSON)</label>
|
|
@@ -86,7 +93,7 @@ function confirmCopy(p: Pending): { tier: "external" | "destructive"; title: str
|
|
|
86
93
|
return { tier: "destructive", title: `Delete "${p.workflow.name}"`, label: "Delete", description: "The workflow and its trigger are removed. Past runs stay in history." };
|
|
87
94
|
}
|
|
88
95
|
|
|
89
|
-
export function WorkflowList({ onOpen, onRunStarted, className }: WorkflowListProps) {
|
|
96
|
+
export function WorkflowList({ linkParam, onOpen, onRunStarted, className }: WorkflowListProps) {
|
|
90
97
|
const list = useG8Query((signal) => g8Get("/workflows", undefined, { signal }), []);
|
|
91
98
|
const [name, setName] = useState("");
|
|
92
99
|
const [pending, setPending] = useState<Pending | null>(null);
|
|
@@ -128,8 +135,8 @@ export function WorkflowList({ onOpen, onRunStarted, className }: WorkflowListPr
|
|
|
128
135
|
<TableHeader><TableRow><TableHead>Name</TableHead><TableHead>Status</TableHead><TableHead>Category</TableHead><TableHead className="text-right">Actions</TableHead></TableRow></TableHeader>
|
|
129
136
|
<TableBody>
|
|
130
137
|
{rows.map((w) => (
|
|
131
|
-
<TableRow key={w.action_id}>
|
|
132
|
-
<TableCell
|
|
138
|
+
<TableRow key={w.action_id} {...(onOpen ? rowActivationProps(() => onOpen(w.action_id)) : {})}>
|
|
139
|
+
<TableCell>{onOpen ? <RecordLink param={linkParam} id={w.action_id} onOpen={() => onOpen(w.action_id)}>{w.name}</RecordLink> : <span className="font-medium">{w.name}</span>}{w.description ? <div className="text-muted-foreground text-xs">{w.description}</div> : null}</TableCell>
|
|
133
140
|
<TableCell>{w.enabled === false ? "Disabled" : "Enabled"}</TableCell>
|
|
134
141
|
<TableCell>{w.category ?? ""}</TableCell>
|
|
135
142
|
<TableCell className="space-x-1 text-right">
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WorkflowsScreen: the finished automations workspace. Two tabs.
|
|
5
|
+
* Workflows: stat strip (workflows, enabled, disabled, failed runs), search and status filter, the list with
|
|
6
|
+
* Run / Enable-Disable / Delete per row, and for the open workflow (deep link ?id=) a detail panel with
|
|
7
|
+
* Steps (WorkflowEditor), Trigger (TriggerConfig) and Runs (RunHistory for this workflow).
|
|
8
|
+
* Runs: every run across all workflows as a list; opening one (deep link ?run=) shows the full run (status, timing,
|
|
9
|
+
* who started it, error, input, output) with pause / resume / stop / restart.
|
|
10
|
+
* "New workflow" asks for a name, creates a manual-trigger workflow and opens it so steps can be added. Starting a
|
|
11
|
+
* run shows the plan preview (steps, audience, advisory credits) and the run input first, inside the confirm.
|
|
12
|
+
*
|
|
13
|
+
* Composes: WorkflowEditor, TriggerConfig, RunHistory, RunPlan (from WorkflowList).
|
|
14
|
+
*
|
|
15
|
+
* SDK operations of its own, in the order used:
|
|
16
|
+
* 1. GET /workflows (workflows.listWorkflows) read `{ actions, total }`
|
|
17
|
+
* 2. GET /workflows/executions?status_filter=failed&limit=1 (workflows.listWorkflowExecutions) read failed-run count
|
|
18
|
+
* 3. POST /workflows (workflows.createWorkflow) external -> ConfirmSpendDialog
|
|
19
|
+
* 4. POST /workflows/{id}/plan, then POST /workflows/{id}/execute { input_data } (plan read; execute external -> ConfirmSpendDialog)
|
|
20
|
+
* 5. PUT /workflows/{id} { enabled } (workflows.updateWorkflow) external -> ConfirmSpendDialog
|
|
21
|
+
* 6. DELETE /workflows/{id} (workflows.deleteWorkflow) destructive -> ConfirmSpendDialog
|
|
22
|
+
* Slot props: renderRowActions, renderDetailActions, extraDetailTabs, toolbarActions.
|
|
23
|
+
*/
|
|
24
|
+
import { useEffect, useState, type ReactNode } from "react";
|
|
25
|
+
import { Plus, Workflow } from "lucide-react";
|
|
26
|
+
import { Button, Input, Skeleton } from "@graph8/sdk/ui";
|
|
27
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
28
|
+
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
29
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
30
|
+
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
31
|
+
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
32
|
+
import { g8Delete, g8Get, g8Post, g8Put } from "@/g8/lib/g8-client";
|
|
33
|
+
import { DetailPanel } from "@/g8/components/screen/detail-panel";
|
|
34
|
+
import { FormSheet } from "@/g8/components/screen/form-sheet";
|
|
35
|
+
import { ScreenLayout } from "@/g8/components/screen/screen-layout";
|
|
36
|
+
import { SplitView } from "@/g8/components/screen/split-view";
|
|
37
|
+
import { StatusPill } from "@/g8/components/screen/status-pill";
|
|
38
|
+
import { Toolbar } from "@/g8/components/screen/toolbar";
|
|
39
|
+
import { useUrlState } from "@/g8/hooks/use-url-state";
|
|
40
|
+
import { blankConfig, payload, str } from "../lib/graph";
|
|
41
|
+
import type { WorkflowRow } from "../lib/graph";
|
|
42
|
+
import { RunDetail, RunHistory } from "./run-history";
|
|
43
|
+
import { RunPlan } from "./workflow-list";
|
|
44
|
+
import { TriggerConfig } from "./trigger-config";
|
|
45
|
+
import { WorkflowEditor } from "./workflow-editor";
|
|
46
|
+
|
|
47
|
+
export interface WorkflowsScreenProps {
|
|
48
|
+
renderRowActions?: (workflow: WorkflowRow) => ReactNode;
|
|
49
|
+
renderDetailActions?: (workflow: WorkflowRow) => ReactNode;
|
|
50
|
+
extraDetailTabs?: Array<{ id: string; label: string; render: (workflow: WorkflowRow) => ReactNode }>;
|
|
51
|
+
toolbarActions?: ReactNode;
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type Pending =
|
|
56
|
+
| { kind: "create"; name: string }
|
|
57
|
+
| { kind: "run"; workflow: WorkflowRow }
|
|
58
|
+
| { kind: "toggle"; workflow: WorkflowRow }
|
|
59
|
+
| { kind: "delete"; workflow: WorkflowRow };
|
|
60
|
+
|
|
61
|
+
const SCREEN_TABS = [{ id: "workflows", label: "Workflows" }, { id: "runs", label: "Runs" }];
|
|
62
|
+
const FILTERS = [{ id: "", label: "All" }, { id: "enabled", label: "Enabled" }, { id: "disabled", label: "Disabled" }];
|
|
63
|
+
const isOn = (w: WorkflowRow) => w.enabled !== false;
|
|
64
|
+
|
|
65
|
+
function copyFor(p: Pending): { tier: "external" | "destructive"; title: string; label: string; description?: string } {
|
|
66
|
+
if (p.kind === "create") return { tier: "external", title: `Create workflow "${p.name || "Untitled workflow"}"`, label: "Create", description: "A manual-trigger workflow with no steps yet. Add steps in the editor." };
|
|
67
|
+
if (p.kind === "run") return { tier: "external", title: `Run "${p.workflow.name}"`, label: "Run now" };
|
|
68
|
+
if (p.kind === "toggle") {
|
|
69
|
+
const on = isOn(p.workflow);
|
|
70
|
+
return { tier: "external", title: `${on ? "Disable" : "Enable"} "${p.workflow.name}"`, label: on ? "Disable" : "Enable", description: on ? "Its trigger stops firing." : "Its trigger starts firing and the workflow can act on real contacts." };
|
|
71
|
+
}
|
|
72
|
+
return { tier: "destructive", title: `Delete "${p.workflow.name}"`, label: "Delete", description: "The workflow and its trigger are removed. Past runs stay in history." };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function WorkflowsScreen({ renderRowActions, renderDetailActions, extraDetailTabs, toolbarActions, className }: WorkflowsScreenProps) {
|
|
76
|
+
const [tab, setTab] = useUrlState("tab", "workflows");
|
|
77
|
+
const [selected, setSelected] = useUrlState("id", "");
|
|
78
|
+
const [search, setSearch] = useState("");
|
|
79
|
+
const [filter, setFilter] = useState("");
|
|
80
|
+
const [newName, setNewName] = useState("");
|
|
81
|
+
const [sheet, setSheet] = useState(false);
|
|
82
|
+
const [pending, setPending] = useState<Pending | null>(null);
|
|
83
|
+
const [input, setInput] = useState("{}");
|
|
84
|
+
const [run, setRun] = useUrlState("run", "");
|
|
85
|
+
const [view, setView] = useUrlState("view", "");
|
|
86
|
+
const closeWorkflow = () => { setSelected(""); setView(""); };
|
|
87
|
+
const closeRun = () => { setRun(""); setView(""); };
|
|
88
|
+
const expand = { expanded: view === "full", onExpandedChange: (e: boolean) => setView(e ? "full" : "") };
|
|
89
|
+
const [runsKey, setRunsKey] = useState(0);
|
|
90
|
+
const q = search.trim().toLowerCase();
|
|
91
|
+
useEffect(() => setInput("{}"), [pending]);
|
|
92
|
+
|
|
93
|
+
const list = useG8Query((signal) => g8Get("/workflows", undefined, { signal }), []);
|
|
94
|
+
const failed = useG8Query((signal) => g8Get("/workflows/executions", { status_filter: "failed", limit: 1 }, { signal }), []);
|
|
95
|
+
const all = (Array.isArray(payload(list.data).actions) ? payload(list.data).actions : []) as WorkflowRow[];
|
|
96
|
+
const failedTotal = payload(failed.data).total_count;
|
|
97
|
+
const rows = all.filter((w) => (!q || `${w.name} ${w.description ?? ""}`.toLowerCase().includes(q)) && (!filter || (filter === "enabled") === isOn(w)));
|
|
98
|
+
const open = all.find((w) => w.action_id === selected);
|
|
99
|
+
|
|
100
|
+
const act = useG8Mutation(async (p: Pending) => {
|
|
101
|
+
if (p.kind === "create") return g8Post("/workflows", { name: p.name || "Untitled workflow", description: "", config: blankConfig(p.name || "Untitled workflow") });
|
|
102
|
+
const base = `/workflows/${encodeURIComponent(p.workflow.action_id)}`;
|
|
103
|
+
if (p.kind === "run") return g8Post(`${base}/execute`, { input_data: JSON.parse(input || "{}") });
|
|
104
|
+
if (p.kind === "toggle") return g8Put(base, { enabled: !isOn(p.workflow) });
|
|
105
|
+
return g8Delete(base);
|
|
106
|
+
}, {
|
|
107
|
+
onSuccess: (res, p) => {
|
|
108
|
+
setPending(null);
|
|
109
|
+
if (p.kind === "create") { setNewName(""); setSheet(false); const id = str(payload(res).action_id); if (id) { setSelected(id); setTab("workflows"); } }
|
|
110
|
+
if (p.kind === "run") { setTab("runs"); setRun(str(payload(res).execution_id)); }
|
|
111
|
+
if (p.kind === "delete" && selected === p.workflow.action_id) setSelected("");
|
|
112
|
+
void list.refetch();
|
|
113
|
+
void failed.refetch();
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
let inputOk = true;
|
|
117
|
+
try { JSON.parse(input || "{}"); } catch { inputOk = false; }
|
|
118
|
+
const copy = pending ? copyFor(pending) : null;
|
|
119
|
+
|
|
120
|
+
const detail = open ? (
|
|
121
|
+
<DetailPanel title={open.name} subtitle={open.description ?? open.category ?? undefined} onClose={closeWorkflow} onExpand={() => setView("full")}
|
|
122
|
+
badges={<StatusPill tone={isOn(open) ? "positive" : "neutral"} dot>{isOn(open) ? "Enabled" : "Disabled"}</StatusPill>}
|
|
123
|
+
actions={<>
|
|
124
|
+
<Button size="sm" onClick={() => setPending({ kind: "run", workflow: open })}>Run</Button>
|
|
125
|
+
{renderDetailActions?.(open)}
|
|
126
|
+
</>}
|
|
127
|
+
tabs={[
|
|
128
|
+
{ id: "steps", label: "Steps", content: <WorkflowEditor key={open.action_id} actionId={open.action_id} onSaved={() => void list.refetch()} /> },
|
|
129
|
+
{ id: "trigger", label: "Trigger", content: <TriggerConfig key={open.action_id} actionId={open.action_id} onSaved={() => void list.refetch()} /> },
|
|
130
|
+
{ id: "runs", label: "Runs", content: <RunHistory key={open.action_id} actionId={open.action_id} /> },
|
|
131
|
+
...(extraDetailTabs ?? []).map((t) => ({ id: t.id, label: t.label, content: t.render(open) })),
|
|
132
|
+
]} />
|
|
133
|
+
) : selected && !list.loading ? <EmptyState title="Workflow not found" description="It may have been deleted." /> : null;
|
|
134
|
+
|
|
135
|
+
const listView = (
|
|
136
|
+
<div className="flex flex-col gap-3">
|
|
137
|
+
<Toolbar search={{ value: search, onChange: setSearch, placeholder: "Search workflows" }} actions={toolbarActions}
|
|
138
|
+
filters={<div role="group" aria-label="Status" className="flex gap-1">{FILTERS.map((f) => <Button key={f.id || "all"} size="sm" variant={filter === f.id ? "default" : "outline"} onClick={() => setFilter(f.id)}>{f.label}</Button>)}</div>} />
|
|
139
|
+
{list.error ? <ApiError error={list.error} title="Could not load workflows" onRetry={() => void list.refetch()} /> : null}
|
|
140
|
+
{act.error ? <ApiError error={act.error} title="That action failed" /> : null}
|
|
141
|
+
{list.loading && !list.data ? <div className="space-y-2" aria-busy="true">{[0, 1, 2].map((i) => <Skeleton key={i} className="h-14 w-full" />)}</div> : null}
|
|
142
|
+
<ul className="rounded-md border">
|
|
143
|
+
{rows.map((w) => (
|
|
144
|
+
<li key={w.action_id} className={`flex items-center gap-2 border-b px-3 py-2 ${w.action_id === selected ? "bg-accent" : ""}`}>
|
|
145
|
+
<button type="button" data-testid={`workflow-${w.action_id}`} onClick={() => setSelected(w.action_id)} className="min-w-0 flex-1 text-left">
|
|
146
|
+
<span className="block truncate text-sm font-medium">{w.name}</span>
|
|
147
|
+
<span className="block truncate text-xs text-muted-foreground">{w.description || w.category || "No description"}</span>
|
|
148
|
+
</button>
|
|
149
|
+
<StatusPill tone={isOn(w) ? "positive" : "neutral"} dot>{isOn(w) ? "Enabled" : "Disabled"}</StatusPill>
|
|
150
|
+
<Button size="sm" variant="outline" onClick={() => setPending({ kind: "run", workflow: w })}>Run</Button>
|
|
151
|
+
<Button size="sm" variant="ghost" onClick={() => setPending({ kind: "toggle", workflow: w })}>{isOn(w) ? "Disable" : "Enable"}</Button>
|
|
152
|
+
<Button size="sm" variant="ghost" aria-label={`Delete ${w.name}`} onClick={() => setPending({ kind: "delete", workflow: w })}>Delete</Button>
|
|
153
|
+
{renderRowActions?.(w)}
|
|
154
|
+
</li>
|
|
155
|
+
))}
|
|
156
|
+
</ul>
|
|
157
|
+
{!list.loading && !list.error && rows.length === 0 ? (
|
|
158
|
+
<EmptyState title={all.length ? "No workflows match" : "No workflows yet"} description="A workflow runs steps automatically when its trigger fires or when you run it."
|
|
159
|
+
action={all.length ? undefined : <Button onClick={() => setSheet(true)}>New workflow</Button>} />
|
|
160
|
+
) : null}
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const current = tab === "runs" ? "runs" : "workflows";
|
|
165
|
+
return (
|
|
166
|
+
<ScreenLayout className={className} title="Workflows" description="Automations that run when something happens, or when you press Run."
|
|
167
|
+
icon={<Workflow className="size-5" />} tabs={SCREEN_TABS} activeTab={current} onTabChange={setTab}
|
|
168
|
+
actions={<Button onClick={() => setSheet(true)}><Plus className="mr-1 size-4" />New workflow</Button>}
|
|
169
|
+
stats={current === "workflows" ? [
|
|
170
|
+
{ label: "Workflows", value: String(all.length) },
|
|
171
|
+
{ label: "Enabled", value: String(all.filter(isOn).length), tone: "positive" },
|
|
172
|
+
{ label: "Disabled", value: String(all.filter((w) => !isOn(w)).length) },
|
|
173
|
+
{ label: "Failed runs", value: typeof failedTotal === "number" ? failedTotal.toLocaleString() : "-", tone: failedTotal ? "negative" : "neutral" },
|
|
174
|
+
] : undefined}>
|
|
175
|
+
{current === "runs" ? (
|
|
176
|
+
<SplitView list={<RunHistory hideDetail selectedId={run || null} onSelectRun={(id) => setRun(id ?? "")} refreshKey={runsKey} nameFor={(id) => all.find((w) => w.action_id === id)?.name} />}
|
|
177
|
+
detailOpen={Boolean(run)} onCloseDetail={closeRun} {...expand} detailWidth="lg"
|
|
178
|
+
detail={run ? <DetailPanel title="Run" subtitle={run} onClose={closeRun} onExpand={() => setView("full")}
|
|
179
|
+
tabs={[{ id: "run", label: "Run", content: <RunDetail key={run} executionId={run} onChanged={() => setRunsKey((k) => k + 1)} /> }]} /> : null} />
|
|
180
|
+
) : <SplitView list={listView} detail={detail} detailOpen={Boolean(selected)} onCloseDetail={closeWorkflow} {...expand} detailWidth="xl" />}
|
|
181
|
+
|
|
182
|
+
<FormSheet open={sheet} onOpenChange={setSheet} title="New workflow" description="Starts with a manual trigger and an end step. You add the steps next."
|
|
183
|
+
footer={<><Button variant="ghost" onClick={() => setSheet(false)}>Cancel</Button><Button onClick={() => setPending({ kind: "create", name: newName.trim() })}>Create</Button></>}>
|
|
184
|
+
<div className="flex flex-col gap-1"><label className="text-sm font-medium" htmlFor="wf-name">Name</label><Input id="wf-name" value={newName} onChange={(e) => setNewName(e.target.value)} placeholder="Workflow name" /></div>
|
|
185
|
+
</FormSheet>
|
|
186
|
+
{pending && copy ? (
|
|
187
|
+
<ConfirmSpendDialog open tier={copy.tier} title={copy.title} confirmLabel={copy.label} confirming={act.loading} affected={pending.kind === "delete" ? 1 : undefined}
|
|
188
|
+
description={pending.kind === "run" ? <RunPlan actionId={pending.workflow.action_id} input={input} setInput={setInput} /> : copy.description}
|
|
189
|
+
onConfirm={() => { if (pending.kind !== "run" || inputOk) void act.mutate(pending); }} onCancel={() => setPending(null)} />
|
|
190
|
+
) : null}
|
|
191
|
+
</ScreenLayout>
|
|
192
|
+
);
|
|
193
|
+
}
|