@elevasis/ui 2.35.0 → 2.36.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.
Files changed (51) hide show
  1. package/dist/app/index.d.ts +25 -18
  2. package/dist/app/index.js +2 -2
  3. package/dist/{chunk-26HFM4MH.js → chunk-F6EFC2MJ.js} +1555 -1903
  4. package/dist/{chunk-VDOOGGBA.js → chunk-K4UZU3TU.js} +2 -2
  5. package/dist/{chunk-4U3XAWCN.js → chunk-O2Q4VMRN.js} +134 -495
  6. package/dist/components/index.d.ts +7 -4
  7. package/dist/components/index.js +2 -2
  8. package/dist/components/navigation/index.js +2 -2
  9. package/dist/execution/index.d.ts +2 -2
  10. package/dist/features/auth/index.d.ts +3 -0
  11. package/dist/features/auth/index.js +2 -2
  12. package/dist/features/clients/index.js +2 -2
  13. package/dist/features/crm/index.d.ts +3 -0
  14. package/dist/features/crm/index.js +2 -2
  15. package/dist/features/dashboard/index.d.ts +3 -3
  16. package/dist/features/dashboard/index.js +2 -2
  17. package/dist/features/delivery/index.d.ts +3 -0
  18. package/dist/features/delivery/index.js +2 -2
  19. package/dist/features/knowledge/index.js +3 -3
  20. package/dist/features/lead-gen/index.d.ts +286 -48
  21. package/dist/features/lead-gen/index.js +2 -2
  22. package/dist/features/monitoring/index.js +2 -2
  23. package/dist/features/monitoring/requests/index.js +2 -2
  24. package/dist/features/operations/index.d.ts +30 -30
  25. package/dist/features/operations/index.js +2 -2
  26. package/dist/features/settings/index.d.ts +3 -0
  27. package/dist/features/settings/index.js +2 -2
  28. package/dist/hooks/delivery/index.d.ts +3 -0
  29. package/dist/hooks/delivery/index.js +2 -2
  30. package/dist/hooks/index.d.ts +29 -16
  31. package/dist/hooks/index.js +2 -2
  32. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +13 -13
  33. package/dist/hooks/published.d.ts +29 -16
  34. package/dist/hooks/published.js +2 -2
  35. package/dist/index.d.ts +51 -34
  36. package/dist/index.js +2 -2
  37. package/dist/initialization/index.d.ts +3 -0
  38. package/dist/knowledge/index.d.ts +21 -21
  39. package/dist/knowledge/index.js +6 -6
  40. package/dist/{knowledge-search-index-ORIJCEZX.js → knowledge-search-index-NBCTFIOH.js} +2 -2
  41. package/dist/organization/index.js +2 -2
  42. package/dist/profile/index.d.ts +3 -0
  43. package/dist/provider/index.d.ts +25 -18
  44. package/dist/provider/index.js +2 -2
  45. package/dist/provider/published.d.ts +25 -18
  46. package/dist/provider/published.js +2 -2
  47. package/dist/sse/index.js +26 -5
  48. package/dist/supabase/index.d.ts +6 -0
  49. package/dist/types/index.d.ts +16 -13
  50. package/dist/utils/index.d.ts +3 -3
  51. package/package.json +4 -4
@@ -1,10 +1,10 @@
1
1
  import { ChatHeader, ChatSidebar } from './chunk-CXY7FMUM.js';
2
- import { observabilityKeys, useCyberColors, CardHeader, EmptyState, CyberLegendItem, CyberAreaChart, CenteredErrorState, StatsCardSkeleton, TrendIndicator, DetailCardSkeleton, FeatureUnavailableState, ElevasisLoader, GlowDot, JsonViewer, ListSkeleton, PageTitleCaption, StatCard, APIErrorAlert, CyberDonut, CollapsibleSection, ActivityTimeline, StatusBadge, TabCountBadge, ResourceCard, HeroStatsRow, ContextViewer, CostTrendChart, ActivityTrendChart } from './chunk-SIQ3P4OR.js';
2
+ import { observabilityKeys, useCyberColors, CardHeader, EmptyState, CyberLegendItem, CyberAreaChart, CenteredErrorState, StatsCardSkeleton, TrendIndicator, DetailCardSkeleton, FeatureUnavailableState, ElevasisLoader, GlowDot, JsonViewer, ListSkeleton, PageTitleCaption, StatCard, APIErrorAlert, CyberDonut, CollapsibleSection, ActivityTimeline, ContextViewer, StatusBadge, TabCountBadge, ResourceCard, HeroStatsRow, CostTrendChart, ActivityTrendChart } from './chunk-SIQ3P4OR.js';
3
3
  import { StyledMarkdown } from './chunk-3KMDHCAR.js';
4
4
  import { glassBase, mantineThemeOverride, createCssVariablesResolver, PresetsProvider, useAvailablePresets } from './chunk-AKW7KISS.js';
5
5
  import { PRESETS, getPreset, generateShades } from './chunk-WF7CONXF.js';
6
6
  import { useSupabase } from './chunk-BRXELOHC.js';
7
- import { PROSPECTING_STEPS, resolveOrganizationModel, OrgKnowledgeNodeSchema, defineResources, ActionSchema, defineTopology, topologyRef, listAllSystems, projectOrganizationSurfaces, getLeadGenStageCatalog, topologyRelationship, PROJECTS_VIEW_ACTION_ID, resolveSemanticIconComponent, buildOrganizationGraph, getResourcesForSystem, devOnlyFor, requiresAdminFor, topLevel, parentOf, ancestorsOf, childrenOf, findById, findByPath, SemanticIcon, getSystem, defaultPathFor, getSortedSidebarEntries, compileOrganizationOntology, getSystemAncestors, getSemanticIconComponent, findPipeline } from './chunk-4U3XAWCN.js';
7
+ import { resolveOrganizationModel, OrgKnowledgeNodeSchema, defineResources, defineActions, defineTopology, topologyRef, listAllSystems, projectOrganizationSurfaces, topologyRelationship, PROJECTS_VIEW_ACTION_ID, resolveSemanticIconComponent, getAllBuildTemplates, getLeadGenStageCatalog, buildOrganizationGraph, getResourcesForSystem, devOnlyFor, requiresAdminFor, topLevel, parentOf, ancestorsOf, childrenOf, findById, findByPath, getSystem, SemanticIcon, defaultPathFor, getSortedSidebarEntries, compileOrganizationOntology, getSystemAncestors, getSemanticIconComponent } from './chunk-O2Q4VMRN.js';
8
8
  import { SubshellContainer, SubshellSidebar, SubshellRightSideContainer, AppearanceProvider, AppShellLoader, useAppearance, SubshellSidebarLoader, SubshellContentContainer, PageContainer, SubshellLoader, AppShellCenteredContainer } from './chunk-LUYVRATI.js';
9
9
  import { AppBackground } from './chunk-WLOQ4IBG.js';
10
10
  import { HTTP_HEADERS, ApiClientProvider, useApiClient } from './chunk-57OZ3AEG.js';
@@ -14,7 +14,7 @@ import { useRouterContext } from './chunk-Q7DJKLEN.js';
14
14
  import { Graph_module_css_default, useDirectedChainHighlighting, useNodeSelection, useFitViewTrigger, useGraphHighlighting, calculateGraphHeight, GRAPH_CONSTANTS } from './chunk-HENXLGVD.js';
15
15
  import { STATUS_COLORS, getStatusIcon, formatDuration, getStatusColors, AGENT_CONSTANTS, shouldAnimateEdge, TIMELINE_CONSTANTS, calculateBarPosition, CONTAINER_CONSTANTS, useExecutionPath, useUnifiedWorkflowLayout, WORKFLOW_CONSTANTS, useReactFlowAgent, getResourceStatusColor, useMergedExecution, useTimelineData, useAgentIterationData } from './chunk-7FPLLSHN.js';
16
16
  import { ResourceTypeSchema, NonEmptyStringSchema, OriginResourceTypeSchema, UuidSchema, CredentialNameSchema, getErrorInfo, getErrorTitle, formatErrorMessage, STALE_TIME_DEFAULT, isAPIClientError, getTimeRangeDates, formatBucketTime, PAGE_SIZE_DEFAULT, REFETCH_INTERVAL_DASHBOARD, STALE_TIME_MONITORING, REFETCH_INTERVAL_RUNNING, WS_MAX_RETRIES_BEFORE_ERROR, WS_RECONNECT_BASE_DELAY, WS_RECONNECT_MAX_DELAY, STALE_TIME_ADMIN, APIClientError, GC_TIME_MEDIUM, GC_TIME_SHORT, formatDateTime, getResourceIcon, formatTimeAgo, formatDate, formatRelativeTime, debounce, OAUTH_FLOW_TIMEOUT } from './chunk-GEFWMU26.js';
17
- import { ResourceStatusColors, toWorkflowLogMessages, isStepStartedContext, isStepCompletedContext, isStepFailedContext } from './chunk-KRWALB24.js';
17
+ import { ResourceStatusColors, toWorkflowLogMessages } from './chunk-KRWALB24.js';
18
18
  import { useStableAccessToken } from './chunk-VKIZUUPM.js';
19
19
  import { useInitialization, InitializationProvider } from './chunk-533DUEQY.js';
20
20
  import { OrganizationContext, useOrganization } from './chunk-DD3CCMCZ.js';
@@ -24,7 +24,7 @@ import { useAuthContext } from './chunk-BRJ3QZ4E.js';
24
24
  import { createContext, lazy, memo, useMemo, useState, useRef, useEffect, useCallback, createElement, useContext, cloneElement, useId, Fragment as Fragment$1, Component, useDeferredValue, useEffectEvent, Suspense } from 'react';
25
25
  import { useQueryClient, useQuery, useMutation, QueryClientProvider, useQueries, QueryClient } from '@tanstack/react-query';
26
26
  import { createUseExternalEvents, Paper, Stack, Text, Group, Badge, Box, useComputedColorScheme, Card, Title, Checkbox, Tooltip, Center, Modal, TextInput, Textarea, Loader, Alert, Button, Space, Table, Select, ActionIcon, SegmentedControl, NumberFormatter, Switch, Pagination, mergeThemeOverrides, MantineProvider, UnstyledButton, Divider, ScrollArea, Popover, Indicator, Collapse, ThemeIcon, SimpleGrid, Code, CopyButton, PasswordInput, Anchor, Menu, Timeline, useMantineTheme, RangeSlider, Progress, Tabs, Breadcrumbs as Breadcrumbs$1, NumberInput, ColorSwatch, RingProgress, Grid, LoadingOverlay, Accordion, MultiSelect, Radio, Pill, Drawer, Chip, JsonInput, TagsInput } from '@mantine/core';
27
- import { IconPlayerPlay, IconPlayerStop, IconArrowsSplit, IconSquare, IconBrain, IconFileText, IconLayoutGrid, IconColumns, IconFileInvoice, IconNotes, IconBriefcase, IconChecklist, IconFlag, IconList, IconBuilding, IconAddressBook, IconMail, IconSend, IconClock, IconArrowUp, IconMessageCircle, IconRocket, IconEye, IconEdit, IconAlertTriangle, IconRefresh, IconX, IconCheck, IconTarget, IconActivity, IconCode, IconSettings, IconLock, IconShieldOff, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconChartBar, IconCircleCheck, IconThumbDown, IconThumbUp, IconCircleX, IconSearch, IconFilterOff, IconArrowUpRight, IconAlertCircle, IconExternalLink, IconChartPie, IconHeartbeat, IconDownload, IconCpu, IconTrendingUp, IconCash, IconInfoCircle, IconExclamationCircle, IconBug, IconChecks, IconCircle, IconCircleFilled, IconBell, IconFocus2, IconChevronRight, IconTool, IconClockHour4, IconVersions, IconNetwork, IconSitemap, IconPencil, IconKey, IconCalendar, IconExclamationMark, IconShieldLock, IconCopy, IconPlus, IconPower, IconTag, IconUsers, IconWebhook, IconReload, IconTerminal2, IconMessage, IconArrowLeft, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconDotsVertical, IconPlayerPause, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconFilter, IconCategory, IconDatabase, IconTrophy, IconClockExclamation, IconUser, IconHistory, IconCurrencyDollar, IconTargetArrow, IconMessages, IconInbox, IconBolt, IconRestore, IconArrowRight, IconSparkles, IconListDetails, IconLayoutDashboard, IconBuildingFactory2, IconFileDescription, IconApps, IconRoute, IconHandStop, IconAdjustmentsHorizontal, IconCircleDashed, IconFolders, IconBraces, IconTopologyStar3, IconLayoutSidebarRightExpand, IconNote, IconArchive, IconTimeline, IconPalette, IconAdjustments, IconBrandDropbox, IconBrandGoogleDrive, IconPlug, IconMailForward, IconArrowDown, IconUserX, IconUserCheck, IconFileOff, IconCheckbox, IconPhone, IconActivityHeartbeat, IconClockPause, IconArrowsMaximize, IconShare2, IconFolder, IconCircleDot, IconProgressCheck, IconScript, IconPlugConnected } from '@tabler/icons-react';
27
+ import { IconPlayerPlay, IconPlayerStop, IconArrowsSplit, IconSquare, IconBrain, IconFileText, IconLayoutGrid, IconColumns, IconFileInvoice, IconNotes, IconBriefcase, IconChecklist, IconFlag, IconList, IconBuilding, IconAddressBook, IconMail, IconSend, IconClock, IconArrowUp, IconMessageCircle, IconRocket, IconEye, IconEdit, IconAlertTriangle, IconRefresh, IconX, IconCheck, IconTarget, IconActivity, IconCode, IconSettings, IconLock, IconShieldOff, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconChartBar, IconCircleCheck, IconThumbDown, IconThumbUp, IconCircleX, IconSearch, IconFilterOff, IconArrowUpRight, IconAlertCircle, IconExternalLink, IconChartPie, IconHeartbeat, IconDownload, IconCpu, IconTrendingUp, IconCash, IconInfoCircle, IconExclamationCircle, IconBug, IconChecks, IconCircle, IconCircleFilled, IconBell, IconFocus2, IconChevronRight, IconTool, IconClockHour4, IconVersions, IconNetwork, IconSitemap, IconPencil, IconKey, IconCalendar, IconExclamationMark, IconShieldLock, IconCopy, IconPlus, IconPower, IconTag, IconUsers, IconWebhook, IconReload, IconTerminal2, IconMessage, IconArrowLeft, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconDotsVertical, IconPlayerPause, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconFilter, IconCategory, IconDatabase, IconTrophy, IconClockExclamation, IconUser, IconHistory, IconCurrencyDollar, IconTargetArrow, IconMessages, IconInbox, IconBolt, IconRestore, IconArrowRight, IconSparkles, IconListDetails, IconLayoutDashboard, IconBuildingFactory2, IconFileDescription, IconApps, IconRoute, IconHandStop, IconAdjustmentsHorizontal, IconCircleDashed, IconFolders, IconBraces, IconTopologyStar3, IconLayoutSidebarRightExpand, IconNote, IconArchive, IconTimeline, IconPalette, IconAdjustments, IconBrandDropbox, IconBrandGoogleDrive, IconPlug, IconMailForward, IconArrowDown, IconUserX, IconUserCheck, IconFileOff, IconCheckbox, IconPhone, IconActivityHeartbeat, IconClockPause, IconArrowsMaximize, IconShare2, IconFolder, IconScript, IconPlugConnected } from '@tabler/icons-react';
28
28
  import { z } from 'zod';
29
29
  import * as runtime from 'react/jsx-runtime';
30
30
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -135,7 +135,7 @@ function OrganizationProvider({ apiRequest, children }) {
135
135
  const stillPresent = data.find((m) => m.organization?.workos_org_id === currentWorkOSOrganizationId);
136
136
  if (!stillPresent) {
137
137
  applyMembership(data[0] ?? null);
138
- } else if (stillPresent.id !== currentMembership?.id) {
138
+ } else {
139
139
  applyMembership(stillPresent);
140
140
  }
141
141
  }
@@ -152,7 +152,6 @@ function OrganizationProvider({ apiRequest, children }) {
152
152
  memberships.length,
153
153
  apiRequest,
154
154
  currentWorkOSOrganizationId,
155
- currentMembership?.id,
156
155
  selectOrganization,
157
156
  applyMembership
158
157
  ]);
@@ -330,70 +329,6 @@ var ListMembershipsQuerySchema = z.object({
330
329
  }).strict().refine((data) => data.userId || data.organizationId, {
331
330
  message: "Either userId or organizationId must be provided"
332
331
  });
333
-
334
- // ../core/src/business/acquisition/build-templates.ts
335
- var BUILD_TEMPLATE_CATALOG = [
336
- {
337
- id: "local-services",
338
- label: "Local Services",
339
- description: "Source, analyze, qualify, and personalize local service businesses for outreach.",
340
- steps: Object.values(PROSPECTING_STEPS.localServices)
341
- },
342
- {
343
- id: "dtc-subscription-apollo-clickup",
344
- label: "DTC Subscription (Apollo + ClickUp)",
345
- description: "Import DTC brand leads from Apollo, crawl their websites, score fit, enrich contacts, and export via ClickUp.",
346
- steps: Object.values(PROSPECTING_STEPS.dtcApolloClickup)
347
- }
348
- ];
349
- var DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID = "local-services";
350
- var PROSPECTING_BUILD_TEMPLATE_OPTIONS = BUILD_TEMPLATE_CATALOG.map(({ id, label, description }) => ({
351
- id,
352
- label,
353
- description
354
- }));
355
- function createBuildPlanSnapshotFromTemplateId(templateId) {
356
- const template = BUILD_TEMPLATE_CATALOG.find((item) => item.id === templateId);
357
- if (!template) return null;
358
- return {
359
- templateId: template.id,
360
- templateLabel: template.label,
361
- steps: template.steps.map((step) => {
362
- const snapshotStep = {
363
- id: step.id,
364
- label: step.label,
365
- primaryEntity: step.primaryEntity,
366
- outputs: [...step.outputs],
367
- stageKey: step.stageKey,
368
- recordsStageKey: step.recordsStageKey ?? step.stageKey,
369
- recordSourceStageKey: step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey,
370
- dependencyMode: step.dependencyMode,
371
- actionKey: step.actionKey,
372
- defaultBatchSize: step.defaultBatchSize,
373
- maxBatchSize: step.maxBatchSize
374
- };
375
- if (step.description) snapshotStep.description = step.description;
376
- if (step.recordEntity) snapshotStep.recordEntity = step.recordEntity;
377
- if (step.dependsOn?.length) snapshotStep.dependsOn = [...step.dependsOn];
378
- if (step.credentialRequirements?.length) {
379
- snapshotStep.credentialRequirements = step.credentialRequirements.map((requirement) => ({
380
- ...requirement
381
- }));
382
- }
383
- if (step.recordColumns) {
384
- snapshotStep.recordColumns = {
385
- ...step.recordColumns.company ? {
386
- company: step.recordColumns.company.map((column) => ({ ...column }))
387
- } : {},
388
- ...step.recordColumns.contact ? {
389
- contact: step.recordColumns.contact.map((column) => ({ ...column }))
390
- } : {}
391
- };
392
- }
393
- return snapshotStep;
394
- })
395
- };
396
- }
397
332
  var RelativeScheduleItemSchema = z.object({
398
333
  offset: z.string().regex(/^[+-]\d+[dhm]$/, "Invalid offset format. Use +3d, -2h, +30m, etc."),
399
334
  payload: z.record(z.string(), z.unknown()),
@@ -651,6 +586,60 @@ z.object({
651
586
  z.object({
652
587
  ids: z.array(UuidSchema).min(1).max(500)
653
588
  }).strict();
589
+
590
+ // ../core/src/business/acquisition/build-templates.ts
591
+ function getProspectingBuildTemplateOptions(templates) {
592
+ return templates.map(({ id, label, description }) => ({
593
+ id,
594
+ label,
595
+ description
596
+ }));
597
+ }
598
+ function createBuildPlanSnapshotFromTemplate(template) {
599
+ return {
600
+ templateId: template.id,
601
+ templateLabel: template.label,
602
+ steps: template.steps.map((step) => {
603
+ const snapshotStep = {
604
+ id: step.id,
605
+ label: step.label,
606
+ primaryEntity: step.primaryEntity,
607
+ outputs: [...step.outputs],
608
+ stageKey: step.stageKey,
609
+ recordsStageKey: step.recordsStageKey ?? step.stageKey,
610
+ recordSourceStageKey: step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey,
611
+ dependencyMode: step.dependencyMode,
612
+ actionKey: step.actionKey,
613
+ defaultBatchSize: step.defaultBatchSize,
614
+ maxBatchSize: step.maxBatchSize
615
+ };
616
+ if (step.description) snapshotStep.description = step.description;
617
+ if (step.recordEntity) snapshotStep.recordEntity = step.recordEntity;
618
+ if (step.dependsOn?.length) snapshotStep.dependsOn = [...step.dependsOn];
619
+ if (step.credentialRequirements?.length) {
620
+ snapshotStep.credentialRequirements = step.credentialRequirements.map((requirement) => ({
621
+ ...requirement
622
+ }));
623
+ }
624
+ if (step.recordColumns) {
625
+ snapshotStep.recordColumns = {
626
+ ...step.recordColumns.company ? {
627
+ company: step.recordColumns.company.map((column) => ({ ...column }))
628
+ } : {},
629
+ ...step.recordColumns.contact ? {
630
+ contact: step.recordColumns.contact.map((column) => ({ ...column }))
631
+ } : {}
632
+ };
633
+ }
634
+ return snapshotStep;
635
+ })
636
+ };
637
+ }
638
+ function createBuildPlanSnapshotFromTemplateId(templateId, templates = []) {
639
+ const template = templates.find((item) => item.id === templateId);
640
+ if (!template) return null;
641
+ return createBuildPlanSnapshotFromTemplate(template);
642
+ }
654
643
  z.object({
655
644
  replyBody: z.string().trim().min(1).max(1e4)
656
645
  }).strict();
@@ -1956,11 +1945,11 @@ function FilterBar({ children, actions }) {
1956
1945
  /* @__PURE__ */ jsx(Group, { gap: "md", children: actions })
1957
1946
  ] }) });
1958
1947
  }
1959
- function TableSelectionToolbar({ selectedCount: selectedCount2, onDelete, isDeleting }) {
1960
- if (selectedCount2 === 0) return null;
1948
+ function TableSelectionToolbar({ selectedCount, onDelete, isDeleting }) {
1949
+ if (selectedCount === 0) return null;
1961
1950
  return /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1962
1951
  /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 600, c: "blue", children: [
1963
- selectedCount2,
1952
+ selectedCount,
1964
1953
  " selected"
1965
1954
  ] }),
1966
1955
  onDelete && /* @__PURE__ */ jsx(
@@ -3449,16 +3438,190 @@ function useExecutions(resourceId, resourceStatus, limit, offset) {
3449
3438
  enabled: isReady && !!resourceId
3450
3439
  });
3451
3440
  }
3441
+ var acquisitionListKeys = {
3442
+ all: ["acquisition-lists"],
3443
+ list: (organizationId) => [...acquisitionListKeys.all, organizationId],
3444
+ detail: (organizationId, listId) => [...acquisitionListKeys.all, organizationId, listId],
3445
+ telemetry: (organizationId) => [...acquisitionListKeys.all, "telemetry", organizationId],
3446
+ progress: (organizationId, listId) => [...acquisitionListKeys.all, "progress", organizationId, listId],
3447
+ executionsBase: (organizationId, listId) => [...acquisitionListKeys.all, "executions", organizationId, listId],
3448
+ records: (organizationId, listId, filters) => [
3449
+ ...acquisitionListKeys.all,
3450
+ "records",
3451
+ organizationId,
3452
+ listId,
3453
+ filters.entity,
3454
+ filters.stage ?? null,
3455
+ filters.limit ?? null,
3456
+ filters.offset ?? null
3457
+ ],
3458
+ executions: (organizationId, listId, filters) => [
3459
+ ...acquisitionListKeys.executionsBase(organizationId, listId),
3460
+ filters?.resourceId ?? null,
3461
+ filters?.limit ?? null
3462
+ ]
3463
+ };
3464
+ function useLists() {
3465
+ const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3466
+ return useQuery({
3467
+ queryKey: acquisitionListKeys.list(workOSOrganizationId),
3468
+ queryFn: () => apiRequest("/acquisition/lists"),
3469
+ enabled: isReady
3470
+ });
3471
+ }
3472
+ function useList(listId) {
3473
+ const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3474
+ return useQuery({
3475
+ queryKey: acquisitionListKeys.detail(workOSOrganizationId, listId),
3476
+ queryFn: () => apiRequest(`/acquisition/lists/${listId}`),
3477
+ enabled: isReady && !!listId
3478
+ });
3479
+ }
3480
+ function useListsTelemetry() {
3481
+ const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3482
+ return useQuery({
3483
+ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId),
3484
+ queryFn: () => apiRequest("/acquisition/lists/telemetry"),
3485
+ enabled: isReady
3486
+ });
3487
+ }
3488
+ function useListProgress(listId, options = {}) {
3489
+ const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3490
+ return useQuery({
3491
+ queryKey: acquisitionListKeys.progress(workOSOrganizationId, listId),
3492
+ queryFn: () => apiRequest(`/acquisition/lists/${listId}/progress`),
3493
+ enabled: isReady && !!listId && (options.enabled ?? true),
3494
+ refetchInterval: options.refetchInterval
3495
+ });
3496
+ }
3497
+ function useListRecords(listId, filters) {
3498
+ const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3499
+ return useQuery({
3500
+ queryKey: acquisitionListKeys.records(workOSOrganizationId, listId, filters),
3501
+ queryFn: () => {
3502
+ const params = new URLSearchParams({
3503
+ entity: filters.entity,
3504
+ limit: String(filters.limit ?? 50),
3505
+ offset: String(filters.offset ?? 0)
3506
+ });
3507
+ if (filters.stage) params.set("stage", filters.stage);
3508
+ return apiRequest(`/acquisition/lists/${listId}/records?${params.toString()}`);
3509
+ },
3510
+ enabled: isReady && !!listId && !!filters.entity
3511
+ });
3512
+ }
3513
+ function useListExecutions(listId, filters = {}) {
3514
+ const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3515
+ return useQuery({
3516
+ queryKey: acquisitionListKeys.executions(workOSOrganizationId, listId, filters),
3517
+ queryFn: async () => {
3518
+ const executions = await apiRequest(`/acquisition/lists/${listId}/executions`);
3519
+ const filteredExecutions = filters.resourceId ? executions.filter((execution) => execution.resourceId === filters.resourceId) : executions;
3520
+ return typeof filters.limit === "number" ? filteredExecutions.slice(0, filters.limit) : filteredExecutions;
3521
+ },
3522
+ enabled: isReady && !!listId
3523
+ });
3524
+ }
3525
+ function useCreateList() {
3526
+ const { apiRequest, workOSOrganizationId } = useElevasisServices();
3527
+ const queryClient = useQueryClient();
3528
+ return useMutation({
3529
+ mutationFn: (body) => apiRequest("/acquisition/lists", {
3530
+ method: "POST",
3531
+ body: JSON.stringify(body)
3532
+ }),
3533
+ onSuccess: () => {
3534
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3535
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
3536
+ showSuccessNotification("List created");
3537
+ },
3538
+ onError: (error) => {
3539
+ showApiErrorNotification(error);
3540
+ }
3541
+ });
3542
+ }
3543
+ function useUpdateList(listId) {
3544
+ const { apiRequest, workOSOrganizationId } = useElevasisServices();
3545
+ const queryClient = useQueryClient();
3546
+ return useMutation({
3547
+ mutationFn: (body) => apiRequest(`/acquisition/lists/${listId}`, {
3548
+ method: "PATCH",
3549
+ body: JSON.stringify(body)
3550
+ }),
3551
+ onSuccess: () => {
3552
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3553
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.detail(workOSOrganizationId, listId) });
3554
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
3555
+ showSuccessNotification("List updated");
3556
+ },
3557
+ onError: (error) => {
3558
+ showApiErrorNotification(error);
3559
+ }
3560
+ });
3561
+ }
3562
+ function useUpdateListConfig(listId) {
3563
+ const { apiRequest, workOSOrganizationId } = useElevasisServices();
3564
+ const queryClient = useQueryClient();
3565
+ return useMutation({
3566
+ mutationFn: (body) => apiRequest(`/acquisition/lists/${listId}/config`, {
3567
+ method: "PATCH",
3568
+ body: JSON.stringify(body)
3569
+ }),
3570
+ onSuccess: () => {
3571
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3572
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.detail(workOSOrganizationId, listId) });
3573
+ showSuccessNotification("List configuration saved");
3574
+ },
3575
+ onError: (error) => {
3576
+ showApiErrorNotification(error);
3577
+ }
3578
+ });
3579
+ }
3580
+ function useDeleteList() {
3581
+ const { apiRequest, workOSOrganizationId } = useElevasisServices();
3582
+ const queryClient = useQueryClient();
3583
+ return useMutation({
3584
+ mutationFn: async (listId) => {
3585
+ await apiRequest(`/acquisition/lists/${listId}`, {
3586
+ method: "DELETE"
3587
+ });
3588
+ },
3589
+ onSuccess: () => {
3590
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3591
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
3592
+ showSuccessNotification("List deleted");
3593
+ },
3594
+ onError: (error) => {
3595
+ showApiErrorNotification(error);
3596
+ }
3597
+ });
3598
+ }
3599
+
3600
+ // src/hooks/executions/useInFlightExecutions.ts
3452
3601
  function useInFlightExecutions(resourceId, options = {}) {
3453
3602
  const { apiRequest, workOSOrganizationId, isReady } = useElevasisServices();
3454
3603
  const limit = options.limit ?? 20;
3455
3604
  const offset = options.offset ?? 0;
3456
3605
  return useQuery({
3457
- queryKey: [
3458
- ...executionsKeys.executions(workOSOrganizationId, resourceId, "all", limit, offset),
3459
- "in-flight"
3460
- ],
3606
+ queryKey: options.listId ? [...acquisitionListKeys.executions(workOSOrganizationId, options.listId, { resourceId, limit }), "in-flight"] : [...executionsKeys.executions(workOSOrganizationId, resourceId, "all", limit, offset), "in-flight"],
3461
3607
  queryFn: async () => {
3608
+ if (options.listId) {
3609
+ const executions2 = await apiRequest(
3610
+ `/acquisition/lists/${options.listId}/executions`
3611
+ );
3612
+ const runningExecutions = executions2.filter((execution) => execution.resourceId === resourceId && execution.status === "running").slice(0, limit);
3613
+ return {
3614
+ executions: runningExecutions.map((execution) => ({
3615
+ id: execution.executionId,
3616
+ status: "running",
3617
+ startTime: new Date(execution.createdAt).getTime()
3618
+ })),
3619
+ total: runningExecutions.length,
3620
+ limit,
3621
+ offset,
3622
+ hasMore: false
3623
+ };
3624
+ }
3462
3625
  const params = new URLSearchParams({
3463
3626
  limit: String(limit),
3464
3627
  offset: String(offset)
@@ -3494,11 +3657,21 @@ function useExecuteAsync() {
3494
3657
  const { apiRequest, workOSOrganizationId } = useElevasisServices();
3495
3658
  const queryClient = useQueryClient();
3496
3659
  return useMutation({
3497
- mutationFn: async ({ resourceId, resourceType, input }) => {
3660
+ mutationFn: async ({ resourceId, resourceType, input, listId }) => {
3498
3661
  const response = await apiRequest("/execution-engine/execute-async", {
3499
3662
  method: "POST",
3500
3663
  body: JSON.stringify({ resourceType, resourceId, input })
3501
3664
  });
3665
+ if (listId) {
3666
+ try {
3667
+ await apiRequest(`/acquisition/lists/${listId}/executions`, {
3668
+ method: "POST",
3669
+ body: JSON.stringify({ executionId: response.executionId })
3670
+ });
3671
+ } catch (error) {
3672
+ console.warn("Failed to attach execution to list history", error);
3673
+ }
3674
+ }
3502
3675
  return { ...response, resourceId };
3503
3676
  },
3504
3677
  onSuccess: (data) => {
@@ -3508,166 +3681,6 @@ function useExecuteAsync() {
3508
3681
  }
3509
3682
  });
3510
3683
  }
3511
- var acquisitionListKeys = {
3512
- all: ["acquisition-lists"],
3513
- list: (organizationId) => [...acquisitionListKeys.all, organizationId],
3514
- detail: (organizationId, listId) => [...acquisitionListKeys.all, organizationId, listId],
3515
- telemetry: (organizationId) => [...acquisitionListKeys.all, "telemetry", organizationId],
3516
- progress: (organizationId, listId) => [...acquisitionListKeys.all, "progress", organizationId, listId],
3517
- records: (organizationId, listId, filters) => [
3518
- ...acquisitionListKeys.all,
3519
- "records",
3520
- organizationId,
3521
- listId,
3522
- filters.entity,
3523
- filters.stage ?? null,
3524
- filters.limit ?? null,
3525
- filters.offset ?? null
3526
- ],
3527
- executions: (organizationId, listId, filters) => [
3528
- ...acquisitionListKeys.all,
3529
- "executions",
3530
- organizationId,
3531
- listId,
3532
- filters?.resourceId ?? null,
3533
- filters?.limit ?? null
3534
- ]
3535
- };
3536
- function useLists() {
3537
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3538
- return useQuery({
3539
- queryKey: acquisitionListKeys.list(workOSOrganizationId),
3540
- queryFn: () => apiRequest("/acquisition/lists"),
3541
- enabled: isReady
3542
- });
3543
- }
3544
- function useList(listId) {
3545
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3546
- return useQuery({
3547
- queryKey: acquisitionListKeys.detail(workOSOrganizationId, listId),
3548
- queryFn: () => apiRequest(`/acquisition/lists/${listId}`),
3549
- enabled: isReady && !!listId
3550
- });
3551
- }
3552
- function useListsTelemetry() {
3553
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3554
- return useQuery({
3555
- queryKey: acquisitionListKeys.telemetry(workOSOrganizationId),
3556
- queryFn: () => apiRequest("/acquisition/lists/telemetry"),
3557
- enabled: isReady
3558
- });
3559
- }
3560
- function useListProgress(listId, options = {}) {
3561
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3562
- return useQuery({
3563
- queryKey: acquisitionListKeys.progress(workOSOrganizationId, listId),
3564
- queryFn: () => apiRequest(`/acquisition/lists/${listId}/progress`),
3565
- enabled: isReady && !!listId && (options.enabled ?? true),
3566
- refetchInterval: options.refetchInterval
3567
- });
3568
- }
3569
- function useListRecords(listId, filters) {
3570
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3571
- return useQuery({
3572
- queryKey: acquisitionListKeys.records(workOSOrganizationId, listId, filters),
3573
- queryFn: () => {
3574
- const params = new URLSearchParams({
3575
- entity: filters.entity,
3576
- limit: String(filters.limit ?? 50),
3577
- offset: String(filters.offset ?? 0)
3578
- });
3579
- if (filters.stage) params.set("stage", filters.stage);
3580
- return apiRequest(`/acquisition/lists/${listId}/records?${params.toString()}`);
3581
- },
3582
- enabled: isReady && !!listId && !!filters.entity
3583
- });
3584
- }
3585
- function useListExecutions(listId, filters = {}) {
3586
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3587
- return useQuery({
3588
- queryKey: acquisitionListKeys.executions(workOSOrganizationId, listId, filters),
3589
- queryFn: async () => {
3590
- const executions = await apiRequest(`/acquisition/lists/${listId}/executions`);
3591
- const filteredExecutions = filters.resourceId ? executions.filter((execution) => execution.resourceId === filters.resourceId) : executions;
3592
- return typeof filters.limit === "number" ? filteredExecutions.slice(0, filters.limit) : filteredExecutions;
3593
- },
3594
- enabled: isReady && !!listId
3595
- });
3596
- }
3597
- function useCreateList() {
3598
- const { apiRequest, workOSOrganizationId } = useElevasisServices();
3599
- const queryClient = useQueryClient();
3600
- return useMutation({
3601
- mutationFn: (body) => apiRequest("/acquisition/lists", {
3602
- method: "POST",
3603
- body: JSON.stringify(body)
3604
- }),
3605
- onSuccess: () => {
3606
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3607
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
3608
- showSuccessNotification("List created");
3609
- },
3610
- onError: (error) => {
3611
- showApiErrorNotification(error);
3612
- }
3613
- });
3614
- }
3615
- function useUpdateList(listId) {
3616
- const { apiRequest, workOSOrganizationId } = useElevasisServices();
3617
- const queryClient = useQueryClient();
3618
- return useMutation({
3619
- mutationFn: (body) => apiRequest(`/acquisition/lists/${listId}`, {
3620
- method: "PATCH",
3621
- body: JSON.stringify(body)
3622
- }),
3623
- onSuccess: () => {
3624
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3625
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.detail(workOSOrganizationId, listId) });
3626
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
3627
- showSuccessNotification("List updated");
3628
- },
3629
- onError: (error) => {
3630
- showApiErrorNotification(error);
3631
- }
3632
- });
3633
- }
3634
- function useUpdateListConfig(listId) {
3635
- const { apiRequest, workOSOrganizationId } = useElevasisServices();
3636
- const queryClient = useQueryClient();
3637
- return useMutation({
3638
- mutationFn: (body) => apiRequest(`/acquisition/lists/${listId}/config`, {
3639
- method: "PATCH",
3640
- body: JSON.stringify(body)
3641
- }),
3642
- onSuccess: () => {
3643
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3644
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.detail(workOSOrganizationId, listId) });
3645
- showSuccessNotification("List configuration saved");
3646
- },
3647
- onError: (error) => {
3648
- showApiErrorNotification(error);
3649
- }
3650
- });
3651
- }
3652
- function useDeleteList() {
3653
- const { apiRequest, workOSOrganizationId } = useElevasisServices();
3654
- const queryClient = useQueryClient();
3655
- return useMutation({
3656
- mutationFn: (listId) => apiRequest(`/acquisition/lists/${listId}`, {
3657
- method: "DELETE"
3658
- }),
3659
- onSuccess: () => {
3660
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
3661
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
3662
- showSuccessNotification("List deleted");
3663
- },
3664
- onError: (error) => {
3665
- showApiErrorNotification(error);
3666
- }
3667
- });
3668
- }
3669
-
3670
- // src/hooks/executions/useWorkflowExecution.ts
3671
3684
  function useWorkflowExecution({ workflowId, listId }) {
3672
3685
  const mutation = useExecuteAsync();
3673
3686
  const queryClient = useQueryClient();
@@ -3677,14 +3690,15 @@ function useWorkflowExecution({ workflowId, listId }) {
3677
3690
  const result = await mutation.mutateAsync({
3678
3691
  resourceId: workflowId,
3679
3692
  resourceType: "workflow",
3680
- input
3693
+ input,
3694
+ listId
3681
3695
  });
3682
3696
  queryClient.invalidateQueries({
3683
3697
  queryKey: executionsKeys.executionsListBase(workOSOrganizationId, workflowId)
3684
3698
  });
3685
3699
  if (listId) {
3686
3700
  queryClient.invalidateQueries({
3687
- queryKey: acquisitionListKeys.executions(workOSOrganizationId, listId)
3701
+ queryKey: acquisitionListKeys.executionsBase(workOSOrganizationId, listId)
3688
3702
  });
3689
3703
  }
3690
3704
  return result;
@@ -4101,7 +4115,7 @@ function useExecutionSSE(resourceId, options = {}) {
4101
4115
  });
4102
4116
  if (listId) {
4103
4117
  queryClient.invalidateQueries({
4104
- queryKey: acquisitionListKeys.executions(workOSOrganizationId, listId)
4118
+ queryKey: acquisitionListKeys.executionsBase(workOSOrganizationId, listId)
4105
4119
  });
4106
4120
  queryClient.invalidateQueries({
4107
4121
  queryKey: acquisitionListKeys.progress(workOSOrganizationId, listId)
@@ -6725,7 +6739,8 @@ function useCompanies(filters = {}) {
6725
6739
  });
6726
6740
  return apiRequest(`/acquisition/companies${queryString}`);
6727
6741
  },
6728
- enabled: isReady && !!workOSOrganizationId
6742
+ enabled: isReady && !!workOSOrganizationId,
6743
+ retry: (_failureCount, error) => !(error instanceof APIClientError && error.statusCode === 401)
6729
6744
  });
6730
6745
  }
6731
6746
  function useCompanyFacets() {
@@ -6883,7 +6898,8 @@ function useContacts(filters = {}) {
6883
6898
  });
6884
6899
  return apiRequest(`/acquisition/contacts${queryString}`);
6885
6900
  },
6886
- enabled: isReady && !!workOSOrganizationId
6901
+ enabled: isReady && !!workOSOrganizationId,
6902
+ retry: (_failureCount, error) => !(error instanceof APIClientError && error.statusCode === 401)
6887
6903
  });
6888
6904
  }
6889
6905
  function useContact(contactId) {
@@ -7304,7 +7320,8 @@ function useCredentials() {
7304
7320
  const service = new CredentialService(apiRequest);
7305
7321
  return await service.listCredentials();
7306
7322
  },
7307
- enabled: isReady
7323
+ enabled: isReady && Boolean(workOSOrganizationId),
7324
+ retry: false
7308
7325
  });
7309
7326
  }
7310
7327
  function useVerifyCredential() {
@@ -7653,6 +7670,7 @@ function useUpdateMemberConfig() {
7653
7670
  const { apiRequest } = useElevasisServices();
7654
7671
  const adapter = useNotificationAdapter();
7655
7672
  const queryClient = useQueryClient();
7673
+ const { retry: refreshOrganizations } = useOrganization();
7656
7674
  return useMutation({
7657
7675
  mutationFn: async ({ membershipId, config }) => {
7658
7676
  return apiRequest(`/memberships/${membershipId}/config`, {
@@ -7660,8 +7678,9 @@ function useUpdateMemberConfig() {
7660
7678
  body: JSON.stringify({ config })
7661
7679
  });
7662
7680
  },
7663
- onSuccess: () => {
7681
+ onSuccess: async () => {
7664
7682
  queryClient.invalidateQueries({ queryKey: ["memberships"] });
7683
+ await refreshOrganizations();
7665
7684
  notifications.show({
7666
7685
  title: "Success",
7667
7686
  message: "Member config updated",
@@ -10883,6 +10902,27 @@ function renderField(field, form, disabled, jsonState, setJsonState) {
10883
10902
  const description = field.description;
10884
10903
  const placeholder = field.placeholder;
10885
10904
  switch (field.component) {
10905
+ case "checkboxgroup":
10906
+ return /* @__PURE__ */ jsx(
10907
+ Checkbox.Group,
10908
+ {
10909
+ label,
10910
+ description,
10911
+ value: inputProps.value ?? [],
10912
+ onChange: (val) => form.setFieldValue(field.path, val),
10913
+ error: inputProps.error,
10914
+ children: /* @__PURE__ */ jsx(Stack, { gap: 6, mt: "xs", children: (field.options ?? []).map((option) => /* @__PURE__ */ jsx(
10915
+ Checkbox,
10916
+ {
10917
+ value: option.value,
10918
+ label: option.label,
10919
+ disabled
10920
+ },
10921
+ option.value
10922
+ )) })
10923
+ },
10924
+ field.path
10925
+ );
10886
10926
  case "segmented": {
10887
10927
  const options = field.options ?? [];
10888
10928
  return /* @__PURE__ */ jsx(
@@ -15850,7 +15890,7 @@ var mdxKnowledgeNodes = [
15850
15890
  "title": "New Vertical Launch Playbook",
15851
15891
  "summary": "Zero-to-first-campaign workflow for launching a new acquisition vertical: batch definition, tracker setup, lead generation stages, campaign launch, and monitoring.",
15852
15892
  "icon": "playbook",
15853
- "body": '## Overview\n\nUse this playbook when launching a new acquisition vertical from zero to first campaign. A vertical launch turns an audience hypothesis such as independent dental practices, auto repair shops, or bookkeeping firms into a tracked lead generation batch, qualified contacts, a draft Instantly campaign, and an active monitoring loop.\n\nThe workflow has five phases:\n\n1. Define the batch and qualification rules.\n2. Create the batch tracker.\n3. Run the lead generation pipeline.\n4. Launch the outreach campaign.\n5. Monitor replies and campaign quality.\n\n## Define the Batch\n\nChoose a batch ID using the acquisition naming convention: `{vertical}-{number}`, for example `dental-1`, `auto-1`, or `home-1`.\n\nRecord the batch configuration in the tracker before running pipeline stages. At minimum, capture:\n\n- Target description, such as "independent dental practices in Orange County, California".\n- Search queries for the initial source pull.\n- Region: county, state, country, or other geography accepted by the scraper workflow.\n- Minimum review count and minimum rating, when Google Maps quality thresholds matter.\n- Custom disqualification rules, such as excluding chains, franchises, pediatric-only practices, or irrelevant subcategories.\n- Website crawl keywords, such as `about`, `team`, `staff`, `contact`, `services`, or vertical-specific service pages.\n\nUse `packages/elevasis-operations/src/sales/prospecting/constants.ts` as the batch registry. Current launch work should keep the tracker as the human-readable source and pass qualification criteria through the workflow input, list qualification metadata, or the registered batch config for the stage being run.\n\n## Create the Batch Tracker\n\nCreate a tracker from the acquisition batch template:\n\n```text\napps/docs/content/docs/operations/client-acquisition/outreach/batches/_template.mdx\n```\n\nPlace the new tracker in the pending batch directory:\n\n```text\napps/docs/content/docs/operations/client-acquisition/outreach/batches/pending/{batch-id}.mdx\n```\n\nFill in the frontmatter with `status: in-progress`, then complete the batch configuration table before running pipeline work. The tracker should make it possible to reconstruct the vertical, region, search inputs, disqualification rules, and campaign state without reading execution logs.\n\n## Run Lead Generation\n\nRun the lead generation stages with the platform CLI from the monorepo root so `.env.development` and `.env.production` resolve correctly.\n\n### Stage 01: Google Maps Scrape\n\nUse the Google Maps scrape workflow to acquire initial companies:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-01a-google-maps-scrape-workflow --input \'{"searchQueries":["dentist","dental clinic"],"county":"Orange County","state":"California"}\' --async\n```\n\nAfter the execution starts, record the execution ID and source counts in the batch tracker.\n\n### Local Website Crawl\n\nRun the local website crawler against the batch:\n\n```bash\npnpm -C scripts/web-scraper run crawl -- {batch-id}\n```\n\nThe crawl should capture relevant sub-pages for LLM extraction. If vertical-specific keywords are not available in the active code path, use the default crawl coverage and note any manual crawl gaps in the tracker before extraction.\n\n### Stage 02: Website Extract\n\nExtract structured company profile data from crawl output:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-02-website-extract-workflow --input \'{"batchId":"{batch-id}"}\' --async\n```\n\n### Stage 03: Company Qualification\n\nQualify companies using the target description, review thresholds, rating thresholds, and custom rules captured in the tracker. If the workflow does not resolve criteria automatically for the batch, pass the criteria explicitly in the input or attach them through the list qualification surface before running the stage.\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-03-company-qualification-workflow --input \'{"batchId":"{batch-id}","criteria":{"targetDescription":"Independent dental practices in Orange County, California","minimumReviewCount":5,"minimumRating":3,"customRules":"Disqualify franchises and chains. Disqualify orthodontics-only and pediatric-only practices."}}\' --async\n```\n\nFor list-oriented runs, use `listId` instead of `batchId`; list configuration takes priority over the batch registry unless an explicit `criteria` override is provided.\n\n### Stage 04: Email Discovery\n\nDiscover contacts for qualified companies:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-04-email-discovery-workflow --input \'{"batchId":"{batch-id}"}\' --async\n```\n\n### Stage 05: Email Verification\n\nVerify discovered emails before campaign upload:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-05-email-verification-workflow --input \'{"batchId":"{batch-id}"}\' --async\n```\n\nWhen verification completes, update the tracker with company counts, contact counts, usable email counts, and set the batch status to `ready` if campaign launch prerequisites are satisfied.\n\n## Launch the Campaign\n\nUse the acquisition outreach workflow to move a ready batch into Instantly:\n\n1. Check account inventory with `ist-account-inventory-workflow`.\n2. Personalize contacts with `ist-personalization-workflow`.\n3. Create a draft campaign with `ist-campaign-create-workflow` and `activate: false`.\n4. Create the tracking list with `ist-campaign-list-workflow`.\n5. Upload contacts with `ist-upload-workflow`, dry run first and then real.\n6. Activate with `ist-campaign-activate-workflow`.\n7. Update the tracker to `status: active` and fill in campaign metadata.\n\nKeep the first campaign small enough to evaluate copy and deliverability. Prefer 100-200 contacts per segment, 1-2 contacts per company, and conservative sending volume until benchmarks are visible.\n\n## Monitor and Optimize\n\nAfter launch, monitor both campaign metrics and inbound replies:\n\n- Use `/acquisition --outreach` for campaign review and analytics.\n- Use `/acquisition --inbound status` for reply handling and active deal state.\n- Watch open rate, reply rate, positive reply rate, and bounce rate.\n- Pause or repair the campaign if bounce rate rises above the accepted threshold.\n- Rework subject lines, personalization, or offer framing when reply rate is below target.\n\nEvery optimization pass should write back to the tracker: what changed, why it changed, and what result would justify scaling the vertical.\n\n## Launch Checklist\n\n- Batch ID selected with `{vertical}-{number}` naming.\n- Batch tracker created from the template.\n- Target description, geography, search queries, thresholds, and custom rules recorded.\n- Stage 01 scrape execution complete.\n- Website crawl complete or crawl gaps documented.\n- Stage 02 extraction complete.\n- Stage 03 qualification complete with explicit criteria source.\n- Stage 04 email discovery complete.\n- Stage 05 email verification complete.\n- Tracker status set to `ready`.\n- Draft Instantly campaign created.\n- Tracking list created and contacts uploaded.\n- Campaign activated.\n- Tracker status set to `active` with campaign metadata.',
15893
+ "body": '## Overview\n\nUse this playbook when launching a new acquisition vertical from zero to first campaign. A vertical launch turns an audience hypothesis such as independent dental practices, auto repair shops, or bookkeeping firms into a tracked lead generation batch, qualified contacts, a draft Instantly campaign, and an active monitoring loop.\n\nThe workflow has five phases:\n\n1. Define the batch and qualification rules.\n2. Create the batch tracker.\n3. Run the lead generation pipeline.\n4. Launch the outreach campaign.\n5. Monitor replies and campaign quality.\n\n## Define the Batch\n\nChoose a batch ID using the acquisition naming convention: `{vertical}-{number}`, for example `dental-1`, `auto-1`, or `home-1`.\n\nRecord the batch configuration in the tracker before running pipeline stages. At minimum, capture:\n\n- Target description, such as "independent dental practices in Orange County, California".\n- Search queries for the initial source pull.\n- Region: county, state, country, or other geography accepted by the scraper workflow.\n- Minimum review count and minimum rating, when Google Maps quality thresholds matter.\n- Custom disqualification rules, such as excluding chains, franchises, pediatric-only practices, or irrelevant subcategories.\n- Website crawl keywords, such as `about`, `team`, `staff`, `contact`, `services`, or vertical-specific service pages.\n\nUse `packages/elevasis-operations/src/sales/prospecting/constants.ts` as the batch registry. Current launch work should keep the tracker as the human-readable source and pass qualification criteria through the workflow input, list qualification metadata, or the registered batch config for the stage being run.\n\n## Create the Batch Tracker\n\nCreate a tracker from the acquisition batch template:\n\n```text\napps/docs/content/docs/operations/client-acquisition/outreach/batches/_template.mdx\n```\n\nPlace the new tracker in the pending batch directory:\n\n```text\napps/docs/content/docs/operations/client-acquisition/outreach/batches/pending/{batch-id}.mdx\n```\n\nFill in the frontmatter with `status: in-progress`, then complete the batch configuration table before running pipeline work. The tracker should make it possible to reconstruct the vertical, region, search inputs, disqualification rules, and campaign state without reading execution logs.\n\n## Run Lead Generation\n\nRun the lead generation stages with the platform CLI from the monorepo root so `.env.development` and `.env.production` resolve correctly.\n\n### Stage 01: Google Maps Scrape\n\nUse the Google Maps scrape workflow to acquire initial companies:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-01a-google-maps-scrape-workflow --input \'{"searchQueries":["dentist","dental clinic"],"county":"Orange County","state":"California"}\' --async\n```\n\nAfter the execution starts, record the execution ID and source counts in the batch tracker.\n\n### Local Website Crawl\n\nRun the local website crawler against the batch:\n\n```bash\npnpm -C scripts/web-scraper run crawl -- {batch-id}\n```\n\nThe crawl should capture relevant sub-pages for LLM extraction. If vertical-specific keywords are not available in the active code path, use the default crawl coverage and note any manual crawl gaps in the tracker before extraction.\n\n### Stage 02: Website Extract\n\nExtract structured company profile data from crawl output:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-02-website-extract-workflow --input \'{"batchId":"{batch-id}"}\' --async\n```\n\n### Stage 03: Company Qualification\n\nQualify companies using the target description, review thresholds, rating thresholds, and custom rules captured in the tracker. If the workflow does not resolve criteria automatically for the batch, pass the criteria explicitly in the input or attach them through the list qualification surface before running the stage.\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-03-company-qualification-workflow --input \'{"batchId":"{batch-id}","criteria":{"targetDescription":"Independent dental practices in Orange County, California","minimumReviewCount":5,"minimumRating":3,"customRules":"Disqualify franchises and chains. Disqualify orthodontics-only and pediatric-only practices."}}\' --async\n```\n\nFor list-oriented runs, use `listId` instead of `batchId`; list configuration takes priority over the batch registry unless an explicit `criteria` override is provided.\n\n### Stage 04: Email Discovery\n\nDiscover contacts for qualified companies:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-04-email-discovery-workflow --input \'{"batchId":"{batch-id}"}\' --async\n```\n\n### Stage 05: Email Verification\n\nVerify discovered emails before campaign upload:\n\n```bash\npnpm exec elevasis exec Elevasis/lgn-05-email-verification-workflow --input \'{"batchId":"{batch-id}"}\' --async\n```\n\nWhen verification completes, update the tracker with company counts, contact counts, usable email counts, and set the batch status to `ready` if campaign launch prerequisites are satisfied.\n\n## Launch the Campaign\n\nUse the acquisition outreach workflow to move a ready batch into Instantly:\n\n1. Check account inventory with `ist-account-inventory-workflow`.\n2. Personalize contacts with `ist-personalization-workflow`.\n3. Create a draft campaign with `ist-campaign-create-workflow` and `activate: false`.\n4. Create the tracking list with `ist-campaign-list-workflow`.\n5. Upload contacts with `ist-upload-contacts-workflow`, dry run first and then real.\n6. Activate with `ist-campaign-activate-workflow`.\n7. Update the tracker to `status: active` and fill in campaign metadata.\n\nKeep the first campaign small enough to evaluate copy and deliverability. Prefer 100-200 contacts per segment, 1-2 contacts per company, and conservative sending volume until benchmarks are visible.\n\n## Monitor and Optimize\n\nAfter launch, monitor both campaign metrics and inbound replies:\n\n- Use `/acquisition --outreach` for campaign review and analytics.\n- Use `/acquisition --inbound status` for reply handling and active deal state.\n- Watch open rate, reply rate, positive reply rate, and bounce rate.\n- Pause or repair the campaign if bounce rate rises above the accepted threshold.\n- Rework subject lines, personalization, or offer framing when reply rate is below target.\n\nEvery optimization pass should write back to the tracker: what changed, why it changed, and what result would justify scaling the vertical.\n\n## Launch Checklist\n\n- Batch ID selected with `{vertical}-{number}` naming.\n- Batch tracker created from the template.\n- Target description, geography, search queries, thresholds, and custom rules recorded.\n- Stage 01 scrape execution complete.\n- Website crawl complete or crawl gaps documented.\n- Stage 02 extraction complete.\n- Stage 03 qualification complete with explicit criteria source.\n- Stage 04 email discovery complete.\n- Stage 05 email verification complete.\n- Tracker status set to `ready`.\n- Draft Instantly campaign created.\n- Tracking list created and contacts uploaded.\n- Campaign activated.\n- Tracker status set to `active` with campaign metadata.',
15854
15894
  "links": [
15855
15895
  {
15856
15896
  "target": {
@@ -16411,7 +16451,7 @@ Advanced personalization doubles reply rates: ~18% vs ~9% for generic.
16411
16451
  icon: "playbook",
16412
16452
  title: "Lead-Gen Playbook",
16413
16453
  summary: "Empirical benchmarks, threshold guidance, provider economics, and codified learnings from 3 completed batches of the lead-gen pipeline.",
16414
- body: "## Performance Benchmarks\n\nPer-stage success rates across the 3 completed Orange County batches (vet-1, auto-1, home-1).\n\n### Stage 1: Scrape (Raw to Filtered)\n\n| Metric | vet-1 | auto-1 | home-1 | Benchmark |\n| --- | --- | --- | --- | --- |\n| Raw results | 480 | 800 | 1000 | -- |\n| Companies created | 393 | 566 | 701 | -- |\n| Active in DB | 322 | 428 | 640 | -- |\n\n### Stage 3: Company Qualification Rate\n\n| Metric | vet-1 | auto-1 | home-1 | Benchmark |\n| --- | --- | --- | --- | --- |\n| Qualified | 213 (76%) | 284 (79%) | 326 (60%) | 60-80% |\n| Disqualified | 66 (24%) | 74 (21%) | 222 (40%) | 20-40% |\n\n### Stage 5: Email Verification\n\nVALID rate: ~33-41% of discovered emails across batches. Target bounce rate \\<2%.\n\n## Model Selection\n\nUse Gemini Flash models for high-volume qualification steps (cost/quality balance). Use GPT for personalization where quality matters most.\n\n## Provider Economics\n\nTomba domain search provides the best cost-per-verified-contact for local SMBs. Dual-verify (Tomba + Mails.so) catches most false positives.\n\n## Pipeline Stages\n\n1. Scrape (Google Maps via Apify)\n2. LLM Extract (website crawl \xE2\u2020\u2019 structured data)\n3. Company Qualification (LLM ICP scoring)\n4. Email Discovery (Tomba domain search)\n5. Email Verification (Mails.so)\n6. Opening Line Generation (ist-personalization-workflow)\n7. Campaign Upload (ist-upload-workflow)",
16454
+ body: "## Performance Benchmarks\n\nPer-stage success rates across the 3 completed Orange County batches (vet-1, auto-1, home-1).\n\n### Stage 1: Scrape (Raw to Filtered)\n\n| Metric | vet-1 | auto-1 | home-1 | Benchmark |\n| --- | --- | --- | --- | --- |\n| Raw results | 480 | 800 | 1000 | -- |\n| Companies created | 393 | 566 | 701 | -- |\n| Active in DB | 322 | 428 | 640 | -- |\n\n### Stage 3: Company Qualification Rate\n\n| Metric | vet-1 | auto-1 | home-1 | Benchmark |\n| --- | --- | --- | --- | --- |\n| Qualified | 213 (76%) | 284 (79%) | 326 (60%) | 60-80% |\n| Disqualified | 66 (24%) | 74 (21%) | 222 (40%) | 20-40% |\n\n### Stage 5: Email Verification\n\nVALID rate: ~33-41% of discovered emails across batches. Target bounce rate \\<2%.\n\n## Model Selection\n\nUse Gemini Flash models for high-volume qualification steps (cost/quality balance). Use GPT for personalization where quality matters most.\n\n## Provider Economics\n\nTomba domain search provides the best cost-per-verified-contact for local SMBs. Dual-verify (Tomba + Mails.so) catches most false positives.\n\n## Pipeline Stages\n\n1. Scrape (Google Maps via Apify)\n2. LLM Extract (website crawl \xE2\u2020\u2019 structured data)\n3. Company Qualification (LLM ICP scoring)\n4. Email Discovery (Tomba domain search)\n5. Email Verification (Mails.so)\n6. Opening Line Generation (ist-personalization-workflow)\n7. Campaign Upload (ist-upload-contacts-workflow)",
16415
16455
  links: [{ nodeId: "system:sales.lead-gen" }],
16416
16456
  ownerIds: [],
16417
16457
  updatedAt: "2026-05-02"
@@ -18619,12 +18659,7 @@ var LEAD_GEN_ACTION_ENTRY_INPUTS = [
18619
18659
  knowledge: []
18620
18660
  }
18621
18661
  ];
18622
- var LEAD_GEN_ACTION_ENTRIES = Object.fromEntries(
18623
- LEAD_GEN_ACTION_ENTRY_INPUTS.map((action) => {
18624
- const parsed = ActionSchema.parse(action);
18625
- return [parsed.id, parsed];
18626
- })
18627
- );
18662
+ var LEAD_GEN_ACTION_ENTRIES = defineActions(LEAD_GEN_ACTION_ENTRY_INPUTS);
18628
18663
  var CRM_ACTION_ENTRY_INPUTS = [
18629
18664
  {
18630
18665
  id: "send_reply",
@@ -18735,12 +18770,7 @@ var CRM_ACTION_ENTRY_INPUTS = [
18735
18770
  knowledge: []
18736
18771
  }
18737
18772
  ];
18738
- var CRM_ACTION_ENTRIES = Object.fromEntries(
18739
- CRM_ACTION_ENTRY_INPUTS.map((action) => {
18740
- const parsed = ActionSchema.parse(action);
18741
- return [parsed.id, parsed];
18742
- })
18743
- );
18773
+ var CRM_ACTION_ENTRIES = defineActions(CRM_ACTION_ENTRY_INPUTS);
18744
18774
  var ELEVASIS_DEFAULT_ORGANIZATION_MODEL_ACTIONS = {
18745
18775
  ...LEAD_GEN_ACTION_ENTRIES,
18746
18776
  ...CRM_ACTION_ENTRIES
@@ -18867,7 +18897,7 @@ var platformSystems = {
18867
18897
  surfaces: []
18868
18898
  },
18869
18899
  config: {
18870
- defaultBuildTemplateId: "sales.lead-gen:catalog/build-template",
18900
+ defaultBuildTemplateId: "local-services",
18871
18901
  defaultCompanyStageCatalogId: "sales.lead-gen:catalog/company-stage",
18872
18902
  defaultContactStageCatalogId: "sales.lead-gen:catalog/contact-stage",
18873
18903
  listBoard: {
@@ -19972,92 +20002,6 @@ for (const resource2 of Object.values(canonicalOrganizationModel.resources)) {
19972
20002
  projectOrganizationSurfaces(canonicalOrganizationModel);
19973
20003
  ({
19974
20004
  ...canonicalOrganizationModel});
19975
- var PROSPECTING_STEPS2 = {
19976
- localServices: {
19977
- sourceCompanies: {
19978
- id: "source-companies",
19979
- label: "Companies found",
19980
- primaryEntity: "company",
19981
- outputs: ["company"],
19982
- stageKey: "populated",
19983
- dependencyMode: "per-record-eligibility",
19984
- actionKey: "lead-gen.company.source",
19985
- defaultBatchSize: 100,
19986
- maxBatchSize: 250
19987
- },
19988
- analyzeWebsites: {
19989
- id: "analyze-websites",
19990
- label: "Websites analyzed",
19991
- primaryEntity: "company",
19992
- outputs: ["company"],
19993
- stageKey: "extracted",
19994
- dependsOn: ["source-companies"],
19995
- dependencyMode: "per-record-eligibility",
19996
- actionKey: "lead-gen.company.website-extract",
19997
- defaultBatchSize: 50,
19998
- maxBatchSize: 100
19999
- },
20000
- qualifyCompanies: {
20001
- id: "qualify-companies",
20002
- label: "Companies qualified",
20003
- primaryEntity: "company",
20004
- outputs: ["company"],
20005
- stageKey: "qualified",
20006
- dependsOn: ["analyze-websites"],
20007
- dependencyMode: "per-record-eligibility",
20008
- actionKey: "lead-gen.company.qualify",
20009
- defaultBatchSize: 100,
20010
- maxBatchSize: 250
20011
- },
20012
- findContacts: {
20013
- id: "find-contacts",
20014
- label: "Decision-makers found",
20015
- primaryEntity: "contact",
20016
- outputs: ["contact"],
20017
- stageKey: "discovered",
20018
- dependsOn: ["qualify-companies"],
20019
- dependencyMode: "per-record-eligibility",
20020
- actionKey: "lead-gen.contact.discover",
20021
- defaultBatchSize: 50,
20022
- maxBatchSize: 100
20023
- },
20024
- verifyEmails: {
20025
- id: "verify-emails",
20026
- label: "Emails verified",
20027
- primaryEntity: "contact",
20028
- outputs: ["contact"],
20029
- stageKey: "verified",
20030
- dependsOn: ["find-contacts"],
20031
- dependencyMode: "per-record-eligibility",
20032
- actionKey: "lead-gen.contact.verify-email",
20033
- defaultBatchSize: 100,
20034
- maxBatchSize: 500
20035
- },
20036
- personalize: {
20037
- id: "personalize",
20038
- label: "Personalize",
20039
- primaryEntity: "contact",
20040
- outputs: ["contact"],
20041
- stageKey: "personalized",
20042
- dependsOn: ["verify-emails"],
20043
- dependencyMode: "per-record-eligibility",
20044
- actionKey: "lead-gen.contact.personalize",
20045
- defaultBatchSize: 25,
20046
- maxBatchSize: 100
20047
- },
20048
- review: {
20049
- id: "review",
20050
- label: "Reviewed and exported",
20051
- primaryEntity: "contact",
20052
- outputs: ["export"],
20053
- stageKey: "uploaded",
20054
- dependsOn: ["personalize"],
20055
- dependencyMode: "per-record-eligibility",
20056
- actionKey: "lead-gen.review.outreach-ready",
20057
- defaultBatchSize: 25,
20058
- maxBatchSize: 100
20059
- }
20060
- }};
20061
20005
 
20062
20006
  // ../elevasis-core/src/organization-model/sales.ts
20063
20007
  var CRM_DISCOVERY_REPLIED_STATE = {
@@ -20136,40 +20080,6 @@ var DEFAULT_CRM_PRIORITY_RULE_CONFIG = {
20136
20080
  },
20137
20081
  staleAfterDays: 14
20138
20082
  };
20139
- var PENDING_STATE = { stateKey: "pending", label: "Pending" };
20140
- var ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE = {
20141
- pipelineKey: "lead-gen",
20142
- label: "Lead Generation",
20143
- entityKey: "acq.list-member",
20144
- stages: [
20145
- {
20146
- stageKey: "outreach",
20147
- label: "Outreach",
20148
- states: [
20149
- PENDING_STATE,
20150
- { stateKey: "personalized", label: "Personalized" },
20151
- { stateKey: "uploaded", label: "Uploaded" },
20152
- { stateKey: "interested", label: "Interested" }
20153
- ]
20154
- },
20155
- {
20156
- stageKey: "prospecting",
20157
- label: "Prospecting",
20158
- states: [
20159
- PENDING_STATE,
20160
- { stateKey: "discovered", label: "Discovered" },
20161
- { stateKey: "verified", label: "Verified" }
20162
- ]
20163
- },
20164
- {
20165
- stageKey: "qualification",
20166
- label: "Qualification",
20167
- states: [PENDING_STATE]
20168
- }
20169
- ]
20170
- };
20171
- var LEAD_GEN_PIPELINE_DEFINITIONS = {
20172
- "acq.list-member": [ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE]};
20173
20083
 
20174
20084
  // src/lib/crm/pipeline.ts
20175
20085
  function getCrmStageOptions() {
@@ -21984,6 +21894,7 @@ function DealDetailPage({ dealId, renderActions, onDealLoaded }) {
21984
21894
  function CompanyDetailPage({ companyId }) {
21985
21895
  const navigate = useNavigate();
21986
21896
  const { data: company, isLoading, error } = useCompany(companyId);
21897
+ const [enrichmentView, setEnrichmentView] = useState("formatted");
21987
21898
  const title = company ? company.name : "Company Detail";
21988
21899
  const headerActions = /* @__PURE__ */ jsx(
21989
21900
  Button,
@@ -22101,8 +22012,22 @@ function CompanyDetailPage({ companyId }) {
22101
22012
  /* @__PURE__ */ jsx(Text, { size: "sm", style: { whiteSpace: "pre-wrap" }, children: company.verticalResearch })
22102
22013
  ] }) }),
22103
22014
  company.enrichmentData && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
22104
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Enrichment Data" }),
22105
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { whiteSpace: "pre-wrap" }, children: JSON.stringify(company.enrichmentData, null, 2) })
22015
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
22016
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Enrichment Data" }),
22017
+ /* @__PURE__ */ jsx(
22018
+ SegmentedControl,
22019
+ {
22020
+ value: enrichmentView,
22021
+ onChange: (v) => setEnrichmentView(v),
22022
+ size: "xs",
22023
+ data: [
22024
+ { label: "Formatted", value: "formatted" },
22025
+ { label: "JSON", value: "json" }
22026
+ ]
22027
+ }
22028
+ )
22029
+ ] }),
22030
+ enrichmentView === "formatted" ? /* @__PURE__ */ jsx(ContextViewer, { data: company.enrichmentData }) : /* @__PURE__ */ jsx(JsonViewer, { data: company.enrichmentData })
22106
22031
  ] }) })
22107
22032
  ] }) })
22108
22033
  ] }) });
@@ -22655,6 +22580,47 @@ function useDeleteLists() {
22655
22580
  }
22656
22581
  });
22657
22582
  }
22583
+ function actionMatchesExport(primaryAction) {
22584
+ return primaryAction === "lead-gen.export.list" || primaryAction?.endsWith(":action/export.list") === true || primaryAction?.endsWith(".export.list") === true;
22585
+ }
22586
+ function getLeadGenExportWorkflowId(model) {
22587
+ return Object.values(model.resources ?? {}).find(
22588
+ (resource2) => resource2.kind === "workflow" && resource2.systemPath === "sales.lead-gen" && (actionMatchesExport(resource2.ontology?.primaryAction) || resource2.ontology?.actions?.some(actionMatchesExport) === true)
22589
+ )?.id;
22590
+ }
22591
+ function toBuildStepDefinition(step) {
22592
+ return {
22593
+ ...step,
22594
+ description: step.description ?? `Run ${step.label.toLowerCase()} for this list.`,
22595
+ outputs: [...step.outputs],
22596
+ emptyBlockedText: step.emptyBlockedText ?? "Complete prerequisite build steps before this action can run."
22597
+ };
22598
+ }
22599
+ function getDefaultBuildTemplateId(model, templates) {
22600
+ const configuredId = getSystem(model, "sales.lead-gen")?.config?.defaultBuildTemplateId;
22601
+ if (typeof configuredId === "string" && templates.some((template) => template.id === configuredId)) return configuredId;
22602
+ return templates[0]?.id;
22603
+ }
22604
+ function getDefaultBuildSteps(templates, defaultBuildTemplateId) {
22605
+ const template = templates.find((item) => item.id === defaultBuildTemplateId) ?? templates[0];
22606
+ return template?.steps.map((step) => toBuildStepDefinition(step)) ?? [];
22607
+ }
22608
+ function useLeadGenConfig() {
22609
+ const systems = useOptionalElevasisSystems();
22610
+ const organizationModel2 = systems?.organizationModel;
22611
+ return useMemo(() => {
22612
+ if (!organizationModel2) return {};
22613
+ const buildTemplates = getAllBuildTemplates(organizationModel2);
22614
+ const defaultBuildTemplateId = getDefaultBuildTemplateId(organizationModel2, buildTemplates);
22615
+ return {
22616
+ stageCatalog: getLeadGenStageCatalog(organizationModel2),
22617
+ exportWorkflowId: getLeadGenExportWorkflowId(organizationModel2),
22618
+ defaultBuildTemplateId,
22619
+ defaultBuildSteps: getDefaultBuildSteps(buildTemplates, defaultBuildTemplateId),
22620
+ buildTemplates
22621
+ };
22622
+ }, [organizationModel2]);
22623
+ }
22658
22624
  function computeBacklog(current, completed) {
22659
22625
  return Math.max(current - completed, 0);
22660
22626
  }
@@ -22910,10 +22876,10 @@ function LeadGenOverviewPage() {
22910
22876
  ] }) }) });
22911
22877
  }
22912
22878
  var PAGE_SIZE_DEFAULT2 = 20;
22913
- var BUILD_TEMPLATE_SELECT_OPTIONS = PROSPECTING_BUILD_TEMPLATE_OPTIONS.map((template) => ({
22914
- value: template.id,
22915
- label: template.label
22916
- }));
22879
+ var DATA_MODE_OPTIONS = [
22880
+ { value: "mock", label: "Mock data" },
22881
+ { value: "live", label: "Live data" }
22882
+ ];
22917
22883
  var STATUS_FILTER_OPTIONS = [
22918
22884
  { value: "", label: "All statuses" },
22919
22885
  { value: "draft", label: "Draft" },
@@ -22924,12 +22890,22 @@ var STATUS_FILTER_OPTIONS = [
22924
22890
  ];
22925
22891
  function LeadGenListsPage() {
22926
22892
  const navigate = useNavigate();
22893
+ const leadGenConfig = useLeadGenConfig();
22894
+ const buildTemplateOptions = useMemo(
22895
+ () => getProspectingBuildTemplateOptions(leadGenConfig.buildTemplates ?? []).map((template) => ({
22896
+ value: template.id,
22897
+ label: template.label
22898
+ })),
22899
+ [leadGenConfig.buildTemplates]
22900
+ );
22901
+ const defaultBuildTemplateId = leadGenConfig.defaultBuildTemplateId ?? buildTemplateOptions[0]?.value ?? "";
22927
22902
  const [searchQuery, setSearchQuery] = useState("");
22928
22903
  const [statusFilter, setStatusFilter] = useState("");
22929
22904
  const [showCreateList, setShowCreateList] = useState(false);
22930
22905
  const [newListName, setNewListName] = useState("");
22931
22906
  const [newListDescription, setNewListDescription] = useState("");
22932
- const [newBuildTemplateId, setNewBuildTemplateId] = useState(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
22907
+ const [newBuildTemplateId, setNewBuildTemplateId] = useState("");
22908
+ const [newDataMode, setNewDataMode] = useState("mock");
22933
22909
  const [showBatchDelete, setShowBatchDelete] = useState(false);
22934
22910
  const { data: lists, isLoading: listsLoading } = useLists();
22935
22911
  const { data: telemetry, isLoading: telemetryLoading } = useListsTelemetry();
@@ -22970,11 +22946,13 @@ function LeadGenListsPage() {
22970
22946
  [sortedLists, pagination.offset]
22971
22947
  );
22972
22948
  const selection = useTableSelection(paginatedLists, sortedLists);
22973
- const selectedBuildTemplate = PROSPECTING_BUILD_TEMPLATE_OPTIONS.find((template) => template.id === newBuildTemplateId);
22949
+ const selectedBuildTemplateId = newBuildTemplateId || defaultBuildTemplateId;
22950
+ const selectedBuildTemplate = leadGenConfig.buildTemplates?.find((template) => template.id === selectedBuildTemplateId);
22974
22951
  function resetCreateListModal() {
22975
22952
  setNewListName("");
22976
22953
  setNewListDescription("");
22977
- setNewBuildTemplateId(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
22954
+ setNewBuildTemplateId("");
22955
+ setNewDataMode("mock");
22978
22956
  setShowCreateList(false);
22979
22957
  }
22980
22958
  function handleCreateList() {
@@ -22983,7 +22961,10 @@ function LeadGenListsPage() {
22983
22961
  const body = {
22984
22962
  name: trimmedName,
22985
22963
  description: newListDescription.trim() || null,
22986
- buildTemplateId: newBuildTemplateId
22964
+ ...selectedBuildTemplateId ? { buildTemplateId: selectedBuildTemplateId } : {},
22965
+ pipelineConfig: {
22966
+ dataMode: newDataMode
22967
+ }
22987
22968
  };
22988
22969
  createListMutation.mutate(body, {
22989
22970
  onSuccess: (list) => {
@@ -23180,15 +23161,27 @@ function LeadGenListsPage() {
23180
23161
  {
23181
23162
  label: "Build Pipeline",
23182
23163
  description: "Choose the sequence this list should follow.",
23183
- data: BUILD_TEMPLATE_SELECT_OPTIONS,
23184
- value: newBuildTemplateId,
23185
- onChange: (value) => setNewBuildTemplateId(value ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID),
23186
- disabled: createListMutation.isPending,
23164
+ data: buildTemplateOptions,
23165
+ value: selectedBuildTemplateId || null,
23166
+ onChange: (value) => setNewBuildTemplateId(value ?? ""),
23167
+ disabled: createListMutation.isPending || buildTemplateOptions.length === 0,
23187
23168
  required: true
23188
23169
  }
23189
23170
  ),
23190
23171
  selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
23191
23172
  ] }),
23173
+ /* @__PURE__ */ jsx(
23174
+ Select,
23175
+ {
23176
+ label: "Data Mode",
23177
+ description: "Choose whether sourcing and enrichment use demo fixtures or live providers.",
23178
+ data: DATA_MODE_OPTIONS,
23179
+ value: newDataMode,
23180
+ onChange: (value) => setNewDataMode(value ?? "mock"),
23181
+ disabled: createListMutation.isPending,
23182
+ required: true
23183
+ }
23184
+ ),
23192
23185
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "The list will open in draft mode after creation so you can review configuration and run build steps from the list workspace." }),
23193
23186
  /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
23194
23187
  /* @__PURE__ */ jsx(Button, { variant: "light", onClick: resetCreateListModal, disabled: createListMutation.isPending, children: "Cancel" }),
@@ -23255,20 +23248,14 @@ function LeadGenListsPage() {
23255
23248
  }
23256
23249
 
23257
23250
  // src/lib/lead-gen/legacy-pipeline.ts
23258
- function resolveLegacyListMemberPipelineLabel(member) {
23259
- const defs = LEAD_GEN_PIPELINE_DEFINITIONS["acq.list-member"];
23260
- if (!defs) return null;
23261
- const pipeline = findPipeline(defs, member.pipelineKey);
23262
- if (!pipeline) return null;
23263
- const stage = pipeline.stages.find((s) => s.stageKey === member.stageKey);
23251
+ function resolveLegacyListMemberPipelineLabel(member, stageCatalog = {}) {
23252
+ const stage = stageCatalog[member.stageKey];
23264
23253
  if (!stage) return null;
23265
- const state = stage.states.find((s) => s.stateKey === member.stateKey);
23266
23254
  return {
23267
23255
  stageLabel: stage.label,
23268
- stateLabel: state?.label ?? member.stateKey
23256
+ stateLabel: stageCatalog[member.stateKey]?.label ?? member.stateKey
23269
23257
  };
23270
23258
  }
23271
- var LEAD_GEN_STAGE_CATALOG = getLeadGenStageCatalog(canonicalOrganizationModel);
23272
23259
  function formatDateTime2(value) {
23273
23260
  if (!value) return "Not yet";
23274
23261
  return new Date(value).toLocaleString("en-US", {
@@ -23279,8 +23266,8 @@ function formatDateTime2(value) {
23279
23266
  minute: "2-digit"
23280
23267
  });
23281
23268
  }
23282
- function getMemberStateColor(stateKey) {
23283
- const stage = LEAD_GEN_STAGE_CATALOG[stateKey];
23269
+ function getMemberStateColor(stateKey, stageCatalog) {
23270
+ const stage = stageCatalog[stateKey];
23284
23271
  if (stage?.entity === "contact") return "green";
23285
23272
  if (stage?.entity === "company") return "blue";
23286
23273
  return "gray";
@@ -23312,6 +23299,7 @@ function ArtifactsPanel({ listMemberId }) {
23312
23299
  ] }) }, artifact.id)) });
23313
23300
  }
23314
23301
  function ListMemberDrawerContent({ member, listId }) {
23302
+ const { stageCatalog = {} } = useLeadGenConfig();
23315
23303
  const transitionMember = useTransitionListMember();
23316
23304
  const statefulItem = useMemo(
23317
23305
  () => ({
@@ -23324,15 +23312,15 @@ function ListMemberDrawerContent({ member, listId }) {
23324
23312
  [member]
23325
23313
  );
23326
23314
  const derivedActions = useDeriveActions(statefulItem);
23327
- const resolved = resolveLegacyListMemberPipelineLabel(member);
23315
+ const resolved = resolveLegacyListMemberPipelineLabel(member, stageCatalog);
23328
23316
  const contactName = [member.contact?.firstName, member.contact?.lastName].filter(Boolean).join(" ").trim() || member.contact?.email || "\u2014";
23329
23317
  return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
23330
23318
  /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
23331
23319
  /* @__PURE__ */ jsx(Group, { gap: "xs", align: "center", children: resolved ? /* @__PURE__ */ jsxs(Fragment, { children: [
23332
23320
  /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "blue", children: resolved.stageLabel }),
23333
23321
  /* @__PURE__ */ jsx(IconChevronRight, { size: 14, style: { color: "var(--color-text-dimmed)" } }),
23334
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getMemberStateColor(member.stateKey), children: resolved.stateLabel })
23335
- ] }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getMemberStateColor(member.stateKey), children: member.stateKey }) }),
23322
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getMemberStateColor(member.stateKey, stageCatalog), children: resolved.stateLabel })
23323
+ ] }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getMemberStateColor(member.stateKey, stageCatalog), children: member.stateKey }) }),
23336
23324
  /* @__PURE__ */ jsx(Title, { order: 4, children: contactName }),
23337
23325
  member.contact?.email && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
23338
23326
  /* @__PURE__ */ jsx(IconMail, { size: 14, style: { color: "var(--color-text-dimmed)" } }),
@@ -23415,257 +23403,11 @@ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
23415
23403
  }
23416
23404
  );
23417
23405
  }
23418
- var LEAD_GEN_STAGE_CATALOG2 = getLeadGenStageCatalog(canonicalOrganizationModel);
23419
- function formatEventTime(timestamp) {
23420
- return new Date(timestamp).toLocaleTimeString("en-US", {
23421
- hour12: false,
23422
- hour: "2-digit",
23423
- minute: "2-digit",
23424
- second: "2-digit"
23425
- });
23426
- }
23427
- function formatId(executionId) {
23428
- return executionId.length > 10 ? `${executionId.slice(0, 8)}...` : executionId;
23429
- }
23430
- function getLogLevelColor(level) {
23431
- switch (level) {
23432
- case "error":
23433
- return "red";
23434
- case "warn":
23435
- return "yellow";
23436
- case "debug":
23437
- return "gray";
23438
- default:
23439
- return "blue";
23440
- }
23441
- }
23442
- function getStepStatusColor(status) {
23443
- switch (status) {
23444
- case "failed":
23445
- return "red";
23446
- case "done":
23447
- return "green";
23448
- default:
23449
- return "blue";
23450
- }
23451
- }
23452
- function getStepStatusLabel(status) {
23453
- switch (status) {
23454
- case "failed":
23455
- return "Failed";
23456
- case "done":
23457
- return "Done";
23458
- default:
23459
- return "Running";
23460
- }
23461
- }
23462
- function getEventColor(event) {
23463
- switch (event.type) {
23464
- case "new-execution":
23465
- return "blue";
23466
- case "execution-complete":
23467
- return event.data.success ? "green" : "red";
23468
- case "log":
23469
- return getLogLevelColor(event.data.log.level);
23470
- default:
23471
- return "gray";
23472
- }
23473
- }
23474
- function getEventLabel(event) {
23475
- switch (event.type) {
23476
- case "new-execution":
23477
- return "Execution started";
23478
- case "execution-complete":
23479
- return event.data.success ? "Execution completed" : "Execution failed";
23480
- case "log":
23481
- return event.data.log.message;
23482
- default:
23483
- return "Stream connected";
23484
- }
23485
- }
23486
- function hasExecutionId(event) {
23487
- return event.type !== "connected" && typeof event.executionId === "string" && event.executionId.length > 0;
23488
- }
23489
- function toTimelineEvents(events) {
23490
- return events.filter(hasExecutionId).map((event) => ({
23491
- executionId: event.executionId,
23492
- timestamp: event.timestamp,
23493
- label: getEventLabel(event),
23494
- color: getEventColor(event),
23495
- detail: event.type === "execution-complete" ? event.data.error : void 0
23496
- })).sort((a, b) => b.timestamp - a.timestamp).slice(0, 5);
23497
- }
23498
- function collectStepStatuses(events, streamingLogs) {
23499
- const statuses = /* @__PURE__ */ new Map();
23500
- const applyLog = (executionId, log) => {
23501
- const context = log.context;
23502
- if (!context || context.type !== "workflow") return;
23503
- if (!("stepId" in context)) return;
23504
- let status = null;
23505
- if (isStepStartedContext(context)) status = "running";
23506
- if (isStepCompletedContext(context)) status = "done";
23507
- if (isStepFailedContext(context)) status = "failed";
23508
- if (!status) return;
23509
- statuses.set(executionId, {
23510
- executionId,
23511
- stepId: context.stepId,
23512
- status,
23513
- timestamp: log.timestamp,
23514
- message: log.message
23515
- });
23516
- };
23517
- events.forEach((event) => {
23518
- if (event.type === "log") applyLog(event.executionId, event.data.log);
23519
- });
23520
- streamingLogs.forEach((logs, executionId) => {
23521
- logs.forEach((log) => applyLog(executionId, log));
23522
- });
23523
- return Array.from(statuses.values()).sort((a, b) => b.timestamp - a.timestamp);
23524
- }
23525
- function getExecutionId(execution) {
23526
- return "executionId" in execution ? execution.executionId : execution.id;
23527
- }
23528
- function getInFlightExecutions(data) {
23529
- if (Array.isArray(data)) return data;
23530
- if (data && typeof data === "object" && "executions" in data) {
23531
- const executions = data.executions;
23532
- return Array.isArray(executions) ? executions : [];
23533
- }
23534
- return [];
23535
- }
23536
- function getStageProgress(progress, stageKey) {
23537
- return progress?.byCompanyStage[stageKey] ?? progress?.byContactStage[stageKey];
23538
- }
23539
- function getStageLabel(stageKey) {
23540
- return LEAD_GEN_STAGE_CATALOG2[stageKey]?.label ?? stageKey;
23541
- }
23542
- function getDoneCount(stageProgress) {
23543
- return stageProgress?.attempted ?? 0;
23544
- }
23545
- function LeadGenLiveStatusPanel({
23546
- listId,
23547
- resourceId,
23548
- stageKey,
23549
- stepLabel,
23550
- enabled = true,
23551
- onRunningChange
23552
- }) {
23553
- const sse = useExecutionSSE(resourceId, {
23554
- enabled: enabled && Boolean(resourceId),
23555
- listId
23556
- });
23557
- const inFlightQuery = useInFlightExecutions(resourceId, {
23558
- enabled: enabled && Boolean(resourceId),
23559
- limit: 5,
23560
- refetchInterval: enabled && Boolean(resourceId) ? 2e3 : false
23561
- });
23562
- const inFlightExecutions = useMemo(() => getInFlightExecutions(inFlightQuery.data), [inFlightQuery.data]);
23563
- const activeExecutionIds = useMemo(() => {
23564
- const ids = new Set(sse.liveExecutions);
23565
- inFlightExecutions.forEach((execution) => {
23566
- const executionId = getExecutionId(execution);
23567
- if (executionId) ids.add(executionId);
23568
- });
23569
- return Array.from(ids);
23570
- }, [inFlightExecutions, sse.liveExecutions]);
23571
- const running = enabled && activeExecutionIds.length > 0;
23572
- const [showCompletedActivity, setShowCompletedActivity] = useState(false);
23573
- const progressQuery = useListProgress(listId, {
23574
- enabled: enabled && Boolean(listId),
23575
- refetchInterval: running ? 2e3 : false
23576
- });
23577
- useEffect(() => {
23578
- onRunningChange?.(running);
23579
- }, [onRunningChange, running]);
23580
- const stepStatuses = useMemo(
23581
- () => collectStepStatuses(sse.events, sse.streamingLogs),
23582
- [sse.events, sse.streamingLogs]
23583
- );
23584
- const latestStepStatus = stepStatuses[0];
23585
- const lastEvents = useMemo(() => toTimelineEvents(sse.events), [sse.events]);
23586
- const stageProgress = getStageProgress(progressQuery.data, stageKey);
23587
- const doneCount = getDoneCount(stageProgress);
23588
- const totalCount = stageProgress?.total ?? 0;
23589
- const failed = Boolean(sse.error) || sse.events.some((event) => event.type === "execution-complete" && !event.data.success);
23590
- const latestEvent = sse.latestEvent;
23591
- useEffect(() => {
23592
- if (running || failed || sse.error) {
23593
- setShowCompletedActivity(true);
23594
- return;
23595
- }
23596
- if (latestEvent?.type !== "execution-complete") return;
23597
- setShowCompletedActivity(true);
23598
- const timeoutId = window.setTimeout(() => setShowCompletedActivity(false), 1e4);
23599
- return () => window.clearTimeout(timeoutId);
23600
- }, [failed, latestEvent, running, sse.error]);
23601
- const hasActivity = activeExecutionIds.length > 0 || sse.streamingLogs.size > 0 || showCompletedActivity || Boolean(sse.error);
23602
- if (!enabled || !hasActivity && !sse.error && !failed) {
23603
- return null;
23604
- }
23605
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
23606
- /* @__PURE__ */ jsx(
23607
- CardHeader,
23608
- {
23609
- icon: /* @__PURE__ */ jsx(IconTerminal2, { size: 16 }),
23610
- title: stepLabel ? `${stepLabel} status` : "Live status",
23611
- rightSection: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
23612
- /* @__PURE__ */ jsx(
23613
- Badge,
23614
- {
23615
- size: "xs",
23616
- variant: "light",
23617
- color: sse.connected ? "green" : running ? "yellow" : "gray",
23618
- leftSection: sse.connected ? /* @__PURE__ */ jsx(IconCircleCheck, { size: 9 }) : /* @__PURE__ */ jsx(IconCircleDot, { size: 9 }),
23619
- children: sse.connected ? "Connected" : running ? "Reconnecting" : "Idle"
23620
- }
23621
- ),
23622
- running ? /* @__PURE__ */ jsxs(Badge, { size: "xs", variant: "filled", color: "blue", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 9 }), children: [
23623
- activeExecutionIds.length,
23624
- " running"
23625
- ] }) : null
23626
- ] })
23627
- }
23628
- ),
23629
- sse.error ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 14 }), color: "red", variant: "light", p: "xs", children: /* @__PURE__ */ jsx(Text, { size: "xs", children: sse.error }) }) : null,
23630
- activeExecutionIds.length > 0 ? /* @__PURE__ */ jsx(Group, { gap: 6, children: activeExecutionIds.map((executionId) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "blue", ff: "monospace", children: formatId(executionId) }, executionId)) }) : null,
23631
- /* @__PURE__ */ jsxs(Box, { p: "xs", bg: "var(--surface-primary-subtle)", style: { borderRadius: "var(--mantine-radius-sm)" }, children: [
23632
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
23633
- /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", style: { minWidth: 0 }, children: [
23634
- /* @__PURE__ */ jsx(IconProgressCheck, { size: 14 }),
23635
- /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, truncate: "end", children: getStageLabel(stageKey) })
23636
- ] }),
23637
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", ff: "monospace", children: [
23638
- doneCount,
23639
- "/",
23640
- totalCount,
23641
- " done"
23642
- ] })
23643
- ] }),
23644
- /* @__PURE__ */ jsx(
23645
- Progress,
23646
- {
23647
- mt: 6,
23648
- size: "xs",
23649
- value: totalCount > 0 ? doneCount / totalCount * 100 : 0,
23650
- color: stageProgress && stageProgress.error > 0 ? "red" : "blue"
23651
- }
23652
- )
23653
- ] }),
23654
- latestStepStatus ? /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
23655
- /* @__PURE__ */ jsx(Badge, { size: "xs", color: getStepStatusColor(latestStepStatus.status), variant: "light", children: getStepStatusLabel(latestStepStatus.status) }),
23656
- /* @__PURE__ */ jsx(Text, { size: "xs", fw: 500, truncate: "end", children: latestStepStatus.stepId }),
23657
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", truncate: "end", style: { flex: 1 }, children: latestStepStatus.message })
23658
- ] }) : running ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Waiting for workflow step events." }) : null,
23659
- lastEvents.length > 0 ? /* @__PURE__ */ jsx(Stack, { gap: 4, children: lastEvents.map((event) => /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
23660
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "dot", color: event.color, miw: 68, children: formatEventTime(event.timestamp) }),
23661
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", miw: 62, children: formatId(event.executionId) }),
23662
- /* @__PURE__ */ jsx(Text, { size: "xs", truncate: "end", style: { flex: 1 }, children: event.detail ?? event.label })
23663
- ] }, `${event.executionId}-${event.timestamp}-${event.label}`)) }) : null
23664
- ] }) });
23665
- }
23666
23406
  var panelStyle = {
23667
23407
  flex: 1,
23668
23408
  minHeight: 0,
23409
+ minWidth: 0,
23410
+ overflowX: "hidden",
23669
23411
  overflowY: "auto",
23670
23412
  paddingTop: "var(--mantine-spacing-sm)"
23671
23413
  };
@@ -23684,7 +23426,8 @@ function StepDetailRightColumn({
23684
23426
  gap: "sm",
23685
23427
  style: {
23686
23428
  flex: 1,
23687
- minHeight: 0
23429
+ minHeight: 0,
23430
+ minWidth: 0
23688
23431
  },
23689
23432
  children: [
23690
23433
  /* @__PURE__ */ jsxs(
@@ -23699,6 +23442,7 @@ function StepDetailRightColumn({
23699
23442
  style: {
23700
23443
  flex: 1,
23701
23444
  minHeight: 0,
23445
+ minWidth: 0,
23702
23446
  display: "flex",
23703
23447
  flexDirection: "column"
23704
23448
  },
@@ -23716,590 +23460,11 @@ function StepDetailRightColumn({
23716
23460
  ]
23717
23461
  }
23718
23462
  ),
23719
- /* @__PURE__ */ jsx(Stack, { gap: "xs", style: { flexShrink: 0 }, children: action })
23463
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", style: { flexShrink: 0, minWidth: 0, overflowX: "hidden" }, children: action })
23720
23464
  ]
23721
23465
  }
23722
23466
  );
23723
23467
  }
23724
- function ListBuilderIndexPage() {
23725
- const navigate = useNavigate();
23726
- const [query, setQuery] = useState("");
23727
- const listsQuery = useLists();
23728
- const lists = listsQuery.data ?? [];
23729
- const filteredLists = useMemo(() => {
23730
- const normalized = query.trim().toLowerCase();
23731
- if (!normalized) return lists;
23732
- return lists.filter((list) => list.name.toLowerCase().includes(normalized));
23733
- }, [lists, query]);
23734
- const openList = (listId) => {
23735
- navigate({ to: "/lead-gen/list-builder/$listId", params: { listId } });
23736
- };
23737
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
23738
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Choose a list to run lead-gen workflows and monitor progress." }),
23739
- /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
23740
- /* @__PURE__ */ jsx(
23741
- TextInput,
23742
- {
23743
- placeholder: "Search lists...",
23744
- leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
23745
- value: query,
23746
- onChange: (event) => setQuery(event.currentTarget.value)
23747
- }
23748
- ),
23749
- listsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !filteredLists.length ? /* @__PURE__ */ jsx(
23750
- EmptyState,
23751
- {
23752
- icon: IconLayoutDashboard,
23753
- title: query.trim() ? "No lists match your search" : "No lists available",
23754
- description: query.trim() ? void 0 : "Create a list first, then open it in the builder."
23755
- }
23756
- ) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
23757
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
23758
- /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
23759
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
23760
- /* @__PURE__ */ jsx(Table.Th, { children: "Batches" }),
23761
- /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
23762
- ] }) }),
23763
- /* @__PURE__ */ jsx(Table.Tbody, { children: filteredLists.map((list) => /* @__PURE__ */ jsxs(
23764
- Table.Tr,
23765
- {
23766
- role: "button",
23767
- tabIndex: 0,
23768
- onClick: () => openList(list.id),
23769
- onKeyDown: (event) => {
23770
- if (event.key === "Enter" || event.key === " ") {
23771
- event.preventDefault();
23772
- openList(list.id);
23773
- }
23774
- },
23775
- style: { cursor: "pointer" },
23776
- children: [
23777
- /* @__PURE__ */ jsxs(Table.Td, { children: [
23778
- /* @__PURE__ */ jsx(Text, { fw: 500, children: list.name }),
23779
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.description ?? list.id })
23780
- ] }),
23781
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStateKeyColor(list.status), children: list.status }) }),
23782
- /* @__PURE__ */ jsx(Table.Td, { children: list.batchIds.length }),
23783
- /* @__PURE__ */ jsx(Table.Td, { children: formatDate3(list.createdAt) })
23784
- ]
23785
- },
23786
- list.id
23787
- )) })
23788
- ] })
23789
- ] }) })
23790
- ] }) }) });
23791
- }
23792
-
23793
- // src/lib/lead-gen/stage-colors.ts
23794
- var FALLBACK_STAGE_COLORS = {
23795
- accent: "var(--color-text-dimmed)",
23796
- background: "color-mix(in srgb, var(--color-text-dimmed) 10%, transparent)",
23797
- border: "color-mix(in srgb, var(--color-border) 85%, var(--color-text-dimmed))",
23798
- text: "var(--color-text)"
23799
- };
23800
- var STAGE_COLOR_SETS = {
23801
- scraped: {
23802
- accent: "var(--color-text-subtle)",
23803
- background: "color-mix(in srgb, var(--color-text-subtle) 12%, transparent)",
23804
- border: "color-mix(in srgb, var(--color-border) 80%, var(--color-text-subtle))",
23805
- text: "var(--color-text)"
23806
- },
23807
- populated: {
23808
- accent: "var(--color-primary)",
23809
- background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
23810
- border: "color-mix(in srgb, var(--color-border) 70%, var(--color-primary))",
23811
- text: "var(--color-text)"
23812
- },
23813
- crawled: {
23814
- accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-text-subtle))",
23815
- background: "color-mix(in srgb, var(--color-primary) 9%, transparent)",
23816
- border: "color-mix(in srgb, var(--color-border) 72%, var(--color-primary))",
23817
- text: "var(--color-text)"
23818
- },
23819
- extracted: {
23820
- accent: "color-mix(in srgb, var(--color-primary) 78%, var(--color-success))",
23821
- background: "color-mix(in srgb, var(--color-primary) 10%, transparent)",
23822
- border: "color-mix(in srgb, var(--color-border) 65%, var(--color-primary))",
23823
- text: "var(--color-text)"
23824
- },
23825
- enriched: {
23826
- accent: "color-mix(in srgb, var(--color-success) 70%, var(--color-primary))",
23827
- background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
23828
- border: "color-mix(in srgb, var(--color-border) 68%, var(--color-success))",
23829
- text: "var(--color-text)"
23830
- },
23831
- "decision-makers-enriched": {
23832
- accent: "color-mix(in srgb, var(--color-primary) 66%, var(--color-warning))",
23833
- background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
23834
- border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
23835
- text: "var(--color-text)"
23836
- },
23837
- discovered: {
23838
- accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-warning))",
23839
- background: "color-mix(in srgb, var(--color-warning) 10%, transparent)",
23840
- border: "color-mix(in srgb, var(--color-border) 68%, var(--color-warning))",
23841
- text: "var(--color-text)"
23842
- },
23843
- verified: {
23844
- accent: "var(--color-success)",
23845
- background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
23846
- border: "color-mix(in srgb, var(--color-border) 64%, var(--color-success))",
23847
- text: "var(--color-text)"
23848
- },
23849
- qualified: {
23850
- accent: "color-mix(in srgb, var(--color-success) 82%, var(--color-text))",
23851
- background: "color-mix(in srgb, var(--color-success) 14%, transparent)",
23852
- border: "color-mix(in srgb, var(--color-border) 62%, var(--color-success))",
23853
- text: "var(--color-text)"
23854
- },
23855
- personalized: {
23856
- accent: "color-mix(in srgb, var(--color-primary) 58%, var(--color-success))",
23857
- background: "color-mix(in srgb, var(--color-primary) 11%, transparent)",
23858
- border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
23859
- text: "var(--color-text)"
23860
- },
23861
- uploaded: {
23862
- accent: "color-mix(in srgb, var(--color-warning) 72%, var(--color-primary))",
23863
- background: "color-mix(in srgb, var(--color-warning) 12%, transparent)",
23864
- border: "color-mix(in srgb, var(--color-border) 64%, var(--color-warning))",
23865
- text: "var(--color-text)"
23866
- },
23867
- interested: {
23868
- accent: "color-mix(in srgb, var(--color-success) 72%, var(--color-warning))",
23869
- background: "color-mix(in srgb, var(--color-success) 15%, transparent)",
23870
- border: "color-mix(in srgb, var(--color-border) 58%, var(--color-success))",
23871
- text: "var(--color-text)"
23872
- },
23873
- disqualified: {
23874
- accent: "var(--color-error)",
23875
- background: "color-mix(in srgb, var(--color-error) 10%, transparent)",
23876
- border: "color-mix(in srgb, var(--color-border) 66%, var(--color-error))",
23877
- text: "var(--color-text)"
23878
- },
23879
- invalid: {
23880
- accent: "color-mix(in srgb, var(--color-error) 72%, var(--color-text-dimmed))",
23881
- background: "color-mix(in srgb, var(--color-error) 8%, transparent)",
23882
- border: "color-mix(in srgb, var(--color-border) 78%, var(--color-error))",
23883
- text: "var(--color-text)"
23884
- }
23885
- };
23886
- function getLeadGenStageColor(stageKey) {
23887
- return STAGE_COLOR_SETS[stageKey] ?? FALLBACK_STAGE_COLORS;
23888
- }
23889
- function getLeadGenStageColorVar(stageKey, tone = "accent") {
23890
- return getLeadGenStageColor(stageKey)[tone];
23891
- }
23892
- var FALLBACK_STAGE_ORDER = 1e4;
23893
- var LEAD_GEN_STAGE_CATALOG3 = getLeadGenStageCatalog(canonicalOrganizationModel);
23894
- function unique(values) {
23895
- return Array.from(new Set(values.filter(Boolean)));
23896
- }
23897
- function configuredStages(pipelineConfig) {
23898
- return (pipelineConfig?.stages ?? []).filter((stage) => stage.enabled !== false).slice().sort((a, b) => {
23899
- const aOrder = a.order ?? LEAD_GEN_STAGE_CATALOG3[a.key]?.order ?? FALLBACK_STAGE_ORDER;
23900
- const bOrder = b.order ?? LEAD_GEN_STAGE_CATALOG3[b.key]?.order ?? FALLBACK_STAGE_ORDER;
23901
- return aOrder - bOrder || a.key.localeCompare(b.key);
23902
- });
23903
- }
23904
- function workflowStageKeys(actions) {
23905
- return unique((actions ?? []).flatMap((action) => [...action.stagesAffected]));
23906
- }
23907
- function sourceFor(stageKey, observedKeys, configKeys, workflowKeys) {
23908
- if (observedKeys.has(stageKey)) return "activity";
23909
- if (configKeys.has(stageKey)) return "config";
23910
- if (workflowKeys.has(stageKey)) return "workflow";
23911
- return "activity";
23912
- }
23913
- function sortStageNodes(nodes) {
23914
- return nodes.slice().sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));
23915
- }
23916
- function buildLaneStages({
23917
- entity,
23918
- progress,
23919
- pipelineConfig,
23920
- actions
23921
- }) {
23922
- const stageMap = entity === "company" ? progress.byCompanyStage : progress.byContactStage;
23923
- const observedKeys = new Set(Object.keys(stageMap));
23924
- const configStages = configuredStages(pipelineConfig);
23925
- const configStageByKey = new Map(configStages.map((stage) => [stage.key, stage]));
23926
- const configKeys = new Set(configStages.map((stage) => stage.key));
23927
- const workflowKeys = new Set(workflowStageKeys(actions));
23928
- const stageKeys = unique([...observedKeys, ...configKeys, ...workflowKeys]);
23929
- return sortStageNodes(
23930
- stageKeys.flatMap((key) => {
23931
- const catalogEntry = LEAD_GEN_STAGE_CATALOG3[key];
23932
- const inferredEntity = catalogEntry?.entity ?? (observedKeys.has(key) ? entity : null);
23933
- if (inferredEntity !== entity) return [];
23934
- return [
23935
- {
23936
- key,
23937
- label: configStageByKey.get(key)?.label ?? catalogEntry?.label ?? key,
23938
- order: configStageByKey.get(key)?.order ?? catalogEntry?.order ?? FALLBACK_STAGE_ORDER,
23939
- entity,
23940
- source: sourceFor(key, observedKeys, configKeys, workflowKeys)
23941
- }
23942
- ];
23943
- })
23944
- );
23945
- }
23946
- function getStageProgress2(progress, stageKey, entity) {
23947
- const stageProgress = entity === "company" ? progress.byCompanyStage[stageKey] : progress.byContactStage[stageKey];
23948
- const total = stageProgress?.total ?? (entity === "company" ? progress.totalCompanies : progress.totalMembers);
23949
- return {
23950
- total,
23951
- attempted: stageProgress?.attempted ?? 0,
23952
- success: stageProgress?.success ?? 0,
23953
- noResult: stageProgress?.noResult ?? 0,
23954
- skipped: stageProgress?.skipped ?? 0,
23955
- error: stageProgress?.error ?? 0,
23956
- other: stageProgress?.other ?? 0,
23957
- notAttempted: stageProgress?.notAttempted ?? total
23958
- };
23959
- }
23960
- function percent(value, total) {
23961
- if (total <= 0) return 0;
23962
- return Math.max(0, Math.min(100, value / total * 100));
23963
- }
23964
- function SourceBadge({ source }) {
23965
- const label = source === "activity" ? "recorded" : source;
23966
- const color = source === "activity" ? "green" : source === "config" ? "blue" : "gray";
23967
- return /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color, children: label });
23968
- }
23969
- function ProgressRail({ counts, stageKey }) {
23970
- const accent = getLeadGenStageColorVar(stageKey);
23971
- const segments = [
23972
- { key: "success", value: counts.success, color: accent },
23973
- { key: "noResult", value: counts.noResult, color: "var(--color-text-subtle)" },
23974
- { key: "skipped", value: counts.skipped, color: "var(--color-warning)" },
23975
- { key: "error", value: counts.error, color: "var(--color-error)" },
23976
- { key: "other", value: counts.other, color: "var(--color-primary)" }
23977
- ].filter((segment) => segment.value > 0);
23978
- return /* @__PURE__ */ jsx(
23979
- Box,
23980
- {
23981
- h: 6,
23982
- bg: "var(--color-surface-hover)",
23983
- style: {
23984
- borderRadius: 999,
23985
- display: "flex",
23986
- overflow: "hidden",
23987
- opacity: counts.total > 0 ? 1 : 0.55
23988
- },
23989
- children: segments.length > 0 ? segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${percent(segment.value, counts.total)}%`, bg: segment.color }, segment.key)) : /* @__PURE__ */ jsx(Box, { h: "100%", w: counts.total > 0 ? `${percent(counts.attempted, counts.total)}%` : "0%", bg: accent })
23990
- }
23991
- );
23992
- }
23993
- function TimelineLane({
23994
- title,
23995
- entity,
23996
- total,
23997
- stages,
23998
- progress,
23999
- emptyText
24000
- }) {
24001
- const Icon = entity === "company" ? IconBuilding : IconUsers;
24002
- return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
24003
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", gap: "xs", children: [
24004
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
24005
- /* @__PURE__ */ jsx(
24006
- Box,
24007
- {
24008
- w: 28,
24009
- h: 28,
24010
- style: {
24011
- alignItems: "center",
24012
- background: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
24013
- border: "1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-primary))",
24014
- borderRadius: 999,
24015
- color: "var(--color-primary)",
24016
- display: "flex",
24017
- justifyContent: "center"
24018
- },
24019
- children: /* @__PURE__ */ jsx(Icon, { size: 15 })
24020
- }
24021
- ),
24022
- /* @__PURE__ */ jsxs(Stack, { gap: 0, children: [
24023
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: title }),
24024
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
24025
- total,
24026
- " ",
24027
- entity === "company" ? "companies" : "contacts"
24028
- ] })
24029
- ] })
24030
- ] }),
24031
- /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "outline", color: entity === "company" ? "blue" : "teal", children: [
24032
- stages.length,
24033
- " stages"
24034
- ] })
24035
- ] }),
24036
- stages.length === 0 ? /* @__PURE__ */ jsx(
24037
- Box,
24038
- {
24039
- p: "sm",
24040
- style: {
24041
- border: "1px dashed var(--color-border)",
24042
- borderRadius: "var(--mantine-radius-md)"
24043
- },
24044
- children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: emptyText })
24045
- }
24046
- ) : /* @__PURE__ */ jsx(ScrollArea, { type: "hover", offsetScrollbars: true, children: /* @__PURE__ */ jsx(
24047
- Box,
24048
- {
24049
- style: {
24050
- minWidth: Math.max(560, stages.length * 178),
24051
- padding: "8px 2px 2px"
24052
- },
24053
- children: /* @__PURE__ */ jsx(
24054
- Box,
24055
- {
24056
- style: {
24057
- alignItems: "start",
24058
- display: "grid",
24059
- gap: 0,
24060
- gridTemplateColumns: `repeat(${stages.length}, minmax(150px, 1fr))`,
24061
- position: "relative"
24062
- },
24063
- children: stages.map((stage, index) => {
24064
- const counts = getStageProgress2(progress, stage.key, entity);
24065
- const accent = getLeadGenStageColorVar(stage.key);
24066
- const completion = Math.round(percent(counts.success, counts.total));
24067
- const attempted = Math.round(percent(counts.attempted, counts.total));
24068
- return /* @__PURE__ */ jsxs(Stack, { gap: 8, px: "xs", style: { position: "relative", zIndex: 1 }, children: [
24069
- /* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", children: [
24070
- /* @__PURE__ */ jsx(
24071
- Box,
24072
- {
24073
- w: 44,
24074
- h: 44,
24075
- style: {
24076
- alignItems: "center",
24077
- background: getLeadGenStageColorVar(stage.key, "background"),
24078
- border: `1px solid ${getLeadGenStageColorVar(stage.key, "border")}`,
24079
- borderRadius: 999,
24080
- boxShadow: `0 0 0 4px ${getLeadGenStageColorVar(stage.key, "background")}`,
24081
- color: accent,
24082
- display: "flex",
24083
- flexShrink: 0,
24084
- fontSize: 13,
24085
- fontWeight: 800,
24086
- justifyContent: "center"
24087
- },
24088
- children: index + 1
24089
- }
24090
- ),
24091
- /* @__PURE__ */ jsxs(Stack, { gap: 1, style: { minWidth: 0 }, children: [
24092
- /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
24093
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, lineClamp: 1, children: stage.label }),
24094
- /* @__PURE__ */ jsx(SourceBadge, { source: stage.source })
24095
- ] }),
24096
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: counts.total > 0 ? `${counts.attempted} / ${counts.total} attempted` : `Ready for ${entity === "company" ? "company" : "contact"} rows` })
24097
- ] })
24098
- ] }),
24099
- /* @__PURE__ */ jsx(ProgressRail, { counts, stageKey: stage.key }),
24100
- /* @__PURE__ */ jsxs(Group, { gap: 10, wrap: "nowrap", children: [
24101
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
24102
- counts.success,
24103
- " success"
24104
- ] }),
24105
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
24106
- attempted,
24107
- "% attempted"
24108
- ] }),
24109
- counts.error > 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "red", children: [
24110
- counts.error,
24111
- " errors"
24112
- ] }) : null
24113
- ] }),
24114
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
24115
- completion,
24116
- "% complete"
24117
- ] })
24118
- ] }, stage.key);
24119
- })
24120
- }
24121
- )
24122
- }
24123
- ) })
24124
- ] });
24125
- }
24126
- function PipelineFunnel({ progress, pipelineConfig, actions }) {
24127
- const companyStages = buildLaneStages({ entity: "company", progress, pipelineConfig, actions });
24128
- const contactStages = buildLaneStages({ entity: "contact", progress, pipelineConfig, actions });
24129
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
24130
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconListDetails, { size: 16 }), title: "Pipeline" }),
24131
- /* @__PURE__ */ jsx(
24132
- TimelineLane,
24133
- {
24134
- title: "Company pipeline",
24135
- entity: "company",
24136
- total: progress.totalCompanies,
24137
- stages: companyStages,
24138
- progress,
24139
- emptyText: "No company stages are configured, runnable, or recorded yet."
24140
- }
24141
- ),
24142
- /* @__PURE__ */ jsx(
24143
- TimelineLane,
24144
- {
24145
- title: "Contact pipeline",
24146
- entity: "contact",
24147
- total: progress.totalMembers,
24148
- stages: contactStages,
24149
- progress,
24150
- emptyText: "No contact stages are configured, runnable, or recorded yet."
24151
- }
24152
- )
24153
- ] }) });
24154
- }
24155
- function asRecord(value) {
24156
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
24157
- }
24158
- function selectedCount(selection) {
24159
- return selection.selectedCompanyIds.length + selection.selectedContactIds.length;
24160
- }
24161
- function buildInput(input, selection) {
24162
- const base = input && typeof input === "object" && !Array.isArray(input) ? input : {};
24163
- return {
24164
- ...base,
24165
- selectedCompanyIds: selection.selectedCompanyIds,
24166
- selectedContactIds: selection.selectedContactIds
24167
- };
24168
- }
24169
- function RunWorkflowModal({
24170
- opened,
24171
- onClose,
24172
- list,
24173
- actions,
24174
- selection,
24175
- initialResourceId,
24176
- title = "Run Workflow",
24177
- description = "Start a workflow immediately for this list.",
24178
- lockResourceSelection = false,
24179
- onResourceChange,
24180
- onSubmitted
24181
- }) {
24182
- const [selectedResourceId, setSelectedResourceId] = useState(
24183
- initialResourceId ?? actions[0]?.resourceId ?? null
24184
- );
24185
- const selectedAction = useMemo(
24186
- () => actions.find((action) => action.resourceId === selectedResourceId) ?? actions[0],
24187
- [actions, selectedResourceId]
24188
- );
24189
- const execution = useWorkflowExecution({
24190
- workflowId: selectedAction?.resourceId ?? "",
24191
- listId: list.id
24192
- });
24193
- useEffect(() => {
24194
- if (!opened) return;
24195
- const nextResourceId = initialResourceId ?? actions[0]?.resourceId ?? null;
24196
- setSelectedResourceId(nextResourceId);
24197
- onResourceChange?.(nextResourceId);
24198
- }, [actions, initialResourceId, onResourceChange, opened]);
24199
- const actionOptions = actions.map((action) => ({
24200
- value: action.resourceId,
24201
- label: action.label
24202
- }));
24203
- const handleResourceChange = (value) => {
24204
- setSelectedResourceId(value);
24205
- onResourceChange?.(value);
24206
- execution.reset();
24207
- };
24208
- const submitInput = async (input) => {
24209
- if (!selectedAction) return;
24210
- try {
24211
- const result = await execution.execute({
24212
- input: buildInput(input, selection)
24213
- });
24214
- onSubmitted?.(selectedAction.resourceId, result.executionId);
24215
- onClose();
24216
- } catch (error) {
24217
- showApiErrorNotification(error);
24218
- }
24219
- };
24220
- const count = selectedCount(selection);
24221
- return /* @__PURE__ */ jsx(
24222
- CustomModal,
24223
- {
24224
- opened,
24225
- onClose: () => !execution.isPending && onClose(),
24226
- size: "lg",
24227
- loading: execution.isPending,
24228
- children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
24229
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
24230
- /* @__PURE__ */ jsx(IconBolt, { size: 22 }),
24231
- /* @__PURE__ */ jsxs("div", { children: [
24232
- /* @__PURE__ */ jsx(Text, { fw: 600, children: title }),
24233
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: description })
24234
- ] })
24235
- ] }),
24236
- !actions.length ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "No list builder workflows are registered." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
24237
- /* @__PURE__ */ jsx(
24238
- Select,
24239
- {
24240
- label: "Workflow",
24241
- data: actionOptions,
24242
- value: selectedAction?.resourceId ?? null,
24243
- onChange: handleResourceChange,
24244
- disabled: execution.isPending || lockResourceSelection,
24245
- searchable: true
24246
- }
24247
- ),
24248
- selectedAction ? /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
24249
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
24250
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: selectedAction.category }),
24251
- selectedAction.stagesAffected.map((stage) => /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "outline", color: "gray", children: stage }, stage))
24252
- ] }),
24253
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedAction.description })
24254
- ] }) : null,
24255
- count > 0 ? /* @__PURE__ */ jsxs(Alert, { color: "blue", variant: "light", children: [
24256
- "This run includes ",
24257
- selection.selectedCompanyIds.length,
24258
- " selected companies and",
24259
- " ",
24260
- selection.selectedContactIds.length,
24261
- " selected contacts."
24262
- ] }) : null,
24263
- selectedAction ? /* @__PURE__ */ jsx(
24264
- RunWorkflowConfigForm,
24265
- {
24266
- action: selectedAction,
24267
- list,
24268
- onSubmit: submitInput,
24269
- isSubmitting: execution.isPending
24270
- },
24271
- selectedAction.resourceId
24272
- ) : null
24273
- ] })
24274
- ] })
24275
- }
24276
- );
24277
- }
24278
- function RunWorkflowConfigForm({ action, list, onSubmit, isSubmitting }) {
24279
- const schema = action.schema;
24280
- const layout = action.layout;
24281
- const [config, setConfig] = useState(() => asRecord(action.defaultInput?.(list)));
24282
- const handleChange = (next) => {
24283
- setConfig(next);
24284
- };
24285
- const handleSubmit = async () => {
24286
- await onSubmit(config);
24287
- };
24288
- return /* @__PURE__ */ jsx(Stack, { gap: "md", children: /* @__PURE__ */ jsx(
24289
- StepConfigForm,
24290
- {
24291
- schema,
24292
- layout,
24293
- value: config,
24294
- onChange: (v) => handleChange(asRecord(v)),
24295
- actions: [{ id: "run", label: `Run ${action.label}`, loading: isSubmitting }],
24296
- onAction: async () => {
24297
- await handleSubmit();
24298
- },
24299
- disabled: isSubmitting
24300
- }
24301
- ) });
24302
- }
24303
23468
  function formatDateTime3(value) {
24304
23469
  if (!value) return "Not yet";
24305
23470
  return new Date(value).toLocaleString("en-US", {
@@ -24470,150 +23635,10 @@ function WorkflowRunsPanel({
24470
23635
  }
24471
23636
  );
24472
23637
  }
24473
- var EMPTY_SELECTION = {
24474
- selectedCompanyIds: [],
24475
- selectedContactIds: []
24476
- };
24477
- function formatDateTime4(value) {
24478
- if (!value) return "Not yet";
24479
- return new Date(value).toLocaleString("en-US", {
24480
- month: "short",
24481
- day: "numeric",
24482
- year: "numeric",
24483
- hour: "numeric",
24484
- minute: "2-digit"
24485
- });
24486
- }
24487
- function getStatusColor6(status) {
24488
- switch (status) {
24489
- case "completed":
24490
- case "success":
24491
- case "launched":
24492
- return "green";
24493
- case "running":
24494
- case "pending":
24495
- case "enriching":
24496
- return "blue";
24497
- case "failed":
24498
- case "error":
24499
- case "archived":
24500
- return "red";
24501
- case "closing":
24502
- return "yellow";
24503
- default:
24504
- return "gray";
24505
- }
24506
- }
24507
- function ListBuilderPage({ listId }) {
24508
- const navigate = useNavigate();
24509
- const actions = useListActions();
24510
- const [runModalOpen, setRunModalOpen] = useState(false);
24511
- const [initialRunResourceId, setInitialRunResourceId] = useState(null);
24512
- const listQuery = useList(listId);
24513
- const progressQuery = useListProgress(listId);
24514
- const executionsQuery = useListExecutions(listId);
24515
- const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
24516
- const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
24517
- const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
24518
- const openRunModal = (resourceId) => {
24519
- const nextResourceId = actions[0]?.resourceId ?? null;
24520
- setInitialRunResourceId(nextResourceId);
24521
- setRunModalOpen(true);
24522
- };
24523
- const closeRunModal = () => {
24524
- setRunModalOpen(false);
24525
- setInitialRunResourceId(null);
24526
- };
24527
- const copyListCommand = (id) => {
24528
- void navigator.clipboard.writeText(`/acquisition --lead-gen list ${id}`);
24529
- showSuccessNotification("Copied list command to clipboard");
24530
- };
24531
- const backButton = /* @__PURE__ */ jsx(
24532
- Button,
24533
- {
24534
- variant: "light",
24535
- size: "xs",
24536
- leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
24537
- onClick: () => navigate({ to: "/lead-gen/lists" }),
24538
- children: "Lists"
24539
- }
24540
- );
24541
- if (isLoading) {
24542
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
24543
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
24544
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
24545
- ] }) }) });
24546
- }
24547
- if (error) {
24548
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
24549
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
24550
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load list builder" }) })
24551
- ] }) }) });
24552
- }
24553
- if (!listQuery.data || !progressQuery.data) {
24554
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
24555
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Not Found", caption: "The requested lead-gen list is unavailable." }),
24556
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 240, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "The requested list could not be found." }) }) })
24557
- ] }) }) });
24558
- }
24559
- const list = listQuery.data;
24560
- const progress = progressQuery.data;
24561
- return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
24562
- /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
24563
- /* @__PURE__ */ jsx(
24564
- PageTitleCaption,
24565
- {
24566
- title: list.name,
24567
- caption: list.description ?? "Lead-gen list builder",
24568
- rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
24569
- /* @__PURE__ */ jsx(Button, { size: "xs", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), onClick: () => openRunModal(), children: "Run Workflow" }),
24570
- backButton
24571
- ] })
24572
- }
24573
- ),
24574
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
24575
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
24576
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor6(list.status), children: list.status }),
24577
- /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
24578
- "Created ",
24579
- formatDateTime4(list.createdAt)
24580
- ] })
24581
- ] }),
24582
- /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: () => copyListCommand(list.id), style: { cursor: "pointer" }, children: [
24583
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
24584
- /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
24585
- ] }) })
24586
- ] }),
24587
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2, lg: 4 }, children: [
24588
- /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
24589
- /* @__PURE__ */ jsx(StatCard, { label: "Contacts", value: progress.totalMembers, icon: IconUsers }),
24590
- /* @__PURE__ */ jsx(StatCard, { label: "Workflows", value: actions.length, icon: IconBolt }),
24591
- /* @__PURE__ */ jsx(StatCard, { label: "Runs", value: executions.length, icon: IconPlayerPlay })
24592
- ] }),
24593
- /* @__PURE__ */ jsx(PipelineFunnel, { progress, pipelineConfig: list.pipelineConfig, actions }),
24594
- /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId })
24595
- ] }) }),
24596
- /* @__PURE__ */ jsx(
24597
- RunWorkflowModal,
24598
- {
24599
- opened: runModalOpen,
24600
- onClose: closeRunModal,
24601
- list,
24602
- actions,
24603
- selection: EMPTY_SELECTION,
24604
- initialResourceId: initialRunResourceId,
24605
- onSubmitted: (_resourceId, executionId) => {
24606
- showSuccessNotification(`Workflow run started: ${executionId}`);
24607
- }
24608
- }
24609
- )
24610
- ] });
24611
- }
24612
-
24613
- // src/lib/lead-gen/processing-state.ts
24614
- var LEAD_GEN_STAGE_CATALOG4 = getLeadGenStageCatalog(canonicalOrganizationModel);
24615
- function isRecord(value) {
24616
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
23638
+
23639
+ // src/lib/lead-gen/processing-state.ts
23640
+ function isRecord(value) {
23641
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
24617
23642
  }
24618
23643
  function parseProcessingStageStatus(value) {
24619
23644
  if (value === true || value === "success") return "success";
@@ -24624,7 +23649,7 @@ function parseProcessingStageStatus(value) {
24624
23649
  if (typeof value === "string") return "other";
24625
23650
  return null;
24626
23651
  }
24627
- function parseProcessingState(value) {
23652
+ function parseProcessingState(value, stageCatalog = {}) {
24628
23653
  const parsed = {};
24629
23654
  const stageStatuses = {};
24630
23655
  const knownStageStatuses = {};
@@ -24639,7 +23664,7 @@ function parseProcessingState(value) {
24639
23664
  }
24640
23665
  for (const [key, rawStatus] of Object.entries(value)) {
24641
23666
  const parsedStatus = parseProcessingStageStatus(rawStatus);
24642
- const isKnownStage = Boolean(LEAD_GEN_STAGE_CATALOG4[key]);
23667
+ const isKnownStage = Boolean(stageCatalog[key]);
24643
23668
  if (!parsedStatus) {
24644
23669
  unknownFields[key] = rawStatus;
24645
23670
  continue;
@@ -24668,16 +23693,16 @@ function readLeadGenStateKey(row) {
24668
23693
  const stateKey = row.stateKey ?? row.state_key;
24669
23694
  return typeof stateKey === "string" && stateKey.length > 0 ? stateKey : null;
24670
23695
  }
24671
- function getDisplayLeadGenStageStateFor(row, entity) {
24672
- const parsed = parseProcessingState(readLeadGenProcessingState(row));
24673
- const latest = Object.values(LEAD_GEN_STAGE_CATALOG4).filter((stage) => stage.entity === entity && parsed.knownStageStatuses[stage.key]).sort((a, b) => b.order - a.order)[0];
23696
+ function getDisplayLeadGenStageStateFor(row, entity, stageCatalog = {}) {
23697
+ const parsed = parseProcessingState(readLeadGenProcessingState(row), stageCatalog);
23698
+ const latest = Object.values(stageCatalog).filter((stage) => stage.entity === entity && parsed.knownStageStatuses[stage.key]).sort((a, b) => b.order - a.order)[0];
24674
23699
  if (latest) {
24675
23700
  return {
24676
23701
  stageKey: latest.key,
24677
23702
  stageStatus: parsed.knownStageStatuses[latest.key]
24678
23703
  };
24679
23704
  }
24680
- const unknownStageKey = Object.keys(parsed.stageStatuses).find((stageKey) => !LEAD_GEN_STAGE_CATALOG4[stageKey]);
23705
+ const unknownStageKey = Object.keys(parsed.stageStatuses).find((stageKey) => !stageCatalog[stageKey]);
24681
23706
  if (unknownStageKey) {
24682
23707
  return {
24683
23708
  stageKey: unknownStageKey,
@@ -24685,55 +23710,22 @@ function getDisplayLeadGenStageStateFor(row, entity) {
24685
23710
  };
24686
23711
  }
24687
23712
  const stateKey = readLeadGenStateKey(row);
24688
- return stateKey && LEAD_GEN_STAGE_CATALOG4[stateKey] ? { stageKey: stateKey, stageStatus: null } : { stageKey: null, stageStatus: null };
23713
+ return stateKey && stageCatalog[stateKey] ? { stageKey: stateKey, stageStatus: null } : { stageKey: null, stageStatus: null };
24689
23714
  }
24690
23715
 
24691
23716
  // src/features/lead-gen/build-state.ts
24692
23717
  var ORPHAN_STAGE_ORDER = 9999;
24693
- var LEAD_GEN_STAGE_CATALOG5 = getLeadGenStageCatalog(canonicalOrganizationModel);
24694
- function asRecord2(value) {
23718
+ function asRecord(value) {
24695
23719
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
24696
23720
  }
24697
- function defineMvpBuildStep(step, emptyBlockedText) {
24698
- const description = "description" in step && typeof step.description === "string" ? step.description : `Run ${step.label.toLowerCase()} for this list.`;
24699
- return {
24700
- ...step,
24701
- description,
24702
- emptyBlockedText
24703
- };
24704
- }
24705
- var MVP_BUILD_STEPS = [
24706
- defineMvpBuildStep(
24707
- PROSPECTING_STEPS2.localServices.sourceCompanies,
24708
- "No company source has been configured or populated yet."
24709
- ),
24710
- defineMvpBuildStep(
24711
- PROSPECTING_STEPS2.localServices.analyzeWebsites,
24712
- "Source companies before website analysis can run."
24713
- ),
24714
- defineMvpBuildStep(
24715
- PROSPECTING_STEPS2.localServices.qualifyCompanies,
24716
- "Analyze websites before qualification can run."
24717
- ),
24718
- defineMvpBuildStep(
24719
- PROSPECTING_STEPS2.localServices.findContacts,
24720
- "Qualify companies before contact discovery can run."
24721
- ),
24722
- defineMvpBuildStep(PROSPECTING_STEPS2.localServices.verifyEmails, "Find contacts before email verification can run."),
24723
- defineMvpBuildStep(PROSPECTING_STEPS2.localServices.personalize, "Verify emails before personalization can run."),
24724
- defineMvpBuildStep(
24725
- PROSPECTING_STEPS2.localServices.review,
24726
- "Personalize contacts before records are ready for review."
24727
- )
24728
- ];
24729
- function sortStageKeys(keys) {
23721
+ function sortStageKeys(keys, stageCatalog = {}) {
24730
23722
  return keys.slice().sort((a, b) => {
24731
- const oa = LEAD_GEN_STAGE_CATALOG5[a]?.order ?? ORPHAN_STAGE_ORDER;
24732
- const ob = LEAD_GEN_STAGE_CATALOG5[b]?.order ?? ORPHAN_STAGE_ORDER;
23723
+ const oa = stageCatalog[a]?.order ?? ORPHAN_STAGE_ORDER;
23724
+ const ob = stageCatalog[b]?.order ?? ORPHAN_STAGE_ORDER;
24733
23725
  return oa - ob || a.localeCompare(b);
24734
23726
  });
24735
23727
  }
24736
- function getStageProgress3(progress, step) {
23728
+ function getStageProgress(progress, step) {
24737
23729
  return step.primaryEntity === "company" ? progress.byCompanyStage[step.stageKey] : progress.byContactStage[step.stageKey];
24738
23730
  }
24739
23731
  function getEntityTotal(progress, entity) {
@@ -24742,8 +23734,8 @@ function getEntityTotal(progress, entity) {
24742
23734
  function findActionForStep(actions, step) {
24743
23735
  return actions.find((action) => action.actionKey === step.actionKey) ?? actions.find((action) => action.stagesAffected?.includes(step.stageKey));
24744
23736
  }
24745
- function normalizeBuildPlanStep(step) {
24746
- const fallback = MVP_BUILD_STEPS.find((item) => item.id === step.id || item.stageKey === step.stageKey);
23737
+ function normalizeBuildPlanStep(step, fallbackSteps = []) {
23738
+ const fallback = fallbackSteps.find((item) => item.id === step.id || item.stageKey === step.stageKey);
24747
23739
  return {
24748
23740
  id: step.id,
24749
23741
  label: step.label,
@@ -24769,17 +23761,19 @@ function isCurrentBuildPlanStep(step) {
24769
23761
  const candidate = step;
24770
23762
  return (candidate.primaryEntity === "company" || candidate.primaryEntity === "contact") && Array.isArray(candidate.outputs) && candidate.outputs.length > 0 && candidate.dependencyMode === "per-record-eligibility";
24771
23763
  }
24772
- function resolveBuildPlanSteps(list) {
23764
+ function resolveBuildPlanSteps(list, defaultBuildSteps = [], buildTemplates = []) {
24773
23765
  const snapshot = list.metadata.buildPlanSnapshot;
24774
23766
  const snapshotSteps = snapshot?.steps;
24775
23767
  if (snapshotSteps?.length && snapshotSteps.every(isCurrentBuildPlanStep)) {
24776
- return snapshotSteps.map(normalizeBuildPlanStep);
23768
+ return snapshotSteps.map((step) => normalizeBuildPlanStep(step, defaultBuildSteps));
24777
23769
  }
24778
23770
  if (snapshot?.templateId) {
24779
- const templateSnapshot = createBuildPlanSnapshotFromTemplateId(snapshot.templateId);
24780
- if (templateSnapshot?.steps.length) return templateSnapshot.steps.map(normalizeBuildPlanStep);
23771
+ const templateSnapshot = createBuildPlanSnapshotFromTemplateId(snapshot.templateId, buildTemplates);
23772
+ if (templateSnapshot?.steps.length) {
23773
+ return templateSnapshot.steps.map((step) => normalizeBuildPlanStep(step, defaultBuildSteps));
23774
+ }
24781
23775
  }
24782
- return MVP_BUILD_STEPS;
23776
+ return defaultBuildSteps;
24783
23777
  }
24784
23778
  function getPrerequisiteSteps(step, byStepId) {
24785
23779
  return (step.dependsOn ?? []).flatMap((dependencyId) => {
@@ -24825,19 +23819,20 @@ function isStageComplete(entry) {
24825
23819
  return entry.error === 0 && entry.attempted >= entry.total && entry.success + entry.noResult + entry.skipped + entry.other >= entry.total;
24826
23820
  }
24827
23821
  function getExecutionInput(run2) {
24828
- return asRecord2(run2.input);
23822
+ return asRecord(run2.input);
24829
23823
  }
24830
23824
  function isCompletedRun(run2) {
24831
- return run2.status === "completed" || run2.status === "success" || run2.status === "succeeded";
23825
+ return run2.status === "completed" || run2.status === "warning" || run2.status === "success" || run2.status === "succeeded";
24832
23826
  }
24833
23827
  function getApprovedCompanyIds(run2) {
24834
23828
  const input = getExecutionInput(run2);
24835
23829
  const raw = input.approvedCompanyIds;
24836
23830
  return Array.isArray(raw) ? raw.filter((value) => typeof value === "string") : [];
24837
23831
  }
24838
- function hasApprovedExportRun(executions) {
23832
+ function hasApprovedExportRun(executions, exportWorkflowId) {
23833
+ if (!exportWorkflowId) return false;
24839
23834
  return executions.some((run2) => {
24840
- if (!isCompletedRun(run2) || run2.resourceId !== "lgn-06-export-list-workflow") return false;
23835
+ if (!isCompletedRun(run2) || run2.resourceId !== exportWorkflowId) return false;
24841
23836
  const input = getExecutionInput(run2);
24842
23837
  return input.approved === true || getApprovedCompanyIds(run2).length > 0;
24843
23838
  });
@@ -24845,10 +23840,10 @@ function hasApprovedExportRun(executions) {
24845
23840
  function hasCompletedSourcingRun(step, action, executions) {
24846
23841
  return executions.some((run2) => isCompletedRun(run2) && run2.resourceId === (action?.resourceId ?? step.actionKey));
24847
23842
  }
24848
- function deriveBusinessProgress(list, progress, actions = [], executions = []) {
23843
+ function deriveBusinessProgress(list, progress, actions = [], executions = [], config = {}) {
24849
23844
  const byCompanyStage = { ...progress.byCompanyStage };
24850
23845
  const byContactStage = { ...progress.byContactStage };
24851
- const steps = resolveBuildPlanSteps(list);
23846
+ const steps = resolveBuildPlanSteps(list, config.defaultBuildSteps, config.buildTemplates);
24852
23847
  for (const step of steps) {
24853
23848
  const stageProgress = step.primaryEntity === "company" ? byCompanyStage[step.stageKey] : byContactStage[step.stageKey];
24854
23849
  if (stageProgress || (step.dependsOn?.length ?? 0) > 0 || !step.outputs.includes("company")) continue;
@@ -24858,9 +23853,9 @@ function deriveBusinessProgress(list, progress, actions = [], executions = []) {
24858
23853
  byCompanyStage[step.stageKey] = createCompleteStageCounts(progress.totalCompanies);
24859
23854
  }
24860
23855
  }
24861
- if (!isStageComplete(byCompanyStage.uploaded) && hasApprovedExportRun(executions)) {
23856
+ if (!isStageComplete(byCompanyStage.uploaded) && hasApprovedExportRun(executions, config.exportWorkflowId)) {
24862
23857
  const exportedCount = Math.max(
24863
- ...executions.filter((run2) => isCompletedRun(run2) && run2.resourceId === "lgn-06-export-list-workflow").map((run2) => getApprovedCompanyIds(run2).length),
23858
+ ...executions.filter((run2) => isCompletedRun(run2) && run2.resourceId === config.exportWorkflowId).map((run2) => getApprovedCompanyIds(run2).length),
24864
23859
  0
24865
23860
  );
24866
23861
  const total = exportedCount > 0 ? exportedCount : progress.totalCompanies;
@@ -24882,10 +23877,10 @@ function getStepRecommendedAction(step, action, kind) {
24882
23877
  maxSize: step.maxBatchSize
24883
23878
  };
24884
23879
  }
24885
- function deriveBuildStepStates(list, progress, actions) {
23880
+ function deriveBuildStepStates(list, progress, actions, defaultBuildSteps = [], buildTemplates = []) {
24886
23881
  const byStepId = /* @__PURE__ */ new Map();
24887
- for (const step of resolveBuildPlanSteps(list)) {
24888
- const stageProgress = getStageProgress3(progress, step);
23882
+ for (const step of resolveBuildPlanSteps(list, defaultBuildSteps, buildTemplates)) {
23883
+ const stageProgress = getStageProgress(progress, step);
24889
23884
  const entityTotal = getEntityTotal(progress, step.primaryEntity);
24890
23885
  const prerequisites = getPrerequisiteSteps(step, byStepId);
24891
23886
  const hasDependencies = (step.dependsOn?.length ?? 0) > 0;
@@ -24896,9 +23891,10 @@ function deriveBuildStepStates(list, progress, actions) {
24896
23891
  const failed = stageProgress?.error ?? 0;
24897
23892
  const ready = Math.max(0, eligibleTotal - complete - failed);
24898
23893
  const blocked = hasDependencies ? Math.max(total - eligibleTotal - complete - failed, 0) : 0;
24899
- const status = failed > 0 ? "failed" : ready > 0 ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
24900
23894
  const action = findActionForStep(actions, step);
24901
23895
  const nextActionKind = getStepActionKind(step, ready, failed, action);
23896
+ const isActionableEmptySeedStep = !hasDependencies && total === 0 && nextActionKind === "run_next_batch";
23897
+ const status = failed > 0 ? "failed" : ready > 0 || isActionableEmptySeedStep ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
24902
23898
  const state = {
24903
23899
  ...step,
24904
23900
  status,
@@ -24917,8 +23913,8 @@ function deriveBuildStepStates(list, progress, actions) {
24917
23913
  function getRecommendedBuildStep(steps) {
24918
23914
  return steps.find((step) => step.nextActionKind === "retry_failed") ?? steps.find((step) => step.nextActionKind === "run_next_batch") ?? null;
24919
23915
  }
24920
- function resolveBuildState(list, progress, actions) {
24921
- const steps = deriveBuildStepStates(list, progress, actions);
23916
+ function resolveBuildState(list, progress, actions, config = {}) {
23917
+ const steps = deriveBuildStepStates(list, progress, actions, config.defaultBuildSteps, config.buildTemplates);
24922
23918
  const recommendedStep = getRecommendedBuildStep(steps);
24923
23919
  return {
24924
23920
  steps,
@@ -24926,8 +23922,11 @@ function resolveBuildState(list, progress, actions) {
24926
23922
  recommendedAction: recommendedStep?.recommendedAction ?? null
24927
23923
  };
24928
23924
  }
24929
- var LEAD_GEN_STAGE_CATALOG6 = getLeadGenStageCatalog(canonicalOrganizationModel);
24930
- function formatDateTime5(value) {
23925
+ var DATA_MODE_OPTIONS2 = [
23926
+ { value: "mock", label: "Mock data" },
23927
+ { value: "live", label: "Live data" }
23928
+ ];
23929
+ function formatDateTime4(value) {
24931
23930
  if (!value) return "Not yet";
24932
23931
  return new Date(value).toLocaleString("en-US", {
24933
23932
  month: "short",
@@ -24941,7 +23940,7 @@ function contactDisplayName(firstName, lastName) {
24941
23940
  const full = [firstName, lastName].filter(Boolean).join(" ").trim();
24942
23941
  return full || "\u2014";
24943
23942
  }
24944
- function getStatusColor7(status) {
23943
+ function getStatusColor6(status) {
24945
23944
  switch (status) {
24946
23945
  case "draft":
24947
23946
  return "gray";
@@ -24957,22 +23956,25 @@ function getStatusColor7(status) {
24957
23956
  return "gray";
24958
23957
  }
24959
23958
  }
24960
- function getMemberStateColor2(stateKey) {
24961
- const stage = LEAD_GEN_STAGE_CATALOG6[stateKey];
23959
+ function getListDataMode(list) {
23960
+ return list.pipelineConfig?.dataMode === "live" ? "live" : "mock";
23961
+ }
23962
+ function getMemberStateColor2(stateKey, stageCatalog) {
23963
+ const stage = stageCatalog[stateKey];
24962
23964
  if (stage?.entity === "contact") return "green";
24963
23965
  if (stage?.entity === "company") return "blue";
24964
23966
  return "gray";
24965
23967
  }
24966
- function displayMemberStageFor(row, kind) {
24967
- const stage = getDisplayLeadGenStageStateFor(row, kind);
23968
+ function displayMemberStageFor(row, kind, stageCatalog) {
23969
+ const stage = getDisplayLeadGenStageStateFor(row, kind, stageCatalog);
24968
23970
  if (!stage.stageKey) return null;
24969
- const catalogStage = LEAD_GEN_STAGE_CATALOG6[stage.stageKey];
23971
+ const catalogStage = stageCatalog[stage.stageKey];
24970
23972
  return {
24971
23973
  key: stage.stageKey,
24972
23974
  label: catalogStage?.label ?? stage.stageKey
24973
23975
  };
24974
23976
  }
24975
- function asRecord3(value) {
23977
+ function asRecord2(value) {
24976
23978
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
24977
23979
  }
24978
23980
  function valueAtPath(source, path) {
@@ -25025,14 +24027,14 @@ function getRecordColumnSource(row) {
25025
24027
  }
25026
24028
  function getStageEntry(row, stageKey) {
25027
24029
  const entry = valueAtPath(row.processingState, stageKey);
25028
- return asRecord3(entry);
24030
+ return asRecord2(entry);
25029
24031
  }
25030
24032
  function getRecordStageStatus(row, stageKey) {
25031
24033
  const status = getStageEntry(row, stageKey).status;
25032
24034
  return typeof status === "string" ? status : "pending";
25033
24035
  }
25034
24036
  function getStageFailureReason(row, stageKey) {
25035
- const data = asRecord3(getStageEntry(row, stageKey).data);
24037
+ const data = asRecord2(getStageEntry(row, stageKey).data);
25036
24038
  const error = data.error ?? data.reason ?? data.failureReason ?? data.disqualifiedReason;
25037
24039
  return compactText(error);
25038
24040
  }
@@ -25060,7 +24062,7 @@ function renderRecordValue(value, column) {
25060
24062
  case "badge":
25061
24063
  return /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: column.badgeColor ?? getRecordStatusColor(String(value)), children: compactText(value) });
25062
24064
  case "datetime":
25063
- return compactText(typeof value === "string" ? formatDateTime5(value) : value);
24065
+ return compactText(typeof value === "string" ? formatDateTime4(value) : value);
25064
24066
  case "count":
25065
24067
  return Array.isArray(value) ? value.length : compactText(value);
25066
24068
  case "json":
@@ -25088,33 +24090,32 @@ function getRunStatusColor(status) {
25088
24090
  return "gray";
25089
24091
  }
25090
24092
  }
24093
+ function isRunInFlight(status) {
24094
+ return status === "running" || status === "pending" || status === "queued";
24095
+ }
24096
+ function isRunTerminal(status) {
24097
+ return !isRunInFlight(status);
24098
+ }
25091
24099
  function getRunInput(run2) {
25092
- const payload = asRecord3(run2.payload);
24100
+ const payload = asRecord2(run2.payload);
25093
24101
  return run2.input ?? run2.inputs ?? payload.input ?? payload.inputs;
25094
24102
  }
25095
24103
  function hasRunInput(run2) {
25096
24104
  const input = getRunInput(run2);
25097
24105
  return input !== void 0 && input !== null;
25098
24106
  }
25099
- function sanitizeStepInput(value, _actionKey) {
25100
- return value;
24107
+ function sanitizeStepInput(value, actionKey) {
24108
+ if (actionKey === "lead-gen.export.list") return value;
24109
+ const next = { ...value };
24110
+ delete next.mode;
24111
+ return next;
25101
24112
  }
25102
24113
  function getDefaultStepInput(list, action) {
25103
- return asRecord3(action?.defaultInput?.(list));
24114
+ return asRecord2(action?.defaultInput?.(list));
25104
24115
  }
25105
24116
  function getInitialStepInput(list, action) {
25106
24117
  return getDefaultStepInput(list, action);
25107
24118
  }
25108
- function mergeStepRunInput(list, action, inputAtSubmit) {
25109
- return {
25110
- ...getDefaultStepInput(list, action),
25111
- ...inputAtSubmit
25112
- };
25113
- }
25114
- var EMPTY_SELECTION2 = {
25115
- selectedCompanyIds: [],
25116
- selectedContactIds: []
25117
- };
25118
24119
  function getStageStatus(entry) {
25119
24120
  if (!entry || (entry.total ?? 0) === 0) return "not-started";
25120
24121
  if ((entry.error ?? 0) > 0) return "errors";
@@ -25160,9 +24161,10 @@ function StageNode({
25160
24161
  index,
25161
24162
  stageKey,
25162
24163
  fallbackLabel,
25163
- entry
24164
+ entry,
24165
+ stageCatalog
25164
24166
  }) {
25165
- const label = LEAD_GEN_STAGE_CATALOG6[stageKey]?.label ?? fallbackLabel ?? stageKey;
24167
+ const label = stageCatalog[stageKey]?.label ?? fallbackLabel ?? stageKey;
25166
24168
  const status = getStageStatus(entry);
25167
24169
  const colors = getStageColors(status);
25168
24170
  const total = entry?.total ?? 0;
@@ -25234,9 +24236,10 @@ function StageNode({
25234
24236
  function PipelineStepper({
25235
24237
  plannedSteps,
25236
24238
  byStage,
25237
- emptyText
24239
+ emptyText,
24240
+ stageCatalog
25238
24241
  }) {
25239
- const stageKeys = plannedSteps.length > 0 ? plannedSteps.map((s) => s.stageKey) : sortStageKeys(Object.keys(byStage));
24242
+ const stageKeys = plannedSteps.length > 0 ? plannedSteps.map((s) => s.stageKey) : sortStageKeys(Object.keys(byStage), stageCatalog);
25240
24243
  const labelByKey = new Map(plannedSteps.map((s) => [s.stageKey, s.label]));
25241
24244
  if (stageKeys.length === 0) {
25242
24245
  return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText });
@@ -25265,13 +24268,28 @@ function PipelineStepper({
25265
24268
  }
25266
24269
  }
25267
24270
  ),
25268
- stageKeys.map((key, index) => /* @__PURE__ */ jsx(StageNode, { index, stageKey: key, fallbackLabel: labelByKey.get(key), entry: byStage[key] }, key))
24271
+ stageKeys.map((key, index) => /* @__PURE__ */ jsx(
24272
+ StageNode,
24273
+ {
24274
+ index,
24275
+ stageKey: key,
24276
+ fallbackLabel: labelByKey.get(key),
24277
+ entry: byStage[key],
24278
+ stageCatalog
24279
+ },
24280
+ key
24281
+ ))
25269
24282
  ]
25270
24283
  }
25271
24284
  ) });
25272
24285
  }
25273
- function PipelineStagesCard({ list, progress }) {
25274
- const plannedSteps = resolveBuildPlanSteps(list);
24286
+ function PipelineStagesCard({
24287
+ list,
24288
+ progress,
24289
+ leadGenConfig
24290
+ }) {
24291
+ const stageCatalog = leadGenConfig.stageCatalog ?? {};
24292
+ const plannedSteps = resolveBuildPlanSteps(list, leadGenConfig.defaultBuildSteps, leadGenConfig.buildTemplates);
25275
24293
  const templateLabel = list.metadata.buildPlanSnapshot?.templateLabel;
25276
24294
  const combinedByStage = { ...progress.byCompanyStage, ...progress.byContactStage };
25277
24295
  const hasAnyContent = plannedSteps.length > 0 || Object.keys(combinedByStage).length > 0;
@@ -25285,14 +24303,14 @@ function PipelineStagesCard({ list, progress }) {
25285
24303
  ] })
25286
24304
  ] }),
25287
24305
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
25288
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor7(list.status), children: list.status }),
24306
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor6(list.status), children: list.status }),
25289
24307
  list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
25290
24308
  "Launched ",
25291
- formatDateTime5(list.launchedAt)
24309
+ formatDateTime4(list.launchedAt)
25292
24310
  ] }),
25293
24311
  list.completedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
25294
24312
  "Completed ",
25295
- formatDateTime5(list.completedAt)
24313
+ formatDateTime4(list.completedAt)
25296
24314
  ] })
25297
24315
  ] })
25298
24316
  ] }),
@@ -25309,25 +24327,49 @@ function PipelineStagesCard({ list, progress }) {
25309
24327
  {
25310
24328
  plannedSteps,
25311
24329
  byStage: combinedByStage,
25312
- emptyText: "No pipeline activity recorded yet."
24330
+ emptyText: "No pipeline activity recorded yet.",
24331
+ stageCatalog
25313
24332
  }
25314
24333
  )
25315
24334
  ] })
25316
24335
  ] }) });
25317
24336
  }
25318
24337
  function ListConfigCard({ list }) {
24338
+ const updateListConfig = useUpdateListConfig(list.id);
25319
24339
  const icp = list.icp ?? {};
25320
24340
  const scraping = list.scrapingConfig ?? {};
25321
24341
  const hasIcp = Object.values(icp).some((v) => v !== void 0 && v !== null && v !== "");
25322
24342
  const hasScraping = Object.values(scraping).some((v) => v !== void 0 && v !== null && v !== "");
25323
- if (!hasIcp && !hasScraping) {
25324
- return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
25325
- /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
25326
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria recorded for this list." })
25327
- ] }) });
25328
- }
24343
+ const dataMode = getListDataMode(list);
24344
+ const handleDataModeChange = (value) => {
24345
+ if (value !== "mock" && value !== "live") return;
24346
+ if (value === dataMode) return;
24347
+ updateListConfig.mutate({
24348
+ pipelineConfig: {
24349
+ dataMode: value
24350
+ }
24351
+ });
24352
+ };
25329
24353
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
25330
- /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
24354
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", gap: "sm", children: [
24355
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
24356
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
24357
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Data Mode applies to sourcing, enrichment, and research steps." })
24358
+ ] }),
24359
+ /* @__PURE__ */ jsx(
24360
+ Select,
24361
+ {
24362
+ label: "Data Mode",
24363
+ data: DATA_MODE_OPTIONS2,
24364
+ value: dataMode,
24365
+ onChange: handleDataModeChange,
24366
+ disabled: updateListConfig.isPending,
24367
+ w: 180,
24368
+ size: "xs"
24369
+ }
24370
+ )
24371
+ ] }),
24372
+ !hasIcp && !hasScraping && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria recorded for this list." }),
25331
24373
  (hasIcp || hasScraping) && /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
25332
24374
  hasIcp && /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
25333
24375
  /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "ICP Rubric" }),
@@ -25383,7 +24425,11 @@ function ListConfigCard({ list }) {
25383
24425
  ] })
25384
24426
  ] }) });
25385
24427
  }
25386
- function OverviewTab({ list, progress }) {
24428
+ function OverviewTab({
24429
+ list,
24430
+ progress,
24431
+ leadGenConfig
24432
+ }) {
25387
24433
  const hasMetadata = list.metadata && Object.keys(list.metadata).length > 0;
25388
24434
  return /* @__PURE__ */ jsxs(
25389
24435
  TabSection,
@@ -25396,7 +24442,7 @@ function OverviewTab({ list, progress }) {
25396
24442
  /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
25397
24443
  /* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
25398
24444
  ] }),
25399
- /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress }),
24445
+ /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress, leadGenConfig }),
25400
24446
  /* @__PURE__ */ jsx(ListConfigCard, { list }),
25401
24447
  hasMetadata && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
25402
24448
  /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata" }),
@@ -25458,7 +24504,14 @@ function StatusPill({ label, tone }) {
25458
24504
  }
25459
24505
  );
25460
24506
  }
25461
- function StepCounterLine({ step }) {
24507
+ function StepCounterLine({ step, isRunning = false }) {
24508
+ if (isRunning) {
24509
+ const processed = step.complete > 0 ? `${step.complete} processed` : "processing";
24510
+ return /* @__PURE__ */ jsxs(Text, { size: "xs", fw: 700, style: { color: "var(--color-primary)" }, children: [
24511
+ "Running - ",
24512
+ processed
24513
+ ] });
24514
+ }
25462
24515
  const items = [
25463
24516
  step.ready > 0 ? { label: "ready", value: step.ready, color: "var(--color-primary)" } : null,
25464
24517
  step.complete > 0 ? { label: "done", value: step.complete, color: "var(--color-success)" } : null,
@@ -25474,9 +24527,9 @@ function StepCounterLine({ step }) {
25474
24527
  item.label
25475
24528
  ] }, item.label)) });
25476
24529
  }
25477
- function BuildStepProgressBar({ step }) {
24530
+ function BuildStepProgressBar({ step, isRunning = false }) {
25478
24531
  const segments = [
25479
- { key: "complete", value: step.complete, color: "var(--color-success)" },
24532
+ { key: "complete", value: step.complete, color: isRunning ? "var(--color-primary)" : "var(--color-success)" },
25480
24533
  { key: "ready", value: step.ready, color: "var(--color-primary)" },
25481
24534
  { key: "failed", value: step.failed, color: "var(--color-error)" },
25482
24535
  { key: "blocked", value: step.blocked, color: "var(--color-border)" }
@@ -25486,7 +24539,7 @@ function BuildStepProgressBar({ step }) {
25486
24539
  Box,
25487
24540
  {
25488
24541
  h: 8,
25489
- "aria-label": `${step.label} progress: ${step.complete} complete, ${step.ready} ready, ${step.failed} failed, ${step.blocked} blocked`,
24542
+ "aria-label": `${step.label} progress: ${step.complete} ${isRunning ? "processed" : "complete"}, ${step.ready} ready, ${step.failed} failed, ${step.blocked} blocked`,
25490
24543
  style: {
25491
24544
  backgroundColor: "color-mix(in srgb, var(--color-border) 65%, transparent)",
25492
24545
  borderRadius: 999,
@@ -25500,8 +24553,9 @@ function BuildStepProgressBar({ step }) {
25500
24553
  function getDisplayStep(steps, recommendedStep) {
25501
24554
  return recommendedStep ?? steps.find((step) => step.status !== "complete") ?? steps[steps.length - 1] ?? null;
25502
24555
  }
25503
- function getBuildTabDescription(steps, currentStep) {
24556
+ function getBuildTabDescription(steps, currentStep, runningStep = null) {
25504
24557
  if (!steps.length) return "No build steps are available for this list.";
24558
+ if (runningStep) return `${runningStep.label} is running.`;
25505
24559
  if (steps.every((step) => step.status === "complete")) return "List build complete.";
25506
24560
  if (!currentStep) return "No build steps need attention right now.";
25507
24561
  if (currentStep.status === "ready") return `${currentStep.label} is ready to run.`;
@@ -25509,7 +24563,6 @@ function getBuildTabDescription(steps, currentStep) {
25509
24563
  return `${currentStep.label} is waiting on earlier work.`;
25510
24564
  }
25511
24565
  var RECORDS_PAGE_SIZE = 25;
25512
- var EXPORT_WORKFLOW_ID = "lgn-06-export-list-workflow";
25513
24566
  function setValueAtInputPath(source, path, value) {
25514
24567
  const segments = path.split(".").filter(Boolean);
25515
24568
  if (!segments.length) return source;
@@ -25527,19 +24580,6 @@ function setValueAtInputPath(source, path, value) {
25527
24580
  });
25528
24581
  return next;
25529
24582
  }
25530
- function credentialMatchesRequirement(credential, requirement) {
25531
- const provider = requirement.provider.toLowerCase();
25532
- const credentialProvider = credential.provider?.toLowerCase() ?? "";
25533
- const credentialType = credential.type.toLowerCase();
25534
- const credentialName = credential.name.toLowerCase();
25535
- if (credentialProvider === provider || credentialType === provider || credentialName.startsWith(`${provider}-`) || credentialName.includes(`-${provider}`) || credentialName.includes(`${provider}-`)) {
25536
- return true;
25537
- }
25538
- return credentialType === requirement.credentialType;
25539
- }
25540
- function getMatchingCredentials(credentials, requirement) {
25541
- return credentials.filter((credential) => credentialMatchesRequirement(credential, requirement));
25542
- }
25543
24583
  function getSelectedCredential(credentials, selections, requirement) {
25544
24584
  const selectedId = selections[requirement.key];
25545
24585
  return credentials.find((credential) => credential.id === selectedId) ?? null;
@@ -25577,6 +24617,7 @@ function CredentialRequirementsPanel({
25577
24617
  requirements,
25578
24618
  credentials,
25579
24619
  credentialsLoading,
24620
+ credentialsError,
25580
24621
  selections,
25581
24622
  verificationByCredentialId,
25582
24623
  verifyingCredentialId,
@@ -25587,9 +24628,9 @@ function CredentialRequirementsPanel({
25587
24628
  return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
25588
24629
  /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Credentials" }),
25589
24630
  requirements.map((requirement) => {
25590
- const matchingCredentials = getMatchingCredentials(credentials, requirement);
25591
24631
  const selectedCredential = getSelectedCredential(credentials, selections, requirement);
25592
24632
  const verification = selectedCredential ? verificationByCredentialId[selectedCredential.id] : null;
24633
+ const credentialsUnavailable = Boolean(credentialsError) && credentials.length === 0;
25593
24634
  return /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
25594
24635
  /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-end", wrap: "nowrap", children: [
25595
24636
  /* @__PURE__ */ jsx(
@@ -25597,8 +24638,8 @@ function CredentialRequirementsPanel({
25597
24638
  {
25598
24639
  label: requirement.label,
25599
24640
  description: `${requirement.provider} credential for ${requirement.inputPath}`,
25600
- placeholder: credentialsLoading ? "Loading credentials..." : "Select credential",
25601
- data: matchingCredentials.map((credential) => ({
24641
+ placeholder: credentialsLoading ? "Loading credentials..." : credentialsUnavailable ? "Credentials unavailable" : "Select credential",
24642
+ data: credentials.map((credential) => ({
25602
24643
  value: credential.id,
25603
24644
  label: `${credential.name} (${credential.provider ?? credential.type})`
25604
24645
  })),
@@ -25607,7 +24648,7 @@ function CredentialRequirementsPanel({
25607
24648
  onSelect(requirement, credentials.find((credential) => credential.id === credentialId) ?? null);
25608
24649
  },
25609
24650
  required: requirement.required,
25610
- disabled: credentialsLoading || matchingCredentials.length === 0,
24651
+ disabled: credentialsLoading || credentialsUnavailable || credentials.length === 0,
25611
24652
  style: { flex: 1 }
25612
24653
  }
25613
24654
  ),
@@ -25623,16 +24664,17 @@ function CredentialRequirementsPanel({
25623
24664
  }
25624
24665
  )
25625
24666
  ] }),
25626
- matchingCredentials.length === 0 ? /* @__PURE__ */ jsx(
24667
+ credentialsUnavailable ? /* @__PURE__ */ jsx(Alert, { color: "red", variant: "light", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
24668
+ "Credentials could not be loaded. ",
24669
+ credentialsError?.message ?? "Refresh the page and try again."
24670
+ ] }) }) : !credentialsLoading && credentials.length === 0 ? /* @__PURE__ */ jsx(
25627
24671
  Alert,
25628
24672
  {
25629
24673
  color: requirement.required ? "yellow" : "gray",
25630
24674
  variant: "light",
25631
24675
  icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }),
25632
24676
  children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
25633
- "No matching ",
25634
- requirement.provider,
25635
- " credentials found. Add one in",
24677
+ "No credentials found. Add one in",
25636
24678
  " ",
25637
24679
  /* @__PURE__ */ jsx(Text, { component: "a", href: "/settings/credentials", size: "sm", fw: 600, children: "Settings -> Credentials" }),
25638
24680
  "."
@@ -25649,7 +24691,7 @@ function CredentialRequirementsPanel({
25649
24691
  /* @__PURE__ */ jsx(Text, { size: "sm", children: verification.message }),
25650
24692
  verification.checkedAt ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
25651
24693
  "Checked ",
25652
- formatDateTime5(verification.checkedAt)
24694
+ formatDateTime4(verification.checkedAt)
25653
24695
  ] }) : null,
25654
24696
  formatCredentialVerificationDetails(verification.details) ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatCredentialVerificationDetails(verification.details) }) : null
25655
24697
  ] })
@@ -25660,32 +24702,54 @@ function CredentialRequirementsPanel({
25660
24702
  ] });
25661
24703
  }
25662
24704
  function BuildStepRunningWatcher({
24705
+ listId,
25663
24706
  resourceId,
25664
24707
  onRunningChange
25665
24708
  }) {
24709
+ const queryClient = useQueryClient();
24710
+ const { workOSOrganizationId } = useElevasisServices();
24711
+ const [wasRunning, setWasRunning] = useState(false);
25666
24712
  const inFlightQuery = useInFlightExecutions(resourceId, {
25667
24713
  enabled: Boolean(resourceId),
25668
24714
  limit: 5,
24715
+ listId,
25669
24716
  refetchInterval: 2e3
25670
24717
  });
25671
24718
  const isRunning = (inFlightQuery.data?.executions.length ?? 0) > 0;
24719
+ useExecutionSSE(resourceId, {
24720
+ enabled: Boolean(resourceId) && isRunning,
24721
+ listId
24722
+ });
25672
24723
  useEffect(() => {
24724
+ if (!inFlightQuery.isFetched) return;
25673
24725
  onRunningChange(resourceId, isRunning);
24726
+ if (wasRunning && !isRunning) {
24727
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.executionsBase(workOSOrganizationId, listId) });
24728
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.progress(workOSOrganizationId, listId) });
24729
+ }
24730
+ setWasRunning(isRunning);
25674
24731
  return () => onRunningChange(resourceId, false);
25675
- }, [isRunning, onRunningChange, resourceId]);
24732
+ }, [
24733
+ inFlightQuery.isFetched,
24734
+ isRunning,
24735
+ listId,
24736
+ onRunningChange,
24737
+ queryClient,
24738
+ resourceId,
24739
+ wasRunning,
24740
+ workOSOrganizationId
24741
+ ]);
25676
24742
  return null;
25677
24743
  }
25678
24744
  function StepRecordsPanel({
25679
24745
  list,
25680
24746
  step,
25681
- credentialSelections,
25682
- credentials,
25683
- clickupListId
24747
+ exportWorkflowId,
24748
+ stageCatalog,
24749
+ approvalByCompanyId,
24750
+ onApprovalChange
25684
24751
  }) {
25685
- const queryClient = useQueryClient();
25686
24752
  const [page, setPage] = useState(1);
25687
- const [approvalByCompanyId, setApprovalByCompanyId] = useState({});
25688
- const navigate = useNavigate();
25689
24753
  const recordsStageKey = step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey;
25690
24754
  const recordsEntity = step.recordEntity ?? step.primaryEntity;
25691
24755
  const columns = step.recordColumns?.[recordsEntity] ?? [];
@@ -25695,59 +24759,19 @@ function StepRecordsPanel({
25695
24759
  limit: RECORDS_PAGE_SIZE,
25696
24760
  offset: (page - 1) * RECORDS_PAGE_SIZE
25697
24761
  });
25698
- const exportWorkflowId = step.action?.resourceId ?? EXPORT_WORKFLOW_ID;
25699
- const exportExecution = useWorkflowExecution({
25700
- workflowId: exportWorkflowId,
25701
- listId: list.id
25702
- });
25703
24762
  useEffect(() => {
25704
24763
  setPage(1);
25705
- setApprovalByCompanyId({});
25706
24764
  }, [step.id]);
25707
24765
  const rows = recordsQuery.data?.data ?? [];
25708
24766
  const total = recordsQuery.data?.total ?? 0;
25709
24767
  const totalPages = Math.max(1, Math.ceil(total / RECORDS_PAGE_SIZE));
25710
- const isReviewExportStep = step.primaryEntity === "company" && (step.actionKey === "lead-gen.export.list" || exportWorkflowId === EXPORT_WORKFLOW_ID);
24768
+ const isReviewExportStep = step.primaryEntity === "company" && (step.actionKey === "lead-gen.export.list" || step.action?.resourceId === exportWorkflowId);
25711
24769
  const approvedCompanyIds = Object.entries(approvalByCompanyId).filter(([, status]) => status === "approved").map(([companyId]) => companyId);
25712
24770
  const rejectedCompanyIds = Object.entries(approvalByCompanyId).filter(([, status]) => status === "rejected").map(([companyId]) => companyId);
25713
- const clickupRequirement = getClickUpRequirement(step);
25714
- const clickupCredential = clickupRequirement ? getSelectedCredential(credentials, credentialSelections, clickupRequirement) : null;
25715
- const isClickUpExportPath = isReviewExportStep && Boolean(clickupRequirement);
25716
- const normalizedClickupListId = clickupListId.trim();
25717
- const canExport = approvedCompanyIds.length > 0 && (!isClickUpExportPath || Boolean(clickupCredential) && normalizedClickupListId.length > 0);
25718
- const exportDisabledReason = approvedCompanyIds.length === 0 ? "Approve at least one company before export." : isClickUpExportPath && !clickupCredential ? "Select a ClickUp credential in Configuration." : isClickUpExportPath && !normalizedClickupListId ? "Enter a ClickUp List ID in Configuration." : null;
25719
24771
  const setApproval = (companyId, status) => {
25720
- setApprovalByCompanyId((current) => ({ ...current, [companyId]: status }));
25721
- };
25722
- const openRecordDetail = (row) => {
25723
- if (row.entity === "company") {
25724
- void navigate({ to: "/crm/companies/$companyId", params: { companyId: row.companyId } });
25725
- return;
25726
- }
25727
- void navigate({ to: "/crm/contacts/$contactId", params: { contactId: row.contactId } });
25728
- };
25729
- const exportApproved = async () => {
25730
- try {
25731
- const result = await exportExecution.execute({
25732
- input: {
25733
- listId: list.id,
25734
- mode: "export",
25735
- approved: true,
25736
- ...isClickUpExportPath ? {
25737
- destination: "clickup",
25738
- clickupCredential: clickupCredential?.name,
25739
- clickupListId: normalizedClickupListId
25740
- } : {},
25741
- approvedCompanyIds,
25742
- rejectedCompanyIds
25743
- }
25744
- });
25745
- showSuccessNotification(`Export workflow run started: ${result.executionId}`);
25746
- await queryClient.invalidateQueries({ queryKey: acquisitionListKeys.all });
25747
- } catch (error) {
25748
- showApiErrorNotification(error);
25749
- }
24772
+ onApprovalChange(companyId, status);
25750
24773
  };
24774
+ const getRecordDetailHref = (row) => row.entity === "company" ? `/crm/companies/${row.companyId}` : `/crm/contacts/${row.contactId}`;
25751
24775
  if (!columns.length) {
25752
24776
  return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No record columns are configured for this step." });
25753
24777
  }
@@ -25760,7 +24784,7 @@ function StepRecordsPanel({
25760
24784
  if (!rows.length) {
25761
24785
  return /* @__PURE__ */ jsx(Stack, { gap: "xs", children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
25762
24786
  "No records found for ",
25763
- LEAD_GEN_STAGE_CATALOG6[recordsStageKey]?.label ?? recordsStageKey,
24787
+ stageCatalog[recordsStageKey]?.label ?? recordsStageKey,
25764
24788
  "."
25765
24789
  ] }) });
25766
24790
  }
@@ -25773,7 +24797,7 @@ function StepRecordsPanel({
25773
24797
  " ",
25774
24798
  recordsEntity,
25775
24799
  " records from ",
25776
- LEAD_GEN_STAGE_CATALOG6[recordsStageKey]?.label ?? recordsStageKey
24800
+ stageCatalog[recordsStageKey]?.label ?? recordsStageKey
25777
24801
  ] })
25778
24802
  ] }),
25779
24803
  recordsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
@@ -25783,13 +24807,15 @@ function StepRecordsPanel({
25783
24807
  columns.map((column) => /* @__PURE__ */ jsx(Table.Th, { style: column.width ? { width: column.width } : void 0, children: column.label }, column.key)),
25784
24808
  /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
25785
24809
  /* @__PURE__ */ jsx(Table.Th, { children: "Failure reason" }),
25786
- isReviewExportStep ? /* @__PURE__ */ jsx(Table.Th, { children: "Approval" }) : null
24810
+ isReviewExportStep ? /* @__PURE__ */ jsx(Table.Th, { children: "Approval" }) : null,
24811
+ /* @__PURE__ */ jsx(Table.Th, { w: 44, "aria-label": "Open record" })
25787
24812
  ] }) }),
25788
24813
  /* @__PURE__ */ jsx(Table.Tbody, { children: rows.map((row) => {
25789
24814
  const columnSource = getRecordColumnSource(row);
25790
24815
  const status = getRecordStageStatus(row, recordsStageKey);
25791
24816
  const companyId = row.entity === "company" ? row.companyId : null;
25792
- return /* @__PURE__ */ jsxs(Table.Tr, { onClick: () => openRecordDetail(row), style: { cursor: "pointer" }, children: [
24817
+ const recordHref = getRecordDetailHref(row);
24818
+ return /* @__PURE__ */ jsxs(Table.Tr, { children: [
25793
24819
  columns.map((column) => /* @__PURE__ */ jsx(Table.Td, { children: renderRecordValue(valueAtPath(columnSource, column.path), column) }, column.key)),
25794
24820
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRecordStatusColor(status), children: status }) }),
25795
24821
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", lineClamp: 2, children: getStageFailureReason(row, recordsStageKey) }) }),
@@ -25820,43 +24846,48 @@ function StepRecordsPanel({
25820
24846
  children: "Reject"
25821
24847
  }
25822
24848
  )
25823
- ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "-" }) }) : null
24849
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "-" }) }) : null,
24850
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Tooltip, { label: "Open record", children: /* @__PURE__ */ jsx(
24851
+ ActionIcon,
24852
+ {
24853
+ component: "a",
24854
+ href: recordHref,
24855
+ target: "_blank",
24856
+ rel: "noopener noreferrer",
24857
+ size: "sm",
24858
+ variant: "subtle",
24859
+ "aria-label": `Open ${getRecordDisplayName(row)}`,
24860
+ children: /* @__PURE__ */ jsx(IconExternalLink, { size: 14 })
24861
+ }
24862
+ ) }) })
25824
24863
  ] }, row.id);
25825
24864
  }) })
25826
24865
  ] }) }),
25827
24866
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
25828
24867
  /* @__PURE__ */ jsx(Pagination, { size: "sm", total: totalPages, value: page, onChange: setPage, disabled: recordsQuery.isFetching }),
25829
- isReviewExportStep ? /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
25830
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
25831
- approvedCompanyIds.length,
25832
- " approved, ",
25833
- rejectedCompanyIds.length,
25834
- " rejected"
25835
- ] }),
25836
- exportDisabledReason ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: exportDisabledReason }) : null,
25837
- /* @__PURE__ */ jsx(
25838
- Button,
25839
- {
25840
- size: "xs",
25841
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }),
25842
- loading: exportExecution.isPending,
25843
- disabled: !canExport,
25844
- onClick: () => void exportApproved(),
25845
- children: isClickUpExportPath ? "Export to ClickUp" : "Export approved"
25846
- }
25847
- )
25848
- ] }) : null
24868
+ isReviewExportStep ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
24869
+ approvedCompanyIds.length,
24870
+ " approved, ",
24871
+ rejectedCompanyIds.length,
24872
+ " rejected"
24873
+ ] }) }) : null
25849
24874
  ] })
25850
24875
  ] });
25851
24876
  }
25852
24877
  function BuildTab({
25853
24878
  list,
25854
24879
  steps,
24880
+ executions,
25855
24881
  recommendedStep,
24882
+ optimisticRuns,
24883
+ exportWorkflowId,
24884
+ stageCatalog,
25856
24885
  onRunStep,
25857
24886
  onRetryStep,
25858
24887
  onViewRunLog
25859
24888
  }) {
24889
+ const queryClient = useQueryClient();
24890
+ const { workOSOrganizationId } = useElevasisServices();
25860
24891
  const [selectedStepId, setSelectedStepId] = useState(null);
25861
24892
  const [stepInputFormState, setStepInputFormState] = useState({});
25862
24893
  const [stepInputError, setStepInputError] = useState(null);
@@ -25865,7 +24896,9 @@ function BuildTab({
25865
24896
  const [credentialSelections, setCredentialSelections] = useState({});
25866
24897
  const [credentialVerificationById, setCredentialVerificationById] = useState({});
25867
24898
  const [clickupListId, setClickupListId] = useState("");
24899
+ const [reviewExportApprovals, setReviewExportApprovals] = useState({});
25868
24900
  const [runningResources, setRunningResources] = useState({});
24901
+ const lastObservedTerminalRunRef = useRef(null);
25869
24902
  const [stepInputResetKey, setStepInputResetKey] = useState(0);
25870
24903
  const currentStep = getDisplayStep(steps, recommendedStep);
25871
24904
  const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
@@ -25876,6 +24909,8 @@ function BuildTab({
25876
24909
  const stepSchema = selectedStep?.action?.schema;
25877
24910
  const stepLayout = selectedStep?.action?.layout;
25878
24911
  const credentialRequirements = selectedStep?.credentialRequirements ?? [];
24912
+ const selectedStepIsReviewExport = selectedStep?.primaryEntity === "company" && (selectedStep.actionKey === "lead-gen.export.list" || selectedStep.action?.resourceId === exportWorkflowId);
24913
+ const selectedExportWorkflowId = selectedStep?.action?.resourceId ?? exportWorkflowId ?? "";
25879
24914
  const visibleStepLayout = useMemo(
25880
24915
  () => filterCredentialRequirementFields(stepLayout, credentialRequirements),
25881
24916
  [stepLayout, credentialRequirements]
@@ -25883,33 +24918,74 @@ function BuildTab({
25883
24918
  const showMainStepConfig = stepSchema && hasMainConfigFields(visibleStepLayout);
25884
24919
  const showAdvancedStepConfig = stepSchema && hasAdvancedConfigFields(visibleStepLayout);
25885
24920
  const recentRunsQuery = useListExecutions(list.id, { resourceId: selectedResourceId, limit: 5 });
25886
- const recentRuns = useMemo(() => recentRunsQuery.data ?? [], [recentRunsQuery.data]);
24921
+ const recentRuns = useMemo(() => {
24922
+ const serverRuns = recentRunsQuery.data ?? [];
24923
+ const serverExecutionIds = new Set(serverRuns.map((run2) => run2.executionId));
24924
+ const pendingRuns = optimisticRuns.filter(
24925
+ (run2) => run2.resourceId === selectedResourceId && !serverExecutionIds.has(run2.executionId)
24926
+ );
24927
+ return [...pendingRuns, ...serverRuns].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 5);
24928
+ }, [optimisticRuns, recentRunsQuery.data, selectedResourceId]);
24929
+ const runningResourceIds = useMemo(() => {
24930
+ const ids = /* @__PURE__ */ new Set();
24931
+ const serverExecutionIds = new Set(executions.map((run2) => run2.executionId));
24932
+ executions.forEach((run2) => {
24933
+ if (run2.resourceId && isRunInFlight(run2.status)) ids.add(run2.resourceId);
24934
+ });
24935
+ optimisticRuns.forEach((run2) => {
24936
+ if (serverExecutionIds.has(run2.executionId)) return;
24937
+ if (run2.resourceId && isRunInFlight(run2.status)) ids.add(run2.resourceId);
24938
+ });
24939
+ return ids;
24940
+ }, [executions, optimisticRuns]);
24941
+ const runningStep = steps.find((step) => {
24942
+ const resourceId = step.action?.resourceId;
24943
+ return resourceId ? (runningResources[resourceId] ?? false) || runningResourceIds.has(resourceId) : false;
24944
+ }) ?? null;
24945
+ const latestRecentRun = recentRuns[0] ?? null;
25887
24946
  const credentialsQuery = useCredentials();
25888
24947
  const credentials = credentialsQuery.data ?? [];
25889
24948
  const verifyCredential = useVerifyCredential();
24949
+ const exportExecution = useWorkflowExecution({
24950
+ workflowId: selectedExportWorkflowId,
24951
+ listId: list.id
24952
+ });
24953
+ const approvedCompanyIds = Object.entries(reviewExportApprovals).filter(([, status]) => status === "approved").map(([companyId]) => companyId);
24954
+ const rejectedCompanyIds = Object.entries(reviewExportApprovals).filter(([, status]) => status === "rejected").map(([companyId]) => companyId);
24955
+ const selectedClickupRequirement = selectedStep ? getClickUpRequirement(selectedStep) : null;
24956
+ const selectedClickupCredential = selectedClickupRequirement ? getSelectedCredential(credentials, credentialSelections, selectedClickupRequirement) : null;
24957
+ const isSelectedClickUpExportPath = selectedStepIsReviewExport && Boolean(selectedClickupRequirement);
24958
+ const normalizedClickupListId = clickupListId.trim();
24959
+ const canExportSelectedRecords = selectedStepIsReviewExport && activeRightColumnTab === "records" && approvedCompanyIds.length > 0 && (!isSelectedClickUpExportPath || Boolean(selectedClickupCredential) && normalizedClickupListId.length > 0);
24960
+ const selectedExportDisabledReason = !selectedStepIsReviewExport || activeRightColumnTab !== "records" ? null : approvedCompanyIds.length === 0 ? "Approve records" : isSelectedClickUpExportPath && !selectedClickupCredential ? "Select ClickUp credential" : isSelectedClickUpExportPath && !normalizedClickupListId ? "Enter ClickUp List ID" : null;
25890
24961
  const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
25891
- const selectedResourceIsRunning = selectedResourceId ? runningResources[selectedResourceId] ?? false : false;
25892
- const selectedActionDisabled = !canRunSelectedAction || selectedResourceIsRunning;
25893
- const selectedActionLabel = selectedResourceIsRunning ? "Running..." : selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind);
25894
- const selectedActionIcon = selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
24962
+ const selectedResourceIsRunning = selectedResourceId ? (runningResources[selectedResourceId] ?? false) || runningResourceIds.has(selectedResourceId) : false;
24963
+ const selectedActionDisabled = selectedStepIsReviewExport && activeRightColumnTab !== "records" ? false : selectedStepIsReviewExport ? !canExportSelectedRecords || exportExecution.isPending : !canRunSelectedAction || selectedResourceIsRunning;
24964
+ const selectedActionLabel = selectedResourceIsRunning ? "Running..." : selectedStepIsReviewExport && activeRightColumnTab !== "records" ? "Approve records" : selectedStepIsReviewExport ? selectedExportDisabledReason ?? (isSelectedClickUpExportPath ? "Export to ClickUp" : "Export approved") : selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind);
24965
+ const selectedActionIcon = selectedStepIsReviewExport && activeRightColumnTab !== "records" ? /* @__PURE__ */ jsx(IconAddressBook, { size: 14 }) : selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
25895
24966
  const handleResourceRunningChange = useCallback((resourceId, isRunning) => {
25896
24967
  setRunningResources((current) => {
25897
24968
  if (current[resourceId] === isRunning) return current;
25898
24969
  return { ...current, [resourceId]: isRunning };
25899
24970
  });
25900
24971
  }, []);
25901
- const handleSelectedResourceRunningChange = useCallback(
25902
- (isRunning) => {
25903
- if (!selectedResourceId) return;
25904
- handleResourceRunningChange(selectedResourceId, isRunning);
25905
- },
25906
- [handleResourceRunningChange, selectedResourceId]
25907
- );
24972
+ useEffect(() => {
24973
+ if (!selectedResourceId || !latestRecentRun || !isRunTerminal(latestRecentRun.status)) return;
24974
+ if (lastObservedTerminalRunRef.current === latestRecentRun.executionId) return;
24975
+ lastObservedTerminalRunRef.current = latestRecentRun.executionId;
24976
+ handleResourceRunningChange(selectedResourceId, false);
24977
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.detail(workOSOrganizationId, list.id) });
24978
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.progress(workOSOrganizationId, list.id) });
24979
+ queryClient.invalidateQueries({
24980
+ queryKey: [...acquisitionListKeys.all, "records", workOSOrganizationId, list.id]
24981
+ });
24982
+ }, [handleResourceRunningChange, latestRecentRun, list.id, queryClient, selectedResourceId, workOSOrganizationId]);
25908
24983
  useEffect(() => {
25909
24984
  setStepInputFormState(getInitialStepInput(list, selectedStep?.action));
25910
24985
  setStepInputError(null);
25911
24986
  setStepInputInitialized(false);
25912
24987
  setStepInputResetKey((k) => k + 1);
24988
+ setReviewExportApprovals({});
25913
24989
  }, [list, selectedStep?.action]);
25914
24990
  useEffect(() => {
25915
24991
  if (stepInputInitialized) return;
@@ -25917,7 +24993,7 @@ function BuildTab({
25917
24993
  if (recentRuns.length > 0) {
25918
24994
  const lastInput = getRunInput(recentRuns[0]);
25919
24995
  if (lastInput) {
25920
- setStepInputFormState(sanitizeStepInput(asRecord3(lastInput)));
24996
+ setStepInputFormState(sanitizeStepInput(asRecord2(lastInput), selectedCapabilityKey));
25921
24997
  setStepInputResetKey((k) => k + 1);
25922
24998
  }
25923
24999
  }
@@ -25964,8 +25040,42 @@ function BuildTab({
25964
25040
  setStepInputFormState((current) => setValueAtInputPath(current, "clickupListId", value));
25965
25041
  if (stepInputError) setStepInputError(null);
25966
25042
  };
25967
- const handleSelectedAction = () => {
25043
+ const handleReviewExportApprovalChange = (companyId, status) => {
25044
+ setReviewExportApprovals((current) => ({ ...current, [companyId]: status }));
25045
+ if (stepInputError) setStepInputError(null);
25046
+ };
25047
+ const exportApprovedRecords = async () => {
25048
+ try {
25049
+ const result = await exportExecution.execute({
25050
+ input: {
25051
+ listId: list.id,
25052
+ mode: "export",
25053
+ approved: true,
25054
+ ...isSelectedClickUpExportPath ? {
25055
+ destination: "clickup",
25056
+ clickupCredential: selectedClickupCredential?.name,
25057
+ clickupListId: normalizedClickupListId
25058
+ } : {},
25059
+ approvedCompanyIds,
25060
+ rejectedCompanyIds
25061
+ }
25062
+ });
25063
+ showSuccessNotification(`Export workflow run started: ${result.executionId}`);
25064
+ await queryClient.invalidateQueries({ queryKey: acquisitionListKeys.all });
25065
+ } catch (error) {
25066
+ showApiErrorNotification(error);
25067
+ }
25068
+ };
25069
+ const handleSelectedAction = async () => {
25968
25070
  if (!selectedStep || selectedActionDisabled) return;
25071
+ if (selectedStepIsReviewExport && activeRightColumnTab !== "records") {
25072
+ setActiveRightColumnTab("records");
25073
+ return;
25074
+ }
25075
+ if (selectedStepIsReviewExport) {
25076
+ await exportApprovedRecords();
25077
+ return;
25078
+ }
25969
25079
  if (stepSchema) {
25970
25080
  const parsed = stepSchema.safeParse(stepInputFormState);
25971
25081
  if (!parsed.success) {
@@ -25976,14 +25086,22 @@ function BuildTab({
25976
25086
  }
25977
25087
  setStepInputError(null);
25978
25088
  const submittedInput = stepSchema ? stepInputFormState : {};
25979
- if (selectedActionKind === "retry_failed") {
25980
- onRetryStep(selectedStep, submittedInput);
25981
- return;
25089
+ setActiveRightColumnTab("runs");
25090
+ const resourceId = selectedStep.action?.resourceId ?? null;
25091
+ try {
25092
+ let result = null;
25093
+ if (selectedActionKind === "retry_failed") {
25094
+ result = await onRetryStep(selectedStep, submittedInput);
25095
+ } else {
25096
+ result = await onRunStep(selectedStep, submittedInput);
25097
+ }
25098
+ if (resourceId && result?.executionId) handleResourceRunningChange(resourceId, true);
25099
+ } catch {
25100
+ if (resourceId) handleResourceRunningChange(resourceId, false);
25982
25101
  }
25983
- onRunStep(selectedStep, submittedInput);
25984
25102
  };
25985
25103
  const loadRecentRunInput = (run2) => {
25986
- setStepInputFormState(sanitizeStepInput(asRecord3(getRunInput(run2))));
25104
+ setStepInputFormState(sanitizeStepInput(asRecord2(getRunInput(run2)), selectedCapabilityKey));
25987
25105
  setStepInputResetKey((k) => k + 1);
25988
25106
  setActiveRightColumnTab("configuration");
25989
25107
  };
@@ -25992,242 +25110,231 @@ function BuildTab({
25992
25110
  if (!resourceId) return;
25993
25111
  onViewRunLog(resourceId, run2.executionId);
25994
25112
  };
25995
- return /* @__PURE__ */ jsx(TabSection, { icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), title: "Build", description: getBuildTabDescription(steps, currentStep), children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
25996
- /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", style: { flex: 1, minHeight: 0 }, children: [
25997
- steps.map(
25998
- (step) => step.action?.resourceId ? /* @__PURE__ */ jsx(
25999
- BuildStepRunningWatcher,
26000
- {
26001
- resourceId: step.action.resourceId,
26002
- onRunningChange: handleResourceRunningChange
26003
- },
26004
- `running-${step.id}`
26005
- ) : null
26006
- ),
26007
- steps.map((step, index) => {
26008
- const isCurrent = currentStep?.id === step.id;
26009
- const isSelected = selectedStep?.id === step.id;
26010
- const stepResourceId = step.action?.resourceId ?? null;
26011
- const isStepRunning = stepResourceId ? runningResources[stepResourceId] ?? false : false;
26012
- const isWaiting = step.status === "blocked";
26013
- const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
26014
- const stepTone = isStepRunning ? "ready" : isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
26015
- return /* @__PURE__ */ jsx(
26016
- UnstyledButton,
26017
- {
26018
- onClick: () => setSelectedStepId(step.id),
26019
- style: {
26020
- backgroundColor: isSelected ? "var(--surface-primary-strong)" : isStepRunning ? "color-mix(in srgb, var(--color-primary) 12%, transparent)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
26021
- border: isSelected ? "var(--active-border)" : isStepRunning ? "1px solid var(--border-primary-muted)" : "1px solid var(--color-border)",
26022
- borderStyle: isPassiveWaiting && !isStepRunning ? "dashed" : "solid",
26023
- borderRadius: 8,
26024
- display: "block",
26025
- opacity: isPassiveWaiting && !isStepRunning ? 0.72 : 1,
26026
- padding: 12,
26027
- textAlign: "left",
26028
- width: "100%"
26029
- },
26030
- children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
26031
- /* @__PURE__ */ jsx(
26032
- Box,
26033
- {
26034
- w: 28,
26035
- h: 28,
26036
- style: {
26037
- alignItems: "center",
26038
- ...getBuildToneStyle(stepTone),
26039
- borderRadius: 999,
26040
- borderStyle: "solid",
26041
- borderWidth: 1,
26042
- display: "flex",
26043
- flexShrink: 0,
26044
- fontWeight: 700,
26045
- justifyContent: "center"
26046
- },
26047
- children: index + 1
26048
- }
26049
- ),
26050
- /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
26051
- /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
26052
- /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting && !isStepRunning ? "dimmed" : void 0, truncate: true, children: step.label }),
26053
- isStepRunning ? /* @__PURE__ */ jsx(StatusPill, { label: "Running", tone: "ready" }) : null,
26054
- !isStepRunning && isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
26055
- !isStepRunning && !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
26056
- ] }),
26057
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
26058
- /* @__PURE__ */ jsx(StepCounterLine, { step })
26059
- ] })
26060
- ] })
26061
- },
26062
- step.id
26063
- );
26064
- })
26065
- ] }) }),
26066
- selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", style: { flex: 1, minHeight: 0 }, children: [
26067
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0, flexShrink: 0 }, children: [
26068
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
26069
- "Step ",
26070
- selectedStepIndex + 1,
26071
- " details"
26072
- ] }),
26073
- /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
26074
- ] }),
26075
- /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
26076
- /* @__PURE__ */ jsx(
26077
- StepDetailRightColumn,
26078
- {
26079
- activeTab: activeRightColumnTab,
26080
- onTabChange: setActiveRightColumnTab,
26081
- configuration: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
26082
- selectedResourceId && selectedStep.stageKey ? /* @__PURE__ */ jsx(
26083
- LeadGenLiveStatusPanel,
25113
+ return /* @__PURE__ */ jsx(
25114
+ TabSection,
25115
+ {
25116
+ icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }),
25117
+ title: "Build",
25118
+ description: getBuildTabDescription(steps, currentStep, runningStep),
25119
+ children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
25120
+ /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", style: { flex: 1, minHeight: 0 }, children: [
25121
+ steps.map(
25122
+ (step) => step.action?.resourceId ? /* @__PURE__ */ jsx(
25123
+ BuildStepRunningWatcher,
26084
25124
  {
26085
25125
  listId: list.id,
26086
- resourceId: selectedResourceId,
26087
- stageKey: selectedStep.stageKey,
26088
- stepLabel: selectedStep.label,
26089
- enabled: true,
26090
- onRunningChange: handleSelectedResourceRunningChange
25126
+ resourceId: step.action.resourceId,
25127
+ onRunningChange: handleResourceRunningChange
26091
25128
  },
26092
- selectedResourceId
26093
- ) : null,
26094
- selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
26095
- selectedStep.outputs.includes(selectedStep.primaryEntity) && selectedStep.ready === 0 && selectedStep.complete === 0 && selectedStep.failed === 0 && selectedStep.blocked === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Run this step to populate members." }) : null,
26096
- !selectedStep.outputs.includes(selectedStep.primaryEntity) && selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
26097
- "Batch size: ",
26098
- selectedStep.recommendedAction.defaultSize,
26099
- " default,",
26100
- " ",
26101
- selectedStep.recommendedAction.maxSize,
26102
- " max."
26103
- ] }) : null,
26104
- showMainStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
26105
- StepConfigForm,
25129
+ `running-${step.id}`
25130
+ ) : null
25131
+ ),
25132
+ steps.map((step, index) => {
25133
+ const isCurrent = currentStep?.id === step.id;
25134
+ const isSelected = selectedStep?.id === step.id;
25135
+ const stepResourceId = step.action?.resourceId ?? null;
25136
+ const isStepRunning = stepResourceId ? (runningResources[stepResourceId] ?? false) || runningResourceIds.has(stepResourceId) : false;
25137
+ const isWaiting = step.status === "blocked";
25138
+ const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
25139
+ const stepTone = isStepRunning ? "ready" : isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
25140
+ return /* @__PURE__ */ jsx(
25141
+ UnstyledButton,
26106
25142
  {
26107
- slot: "main",
26108
- schema: stepSchema,
26109
- layout: visibleStepLayout,
26110
- value: stepInputFormState,
26111
- onChange: (v) => handleStepInputChange(asRecord3(v) ?? {}),
26112
- disabled: !canRunSelectedAction
25143
+ onClick: () => setSelectedStepId(step.id),
25144
+ style: {
25145
+ backgroundColor: isSelected ? "var(--surface-primary-strong)" : isStepRunning ? "color-mix(in srgb, var(--color-primary) 12%, transparent)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
25146
+ border: isSelected ? "var(--active-border)" : isStepRunning ? "1px solid var(--border-primary-muted)" : "1px solid var(--color-border)",
25147
+ borderStyle: isPassiveWaiting && !isStepRunning ? "dashed" : "solid",
25148
+ borderRadius: 8,
25149
+ display: "block",
25150
+ opacity: isPassiveWaiting && !isStepRunning ? 0.72 : 1,
25151
+ padding: 12,
25152
+ textAlign: "left",
25153
+ width: "100%"
25154
+ },
25155
+ children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
25156
+ /* @__PURE__ */ jsx(
25157
+ Box,
25158
+ {
25159
+ w: 28,
25160
+ h: 28,
25161
+ style: {
25162
+ alignItems: "center",
25163
+ ...getBuildToneStyle(stepTone),
25164
+ borderRadius: 999,
25165
+ borderStyle: "solid",
25166
+ borderWidth: 1,
25167
+ display: "flex",
25168
+ flexShrink: 0,
25169
+ fontWeight: 700,
25170
+ justifyContent: "center"
25171
+ },
25172
+ children: index + 1
25173
+ }
25174
+ ),
25175
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
25176
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
25177
+ /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting && !isStepRunning ? "dimmed" : void 0, truncate: true, children: step.label }),
25178
+ !isStepRunning && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
25179
+ ] }),
25180
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
25181
+ /* @__PURE__ */ jsx(StepCounterLine, { step, isRunning: isStepRunning })
25182
+ ] })
25183
+ ] })
26113
25184
  },
26114
- `${selectedResourceId ?? "none"}-main-${stepInputResetKey}`
26115
- ) : !credentialRequirements.length ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No configuration for this step." }) : null,
26116
- /* @__PURE__ */ jsx(
26117
- CredentialRequirementsPanel,
26118
- {
26119
- requirements: credentialRequirements,
26120
- credentials,
26121
- credentialsLoading: credentialsQuery.isLoading,
26122
- selections: credentialSelections,
26123
- verificationByCredentialId: credentialVerificationById,
26124
- verifyingCredentialId: verifyCredential.isPending ? verifyCredential.variables ?? null : null,
26125
- onSelect: handleCredentialSelect,
26126
- onVerify: handleVerifyCredential
26127
- }
26128
- ),
26129
- selectedStep.actionKey === "lead-gen.export.list" || getClickUpRequirement(selectedStep) ? /* @__PURE__ */ jsx(
26130
- TextInput,
26131
- {
26132
- label: "ClickUp List ID",
26133
- description: "Destination List ID for approved company tasks.",
26134
- placeholder: "123456789",
26135
- value: clickupListId,
26136
- onChange: (event) => handleClickupListIdChange(event.currentTarget.value)
26137
- }
26138
- ) : null
25185
+ step.id
25186
+ );
25187
+ })
25188
+ ] }) }),
25189
+ selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", style: { flex: 1, minHeight: 0 }, children: [
25190
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0, flexShrink: 0 }, children: [
25191
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
25192
+ "Step ",
25193
+ selectedStepIndex + 1,
25194
+ " details"
25195
+ ] }),
25196
+ /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
26139
25197
  ] }),
26140
- advanced: showAdvancedStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
26141
- StepConfigForm,
26142
- {
26143
- slot: "advanced",
26144
- schema: stepSchema,
26145
- layout: visibleStepLayout,
26146
- value: stepInputFormState,
26147
- onChange: (v) => handleStepInputChange(asRecord3(v) ?? {}),
26148
- disabled: !canRunSelectedAction
26149
- },
26150
- `${selectedResourceId ?? "none"}-advanced-${stepInputResetKey}`
26151
- ) : void 0,
26152
- records: /* @__PURE__ */ jsx(
26153
- StepRecordsPanel,
25198
+ /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep, isRunning: selectedResourceIsRunning }),
25199
+ /* @__PURE__ */ jsx(
25200
+ StepDetailRightColumn,
26154
25201
  {
26155
- list,
26156
- step: selectedStep,
26157
- credentialSelections,
26158
- credentials,
26159
- clickupListId
26160
- },
26161
- selectedStep.id
26162
- ),
26163
- runs: selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
26164
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
26165
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
26166
- recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
26167
- ] }),
26168
- !recentRuns.length && !recentRunsQuery.isLoading ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No runs yet for this step." }) : /* @__PURE__ */ jsx(Stack, { gap: 4, children: recentRuns.map((run2) => {
26169
- const canUseSettings = hasRunInput(run2);
26170
- return /* @__PURE__ */ jsx(
26171
- Box,
26172
- {
26173
- style: {
26174
- border: "1px solid var(--color-border)",
26175
- borderRadius: 8,
26176
- padding: "8px 10px",
26177
- width: "100%"
25202
+ activeTab: activeRightColumnTab,
25203
+ onTabChange: setActiveRightColumnTab,
25204
+ configuration: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
25205
+ showMainStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
25206
+ StepConfigForm,
25207
+ {
25208
+ slot: "main",
25209
+ schema: stepSchema,
25210
+ layout: visibleStepLayout,
25211
+ value: stepInputFormState,
25212
+ onChange: (v) => handleStepInputChange(asRecord2(v) ?? {}),
25213
+ disabled: !canRunSelectedAction
26178
25214
  },
26179
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
26180
- /* @__PURE__ */ jsx(
26181
- UnstyledButton,
26182
- {
26183
- onClick: () => openRecentRunLog(run2),
26184
- style: { flex: 1, minWidth: 0, textAlign: "left" },
26185
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
26186
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
26187
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime5(run2.createdAt) }),
26188
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run2.executionId })
26189
- ] }),
26190
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run2.status), children: run2.status })
26191
- ] })
26192
- }
26193
- ),
26194
- /* @__PURE__ */ jsx(Tooltip, { label: "Use this run's settings", children: /* @__PURE__ */ jsx(
26195
- Button,
26196
- {
26197
- size: "xs",
26198
- variant: "subtle",
26199
- leftSection: /* @__PURE__ */ jsx(IconSettings, { size: 12 }),
26200
- disabled: !canUseSettings,
26201
- onClick: () => loadRecentRunInput(run2),
26202
- children: "Use settings"
26203
- }
26204
- ) })
26205
- ] })
25215
+ `${selectedResourceId ?? "none"}-main-${stepInputResetKey}`
25216
+ ) : !credentialRequirements.length ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No configuration for this step." }) : null,
25217
+ /* @__PURE__ */ jsx(
25218
+ CredentialRequirementsPanel,
25219
+ {
25220
+ requirements: credentialRequirements,
25221
+ credentials,
25222
+ credentialsLoading: credentialsQuery.isLoading,
25223
+ credentialsError: credentialsQuery.error instanceof Error ? credentialsQuery.error : null,
25224
+ selections: credentialSelections,
25225
+ verificationByCredentialId: credentialVerificationById,
25226
+ verifyingCredentialId: verifyCredential.isPending ? verifyCredential.variables ?? null : null,
25227
+ onSelect: handleCredentialSelect,
25228
+ onVerify: handleVerifyCredential
25229
+ }
25230
+ ),
25231
+ selectedStep.actionKey === "lead-gen.export.list" || getClickUpRequirement(selectedStep) ? /* @__PURE__ */ jsx(
25232
+ TextInput,
25233
+ {
25234
+ label: "ClickUp List ID",
25235
+ description: "Destination List ID for approved company tasks.",
25236
+ placeholder: "123456789",
25237
+ value: clickupListId,
25238
+ onChange: (event) => handleClickupListIdChange(event.currentTarget.value)
25239
+ }
25240
+ ) : null
25241
+ ] }),
25242
+ advanced: showAdvancedStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
25243
+ StepConfigForm,
25244
+ {
25245
+ slot: "advanced",
25246
+ schema: stepSchema,
25247
+ layout: visibleStepLayout,
25248
+ value: stepInputFormState,
25249
+ onChange: (v) => handleStepInputChange(asRecord2(v) ?? {}),
25250
+ disabled: !canRunSelectedAction
26206
25251
  },
26207
- run2.executionId
26208
- );
26209
- }) })
26210
- ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No runs available for this step." }),
26211
- action: /* @__PURE__ */ jsxs(Fragment, { children: [
26212
- stepInputError ? /* @__PURE__ */ jsx(Alert, { color: "red", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), variant: "light", children: stepInputError }) : null,
26213
- /* @__PURE__ */ jsx(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: /* @__PURE__ */ jsx(
26214
- Button,
26215
- {
26216
- leftSection: selectedActionIcon,
26217
- disabled: selectedActionDisabled,
26218
- onClick: handleSelectedAction,
26219
- children: selectedActionLabel
26220
- }
26221
- ) })
26222
- ] })
26223
- }
26224
- )
26225
- ] }) }) : null
26226
- ] }) });
25252
+ `${selectedResourceId ?? "none"}-advanced-${stepInputResetKey}`
25253
+ ) : void 0,
25254
+ records: /* @__PURE__ */ jsx(
25255
+ StepRecordsPanel,
25256
+ {
25257
+ list,
25258
+ step: selectedStep,
25259
+ exportWorkflowId,
25260
+ stageCatalog,
25261
+ approvalByCompanyId: reviewExportApprovals,
25262
+ onApprovalChange: handleReviewExportApprovalChange
25263
+ },
25264
+ selectedStep.id
25265
+ ),
25266
+ runs: selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
25267
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
25268
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
25269
+ recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
25270
+ ] }),
25271
+ !recentRuns.length && !recentRunsQuery.isLoading ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No runs yet for this step." }) : /* @__PURE__ */ jsx(Stack, { gap: 4, children: recentRuns.map((run2) => {
25272
+ const canUseSettings = hasRunInput(run2);
25273
+ return /* @__PURE__ */ jsx(
25274
+ Box,
25275
+ {
25276
+ style: {
25277
+ border: "1px solid var(--color-border)",
25278
+ borderRadius: 8,
25279
+ padding: "8px 10px",
25280
+ width: "100%"
25281
+ },
25282
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
25283
+ /* @__PURE__ */ jsx(
25284
+ UnstyledButton,
25285
+ {
25286
+ onClick: () => openRecentRunLog(run2),
25287
+ style: { flex: 1, minWidth: 0, textAlign: "left" },
25288
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
25289
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
25290
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime4(run2.createdAt) }),
25291
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run2.executionId })
25292
+ ] }),
25293
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run2.status), children: run2.status })
25294
+ ] })
25295
+ }
25296
+ ),
25297
+ /* @__PURE__ */ jsx(Tooltip, { label: "Use this run's settings", children: /* @__PURE__ */ jsx(
25298
+ Button,
25299
+ {
25300
+ size: "xs",
25301
+ variant: "subtle",
25302
+ leftSection: /* @__PURE__ */ jsx(IconSettings, { size: 12 }),
25303
+ disabled: !canUseSettings,
25304
+ onClick: () => loadRecentRunInput(run2),
25305
+ children: "Use settings"
25306
+ }
25307
+ ) })
25308
+ ] })
25309
+ },
25310
+ run2.executionId
25311
+ );
25312
+ }) })
25313
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No runs available for this step." }),
25314
+ action: /* @__PURE__ */ jsxs(Fragment, { children: [
25315
+ stepInputError ? /* @__PURE__ */ jsx(Alert, { color: "red", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), variant: "light", children: stepInputError }) : null,
25316
+ /* @__PURE__ */ jsx(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", style: { minWidth: 0, overflowX: "hidden" }, children: /* @__PURE__ */ jsx(Tooltip, { label: selectedExportDisabledReason, disabled: !selectedExportDisabledReason, children: /* @__PURE__ */ jsx(
25317
+ Button,
25318
+ {
25319
+ leftSection: selectedActionIcon,
25320
+ disabled: selectedActionDisabled,
25321
+ onClick: handleSelectedAction,
25322
+ style: { maxWidth: "100%" },
25323
+ children: selectedActionLabel
25324
+ }
25325
+ ) }) })
25326
+ ] })
25327
+ }
25328
+ )
25329
+ ] }) }) : null
25330
+ ] })
25331
+ }
25332
+ );
26227
25333
  }
26228
25334
  function MembersTab({
26229
25335
  listId,
26230
25336
  progress,
25337
+ stageCatalog,
26231
25338
  onMemberClick
26232
25339
  }) {
26233
25340
  const [memberTab, setMemberTab] = useState("contacts");
@@ -26235,6 +25342,9 @@ function MembersTab({
26235
25342
  const companiesQuery = useCompanies({ listId, limit: 100, offset: 0 });
26236
25343
  const contacts = contactsQuery.data?.data ?? [];
26237
25344
  const companies = companiesQuery.data?.data ?? [];
25345
+ const activeQuery = memberTab === "contacts" ? contactsQuery : companiesQuery;
25346
+ const activeError = activeQuery.error ?? activeQuery.failureReason;
25347
+ const activeIsInitialLoading = activeQuery.isLoading && !activeError;
26238
25348
  return /* @__PURE__ */ jsxs(
26239
25349
  TabSection,
26240
25350
  {
@@ -26254,7 +25364,7 @@ function MembersTab({
26254
25364
  }
26255
25365
  ),
26256
25366
  children: [
26257
- memberTab === "contacts" && /* @__PURE__ */ jsx(Fragment, { children: contactsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !contacts.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No contacts attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
25367
+ memberTab === "contacts" && /* @__PURE__ */ jsx(Fragment, { children: activeError ? /* @__PURE__ */ jsx(CenteredErrorState, { error: activeError, title: "Failed to load members", h: 160 }) : activeIsInitialLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !contacts.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No contacts attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
26258
25368
  /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
26259
25369
  /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
26260
25370
  /* @__PURE__ */ jsx(Table.Th, { children: "Email" }),
@@ -26266,19 +25376,19 @@ function MembersTab({
26266
25376
  ] }) }),
26267
25377
  /* @__PURE__ */ jsx(Table.Tbody, { children: contacts.map((contact) => {
26268
25378
  const handleRowClick = () => onMemberClick?.(contact.id, "contact");
26269
- const memberStage = displayMemberStageFor(contact, "contact");
25379
+ const memberStage = displayMemberStageFor(contact, "contact", stageCatalog);
26270
25380
  return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
26271
25381
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: contactDisplayName(contact.firstName, contact.lastName) }) }),
26272
25382
  /* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
26273
25383
  /* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
26274
25384
  /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
26275
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor7(contact.status), children: contact.status }) }),
26276
- /* @__PURE__ */ jsx(Table.Td, { children: memberStage ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberStage.key), children: memberStage.label }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
26277
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime5(contact.createdAt) })
25385
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor6(contact.status), children: contact.status }) }),
25386
+ /* @__PURE__ */ jsx(Table.Td, { children: memberStage ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberStage.key, stageCatalog), children: memberStage.label }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
25387
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(contact.createdAt) })
26278
25388
  ] }, contact.id);
26279
25389
  }) })
26280
25390
  ] }) }),
26281
- memberTab === "companies" && /* @__PURE__ */ jsx(Fragment, { children: companiesQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !companies.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No companies attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
25391
+ memberTab === "companies" && /* @__PURE__ */ jsx(Fragment, { children: activeError ? /* @__PURE__ */ jsx(CenteredErrorState, { error: activeError, title: "Failed to load companies", h: 160 }) : activeIsInitialLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !companies.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No companies attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
26282
25392
  /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
26283
25393
  /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
26284
25394
  /* @__PURE__ */ jsx(Table.Th, { children: "Domain" }),
@@ -26290,15 +25400,15 @@ function MembersTab({
26290
25400
  ] }) }),
26291
25401
  /* @__PURE__ */ jsx(Table.Tbody, { children: companies.map((company) => {
26292
25402
  const handleRowClick = () => onMemberClick?.(company.id, "company");
26293
- const memberStage = displayMemberStageFor(company, "company");
25403
+ const memberStage = displayMemberStageFor(company, "company", stageCatalog);
26294
25404
  return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
26295
25405
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: company.name }) }),
26296
25406
  /* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
26297
25407
  /* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
26298
25408
  /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
26299
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor7(company.status), children: company.status }) }),
26300
- /* @__PURE__ */ jsx(Table.Td, { children: memberStage ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberStage.key), children: memberStage.label }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
26301
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime5(company.createdAt) })
25409
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor6(company.status), children: company.status }) }),
25410
+ /* @__PURE__ */ jsx(Table.Td, { children: memberStage ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberStage.key, stageCatalog), children: memberStage.label }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
25411
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(company.createdAt) })
26302
25412
  ] }, company.id);
26303
25413
  }) })
26304
25414
  ] }) })
@@ -26316,6 +25426,8 @@ function ListDetailHeader({
26316
25426
  function LeadGenListDetailPage({ listId }) {
26317
25427
  const navigate = useNavigate();
26318
25428
  const actions = useListActions();
25429
+ const leadGenConfig = useLeadGenConfig();
25430
+ const stageCatalog = leadGenConfig.stageCatalog ?? {};
26319
25431
  const rawSearch = useSearch({ strict: false });
26320
25432
  const memberId = rawSearch.member ?? null;
26321
25433
  const memberKind = rawSearch.memberKind ?? null;
@@ -26336,22 +25448,13 @@ function LeadGenListDetailPage({ listId }) {
26336
25448
  const progressQuery = useListProgress(listId);
26337
25449
  const executionsQuery = useListExecutions(listId);
26338
25450
  const deleteListMutation = useDeleteList();
25451
+ const executeAsync = useExecuteAsync();
25452
+ const queryClient = useQueryClient();
25453
+ const { workOSOrganizationId } = useElevasisServices();
26339
25454
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);
26340
- const [runModalOpen, setRunModalOpen] = useState(false);
26341
25455
  const [activeTab, setActiveTab] = useState("overview");
26342
25456
  const [runsResourceFilter, setRunsResourceFilter] = useState(null);
26343
- const [initialRunResourceId, setInitialRunResourceId] = useState(null);
26344
- const [pendingStepRunInput, setPendingStepRunInput] = useState(null);
26345
- const runModalActions = useMemo(() => {
26346
- if (!pendingStepRunInput) return actions;
26347
- return actions.map((action) => {
26348
- if (action.resourceId !== pendingStepRunInput.resourceId) return action;
26349
- return {
26350
- ...action,
26351
- defaultInput: (nextList) => mergeStepRunInput(nextList, action, pendingStepRunInput.inputAtSubmit)
26352
- };
26353
- });
26354
- }, [actions, pendingStepRunInput]);
25457
+ const [optimisticRuns, setOptimisticRuns] = useState([]);
26355
25458
  const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
26356
25459
  const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
26357
25460
  const backButton = /* @__PURE__ */ jsx(
@@ -26406,31 +25509,42 @@ function LeadGenListDetailPage({ listId }) {
26406
25509
  const list = listQuery.data;
26407
25510
  const progress = progressQuery.data;
26408
25511
  const executions = executionsQuery.data ?? [];
26409
- const businessProgress = deriveBusinessProgress(list, progress, actions, executions);
25512
+ const businessProgress = deriveBusinessProgress(list, progress, actions, executions, leadGenConfig);
26410
25513
  const currentBuildTemplateLabel = list.metadata.buildPlanSnapshot?.templateLabel ?? "Default lead generation";
26411
- const buildResolution = resolveBuildState(list, businessProgress, actions);
25514
+ const buildResolution = resolveBuildState(list, businessProgress, actions, leadGenConfig);
26412
25515
  const buildSteps = buildResolution.steps;
26413
25516
  const recommendedBuildStep = buildResolution.recommendedStep;
26414
- const openRunModal = (resourceId) => {
26415
- const nextResourceId = resourceId ?? actions[0]?.resourceId ?? null;
26416
- setInitialRunResourceId(nextResourceId);
26417
- setRunModalOpen(true);
26418
- };
26419
- const closeRunModal = () => {
26420
- setRunModalOpen(false);
26421
- setInitialRunResourceId(null);
26422
- setPendingStepRunInput(null);
26423
- };
26424
- const openStepRun = (step, inputAtSubmit) => {
26425
- if (step.action?.schema) {
26426
- setPendingStepRunInput({
26427
- resourceId: step.action.resourceId,
26428
- inputAtSubmit
25517
+ const runStep = async (step, inputAtSubmit) => {
25518
+ const resourceId = step.action?.resourceId;
25519
+ if (!resourceId) return null;
25520
+ try {
25521
+ const result = await executeAsync.mutateAsync({
25522
+ resourceId,
25523
+ resourceType: "workflow",
25524
+ input: inputAtSubmit,
25525
+ listId: list.id
26429
25526
  });
26430
- } else {
26431
- setPendingStepRunInput(null);
25527
+ showSuccessNotification(`Workflow run started: ${result.executionId}`);
25528
+ setOptimisticRuns((current) => [
25529
+ {
25530
+ executionId: result.executionId,
25531
+ resourceId,
25532
+ status: result.status || "running",
25533
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
25534
+ completedAt: null,
25535
+ durationMs: null,
25536
+ input: inputAtSubmit
25537
+ },
25538
+ ...current.filter((run2) => run2.executionId !== result.executionId)
25539
+ ]);
25540
+ queryClient.invalidateQueries({
25541
+ queryKey: acquisitionListKeys.executionsBase(workOSOrganizationId, list.id)
25542
+ });
25543
+ return result;
25544
+ } catch (error2) {
25545
+ showApiErrorNotification(error2);
25546
+ throw error2;
26432
25547
  }
26433
- openRunModal(step.action?.resourceId ?? null);
26434
25548
  };
26435
25549
  const handleCopyListCommand = () => {
26436
25550
  void navigator.clipboard.writeText(`/acquisition --lead-gen list ${list.id}`);
@@ -26455,11 +25569,11 @@ function LeadGenListDetailPage({ listId }) {
26455
25569
  ),
26456
25570
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
26457
25571
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
26458
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor7(list.status), children: list.status }),
25572
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor6(list.status), children: list.status }),
26459
25573
  /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "violet", children: currentBuildTemplateLabel }),
26460
25574
  /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
26461
25575
  "Created ",
26462
- formatDateTime5(list.createdAt)
25576
+ formatDateTime4(list.createdAt)
26463
25577
  ] })
26464
25578
  ] }),
26465
25579
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
@@ -26482,23 +25596,35 @@ function LeadGenListDetailPage({ listId }) {
26482
25596
  backButton
26483
25597
  ] })
26484
25598
  ] }),
26485
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: setActiveTab, children: [
25599
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: setActiveTab, keepMounted: false, children: [
26486
25600
  /* @__PURE__ */ jsxs(Tabs.List, { children: [
26487
25601
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "overview", leftSection: /* @__PURE__ */ jsx(IconBuilding, { size: 14 }), children: "Overview" }),
26488
25602
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "members", leftSection: /* @__PURE__ */ jsx(IconUsers, { size: 14 }), children: "Members" }),
26489
25603
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "build", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 14 }), children: "Build" }),
26490
25604
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), children: "Runs" })
26491
25605
  ] }),
26492
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "overview", pt: "sm", children: /* @__PURE__ */ jsx(OverviewTab, { list, progress: businessProgress }) }),
26493
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "members", pt: "sm", children: /* @__PURE__ */ jsx(MembersTab, { listId, progress: businessProgress, onMemberClick: handleMemberClick }) }),
25606
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "overview", pt: "sm", children: /* @__PURE__ */ jsx(OverviewTab, { list, progress: businessProgress, leadGenConfig }) }),
25607
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "members", pt: "sm", children: /* @__PURE__ */ jsx(
25608
+ MembersTab,
25609
+ {
25610
+ listId,
25611
+ progress: businessProgress,
25612
+ stageCatalog,
25613
+ onMemberClick: handleMemberClick
25614
+ }
25615
+ ) }),
26494
25616
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "build", pt: "sm", children: /* @__PURE__ */ jsx(
26495
25617
  BuildTab,
26496
25618
  {
26497
25619
  list,
26498
25620
  steps: buildSteps,
25621
+ executions,
26499
25622
  recommendedStep: recommendedBuildStep,
26500
- onRunStep: openStepRun,
26501
- onRetryStep: openStepRun,
25623
+ optimisticRuns,
25624
+ exportWorkflowId: leadGenConfig.exportWorkflowId,
25625
+ stageCatalog,
25626
+ onRunStep: runStep,
25627
+ onRetryStep: runStep,
26502
25628
  onViewRunLog: (resourceId, executionId) => {
26503
25629
  void navigate({
26504
25630
  to: "/operations/resources/workflow/$workflowId",
@@ -26545,25 +25671,551 @@ function LeadGenListDetailPage({ listId }) {
26545
25671
  ] })
26546
25672
  ] })
26547
25673
  }
25674
+ )
25675
+ ] });
25676
+ }
25677
+ function ListBuilderIndexPage() {
25678
+ const navigate = useNavigate();
25679
+ const [query, setQuery] = useState("");
25680
+ const listsQuery = useLists();
25681
+ const lists = listsQuery.data ?? [];
25682
+ const filteredLists = useMemo(() => {
25683
+ const normalized = query.trim().toLowerCase();
25684
+ if (!normalized) return lists;
25685
+ return lists.filter((list) => list.name.toLowerCase().includes(normalized));
25686
+ }, [lists, query]);
25687
+ const openList = (listId) => {
25688
+ navigate({ to: "/lead-gen/list-builder/$listId", params: { listId } });
25689
+ };
25690
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
25691
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Choose a list to run lead-gen workflows and monitor progress." }),
25692
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
25693
+ /* @__PURE__ */ jsx(
25694
+ TextInput,
25695
+ {
25696
+ placeholder: "Search lists...",
25697
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
25698
+ value: query,
25699
+ onChange: (event) => setQuery(event.currentTarget.value)
25700
+ }
25701
+ ),
25702
+ listsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !filteredLists.length ? /* @__PURE__ */ jsx(
25703
+ EmptyState,
25704
+ {
25705
+ icon: IconLayoutDashboard,
25706
+ title: query.trim() ? "No lists match your search" : "No lists available",
25707
+ description: query.trim() ? void 0 : "Create a list first, then open it in the builder."
25708
+ }
25709
+ ) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
25710
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
25711
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
25712
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
25713
+ /* @__PURE__ */ jsx(Table.Th, { children: "Batches" }),
25714
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
25715
+ ] }) }),
25716
+ /* @__PURE__ */ jsx(Table.Tbody, { children: filteredLists.map((list) => /* @__PURE__ */ jsxs(
25717
+ Table.Tr,
25718
+ {
25719
+ role: "button",
25720
+ tabIndex: 0,
25721
+ onClick: () => openList(list.id),
25722
+ onKeyDown: (event) => {
25723
+ if (event.key === "Enter" || event.key === " ") {
25724
+ event.preventDefault();
25725
+ openList(list.id);
25726
+ }
25727
+ },
25728
+ style: { cursor: "pointer" },
25729
+ children: [
25730
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
25731
+ /* @__PURE__ */ jsx(Text, { fw: 500, children: list.name }),
25732
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.description ?? list.id })
25733
+ ] }),
25734
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStateKeyColor(list.status), children: list.status }) }),
25735
+ /* @__PURE__ */ jsx(Table.Td, { children: list.batchIds.length }),
25736
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDate3(list.createdAt) })
25737
+ ]
25738
+ },
25739
+ list.id
25740
+ )) })
25741
+ ] })
25742
+ ] }) })
25743
+ ] }) }) });
25744
+ }
25745
+
25746
+ // src/lib/lead-gen/stage-colors.ts
25747
+ var FALLBACK_STAGE_COLORS = {
25748
+ accent: "var(--color-text-dimmed)",
25749
+ background: "color-mix(in srgb, var(--color-text-dimmed) 10%, transparent)",
25750
+ border: "color-mix(in srgb, var(--color-border) 85%, var(--color-text-dimmed))",
25751
+ text: "var(--color-text)"
25752
+ };
25753
+ var STAGE_COLOR_SETS = {
25754
+ scraped: {
25755
+ accent: "var(--color-text-subtle)",
25756
+ background: "color-mix(in srgb, var(--color-text-subtle) 12%, transparent)",
25757
+ border: "color-mix(in srgb, var(--color-border) 80%, var(--color-text-subtle))",
25758
+ text: "var(--color-text)"
25759
+ },
25760
+ populated: {
25761
+ accent: "var(--color-primary)",
25762
+ background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
25763
+ border: "color-mix(in srgb, var(--color-border) 70%, var(--color-primary))",
25764
+ text: "var(--color-text)"
25765
+ },
25766
+ crawled: {
25767
+ accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-text-subtle))",
25768
+ background: "color-mix(in srgb, var(--color-primary) 9%, transparent)",
25769
+ border: "color-mix(in srgb, var(--color-border) 72%, var(--color-primary))",
25770
+ text: "var(--color-text)"
25771
+ },
25772
+ extracted: {
25773
+ accent: "color-mix(in srgb, var(--color-primary) 78%, var(--color-success))",
25774
+ background: "color-mix(in srgb, var(--color-primary) 10%, transparent)",
25775
+ border: "color-mix(in srgb, var(--color-border) 65%, var(--color-primary))",
25776
+ text: "var(--color-text)"
25777
+ },
25778
+ enriched: {
25779
+ accent: "color-mix(in srgb, var(--color-success) 70%, var(--color-primary))",
25780
+ background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
25781
+ border: "color-mix(in srgb, var(--color-border) 68%, var(--color-success))",
25782
+ text: "var(--color-text)"
25783
+ },
25784
+ "decision-makers-enriched": {
25785
+ accent: "color-mix(in srgb, var(--color-primary) 66%, var(--color-warning))",
25786
+ background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
25787
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
25788
+ text: "var(--color-text)"
25789
+ },
25790
+ discovered: {
25791
+ accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-warning))",
25792
+ background: "color-mix(in srgb, var(--color-warning) 10%, transparent)",
25793
+ border: "color-mix(in srgb, var(--color-border) 68%, var(--color-warning))",
25794
+ text: "var(--color-text)"
25795
+ },
25796
+ verified: {
25797
+ accent: "var(--color-success)",
25798
+ background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
25799
+ border: "color-mix(in srgb, var(--color-border) 64%, var(--color-success))",
25800
+ text: "var(--color-text)"
25801
+ },
25802
+ qualified: {
25803
+ accent: "color-mix(in srgb, var(--color-success) 82%, var(--color-text))",
25804
+ background: "color-mix(in srgb, var(--color-success) 14%, transparent)",
25805
+ border: "color-mix(in srgb, var(--color-border) 62%, var(--color-success))",
25806
+ text: "var(--color-text)"
25807
+ },
25808
+ personalized: {
25809
+ accent: "color-mix(in srgb, var(--color-primary) 58%, var(--color-success))",
25810
+ background: "color-mix(in srgb, var(--color-primary) 11%, transparent)",
25811
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
25812
+ text: "var(--color-text)"
25813
+ },
25814
+ uploaded: {
25815
+ accent: "color-mix(in srgb, var(--color-warning) 72%, var(--color-primary))",
25816
+ background: "color-mix(in srgb, var(--color-warning) 12%, transparent)",
25817
+ border: "color-mix(in srgb, var(--color-border) 64%, var(--color-warning))",
25818
+ text: "var(--color-text)"
25819
+ },
25820
+ interested: {
25821
+ accent: "color-mix(in srgb, var(--color-success) 72%, var(--color-warning))",
25822
+ background: "color-mix(in srgb, var(--color-success) 15%, transparent)",
25823
+ border: "color-mix(in srgb, var(--color-border) 58%, var(--color-success))",
25824
+ text: "var(--color-text)"
25825
+ },
25826
+ disqualified: {
25827
+ accent: "var(--color-error)",
25828
+ background: "color-mix(in srgb, var(--color-error) 10%, transparent)",
25829
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-error))",
25830
+ text: "var(--color-text)"
25831
+ },
25832
+ invalid: {
25833
+ accent: "color-mix(in srgb, var(--color-error) 72%, var(--color-text-dimmed))",
25834
+ background: "color-mix(in srgb, var(--color-error) 8%, transparent)",
25835
+ border: "color-mix(in srgb, var(--color-border) 78%, var(--color-error))",
25836
+ text: "var(--color-text)"
25837
+ }
25838
+ };
25839
+ function getLeadGenStageColor(stageKey) {
25840
+ return STAGE_COLOR_SETS[stageKey] ?? FALLBACK_STAGE_COLORS;
25841
+ }
25842
+ function getLeadGenStageColorVar(stageKey, tone = "accent") {
25843
+ return getLeadGenStageColor(stageKey)[tone];
25844
+ }
25845
+ var FALLBACK_STAGE_ORDER = 1e4;
25846
+ function unique(values) {
25847
+ return Array.from(new Set(values.filter(Boolean)));
25848
+ }
25849
+ function configuredStages(pipelineConfig, stageCatalog) {
25850
+ return (pipelineConfig?.stages ?? []).filter((stage) => stage.enabled !== false).slice().sort((a, b) => {
25851
+ const aOrder = a.order ?? stageCatalog[a.key]?.order ?? FALLBACK_STAGE_ORDER;
25852
+ const bOrder = b.order ?? stageCatalog[b.key]?.order ?? FALLBACK_STAGE_ORDER;
25853
+ return aOrder - bOrder || a.key.localeCompare(b.key);
25854
+ });
25855
+ }
25856
+ function workflowStageKeys(actions) {
25857
+ return unique((actions ?? []).flatMap((action) => [...action.stagesAffected]));
25858
+ }
25859
+ function sourceFor(stageKey, observedKeys, configKeys, workflowKeys) {
25860
+ if (observedKeys.has(stageKey)) return "activity";
25861
+ if (configKeys.has(stageKey)) return "config";
25862
+ if (workflowKeys.has(stageKey)) return "workflow";
25863
+ return "activity";
25864
+ }
25865
+ function sortStageNodes(nodes) {
25866
+ return nodes.slice().sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));
25867
+ }
25868
+ function buildLaneStages({
25869
+ entity,
25870
+ progress,
25871
+ pipelineConfig,
25872
+ actions,
25873
+ stageCatalog
25874
+ }) {
25875
+ const stageMap = entity === "company" ? progress.byCompanyStage : progress.byContactStage;
25876
+ const observedKeys = new Set(Object.keys(stageMap));
25877
+ const configStages = configuredStages(pipelineConfig, stageCatalog);
25878
+ const configStageByKey = new Map(configStages.map((stage) => [stage.key, stage]));
25879
+ const configKeys = new Set(configStages.map((stage) => stage.key));
25880
+ const workflowKeys = new Set(workflowStageKeys(actions));
25881
+ const stageKeys = unique([...observedKeys, ...configKeys, ...workflowKeys]);
25882
+ return sortStageNodes(
25883
+ stageKeys.flatMap((key) => {
25884
+ const catalogEntry = stageCatalog[key];
25885
+ const inferredEntity = catalogEntry?.entity ?? (observedKeys.has(key) ? entity : null);
25886
+ if (inferredEntity !== entity) return [];
25887
+ return [
25888
+ {
25889
+ key,
25890
+ label: configStageByKey.get(key)?.label ?? catalogEntry?.label ?? key,
25891
+ order: configStageByKey.get(key)?.order ?? catalogEntry?.order ?? FALLBACK_STAGE_ORDER,
25892
+ entity,
25893
+ source: sourceFor(key, observedKeys, configKeys, workflowKeys)
25894
+ }
25895
+ ];
25896
+ })
25897
+ );
25898
+ }
25899
+ function getStageProgress2(progress, stageKey, entity) {
25900
+ const stageProgress = entity === "company" ? progress.byCompanyStage[stageKey] : progress.byContactStage[stageKey];
25901
+ const total = stageProgress?.total ?? (entity === "company" ? progress.totalCompanies : progress.totalMembers);
25902
+ return {
25903
+ total,
25904
+ attempted: stageProgress?.attempted ?? 0,
25905
+ success: stageProgress?.success ?? 0,
25906
+ noResult: stageProgress?.noResult ?? 0,
25907
+ skipped: stageProgress?.skipped ?? 0,
25908
+ error: stageProgress?.error ?? 0,
25909
+ other: stageProgress?.other ?? 0,
25910
+ notAttempted: stageProgress?.notAttempted ?? total
25911
+ };
25912
+ }
25913
+ function percent(value, total) {
25914
+ if (total <= 0) return 0;
25915
+ return Math.max(0, Math.min(100, value / total * 100));
25916
+ }
25917
+ function SourceBadge({ source }) {
25918
+ const label = source === "activity" ? "recorded" : source;
25919
+ const color = source === "activity" ? "green" : source === "config" ? "blue" : "gray";
25920
+ return /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color, children: label });
25921
+ }
25922
+ function ProgressRail({ counts, stageKey }) {
25923
+ const accent = getLeadGenStageColorVar(stageKey);
25924
+ const segments = [
25925
+ { key: "success", value: counts.success, color: accent },
25926
+ { key: "noResult", value: counts.noResult, color: "var(--color-text-subtle)" },
25927
+ { key: "skipped", value: counts.skipped, color: "var(--color-warning)" },
25928
+ { key: "error", value: counts.error, color: "var(--color-error)" },
25929
+ { key: "other", value: counts.other, color: "var(--color-primary)" }
25930
+ ].filter((segment) => segment.value > 0);
25931
+ return /* @__PURE__ */ jsx(
25932
+ Box,
25933
+ {
25934
+ h: 6,
25935
+ bg: "var(--color-surface-hover)",
25936
+ style: {
25937
+ borderRadius: 999,
25938
+ display: "flex",
25939
+ overflow: "hidden",
25940
+ opacity: counts.total > 0 ? 1 : 0.55
25941
+ },
25942
+ children: segments.length > 0 ? segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${percent(segment.value, counts.total)}%`, bg: segment.color }, segment.key)) : /* @__PURE__ */ jsx(Box, { h: "100%", w: counts.total > 0 ? `${percent(counts.attempted, counts.total)}%` : "0%", bg: accent })
25943
+ }
25944
+ );
25945
+ }
25946
+ function TimelineLane({
25947
+ title,
25948
+ entity,
25949
+ total,
25950
+ stages,
25951
+ progress,
25952
+ emptyText
25953
+ }) {
25954
+ const Icon = entity === "company" ? IconBuilding : IconUsers;
25955
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
25956
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", gap: "xs", children: [
25957
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
25958
+ /* @__PURE__ */ jsx(
25959
+ Box,
25960
+ {
25961
+ w: 28,
25962
+ h: 28,
25963
+ style: {
25964
+ alignItems: "center",
25965
+ background: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
25966
+ border: "1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-primary))",
25967
+ borderRadius: 999,
25968
+ color: "var(--color-primary)",
25969
+ display: "flex",
25970
+ justifyContent: "center"
25971
+ },
25972
+ children: /* @__PURE__ */ jsx(Icon, { size: 15 })
25973
+ }
25974
+ ),
25975
+ /* @__PURE__ */ jsxs(Stack, { gap: 0, children: [
25976
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: title }),
25977
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
25978
+ total,
25979
+ " ",
25980
+ entity === "company" ? "companies" : "contacts"
25981
+ ] })
25982
+ ] })
25983
+ ] }),
25984
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "outline", color: entity === "company" ? "blue" : "teal", children: [
25985
+ stages.length,
25986
+ " stages"
25987
+ ] })
25988
+ ] }),
25989
+ stages.length === 0 ? /* @__PURE__ */ jsx(
25990
+ Box,
25991
+ {
25992
+ p: "sm",
25993
+ style: {
25994
+ border: "1px dashed var(--color-border)",
25995
+ borderRadius: "var(--mantine-radius-md)"
25996
+ },
25997
+ children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: emptyText })
25998
+ }
25999
+ ) : /* @__PURE__ */ jsx(ScrollArea, { type: "hover", offsetScrollbars: true, children: /* @__PURE__ */ jsx(
26000
+ Box,
26001
+ {
26002
+ style: {
26003
+ minWidth: Math.max(560, stages.length * 178),
26004
+ padding: "8px 2px 2px"
26005
+ },
26006
+ children: /* @__PURE__ */ jsx(
26007
+ Box,
26008
+ {
26009
+ style: {
26010
+ alignItems: "start",
26011
+ display: "grid",
26012
+ gap: 0,
26013
+ gridTemplateColumns: `repeat(${stages.length}, minmax(150px, 1fr))`,
26014
+ position: "relative"
26015
+ },
26016
+ children: stages.map((stage, index) => {
26017
+ const counts = getStageProgress2(progress, stage.key, entity);
26018
+ const accent = getLeadGenStageColorVar(stage.key);
26019
+ const completion = Math.round(percent(counts.success, counts.total));
26020
+ const attempted = Math.round(percent(counts.attempted, counts.total));
26021
+ return /* @__PURE__ */ jsxs(Stack, { gap: 8, px: "xs", style: { position: "relative", zIndex: 1 }, children: [
26022
+ /* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", children: [
26023
+ /* @__PURE__ */ jsx(
26024
+ Box,
26025
+ {
26026
+ w: 44,
26027
+ h: 44,
26028
+ style: {
26029
+ alignItems: "center",
26030
+ background: getLeadGenStageColorVar(stage.key, "background"),
26031
+ border: `1px solid ${getLeadGenStageColorVar(stage.key, "border")}`,
26032
+ borderRadius: 999,
26033
+ boxShadow: `0 0 0 4px ${getLeadGenStageColorVar(stage.key, "background")}`,
26034
+ color: accent,
26035
+ display: "flex",
26036
+ flexShrink: 0,
26037
+ fontSize: 13,
26038
+ fontWeight: 800,
26039
+ justifyContent: "center"
26040
+ },
26041
+ children: index + 1
26042
+ }
26043
+ ),
26044
+ /* @__PURE__ */ jsxs(Stack, { gap: 1, style: { minWidth: 0 }, children: [
26045
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
26046
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, lineClamp: 1, children: stage.label }),
26047
+ /* @__PURE__ */ jsx(SourceBadge, { source: stage.source })
26048
+ ] }),
26049
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: counts.total > 0 ? `${counts.attempted} / ${counts.total} attempted` : `Ready for ${entity === "company" ? "company" : "contact"} rows` })
26050
+ ] })
26051
+ ] }),
26052
+ /* @__PURE__ */ jsx(ProgressRail, { counts, stageKey: stage.key }),
26053
+ /* @__PURE__ */ jsxs(Group, { gap: 10, wrap: "nowrap", children: [
26054
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
26055
+ counts.success,
26056
+ " success"
26057
+ ] }),
26058
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
26059
+ attempted,
26060
+ "% attempted"
26061
+ ] }),
26062
+ counts.error > 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "red", children: [
26063
+ counts.error,
26064
+ " errors"
26065
+ ] }) : null
26066
+ ] }),
26067
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
26068
+ completion,
26069
+ "% complete"
26070
+ ] })
26071
+ ] }, stage.key);
26072
+ })
26073
+ }
26074
+ )
26075
+ }
26076
+ ) })
26077
+ ] });
26078
+ }
26079
+ function PipelineFunnel({ progress, pipelineConfig, actions }) {
26080
+ const { stageCatalog = {} } = useLeadGenConfig();
26081
+ const companyStages = buildLaneStages({ entity: "company", progress, pipelineConfig, actions, stageCatalog });
26082
+ const contactStages = buildLaneStages({ entity: "contact", progress, pipelineConfig, actions, stageCatalog });
26083
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
26084
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconListDetails, { size: 16 }), title: "Pipeline" }),
26085
+ /* @__PURE__ */ jsx(
26086
+ TimelineLane,
26087
+ {
26088
+ title: "Company pipeline",
26089
+ entity: "company",
26090
+ total: progress.totalCompanies,
26091
+ stages: companyStages,
26092
+ progress,
26093
+ emptyText: "No company stages are configured, runnable, or recorded yet."
26094
+ }
26548
26095
  ),
26549
26096
  /* @__PURE__ */ jsx(
26550
- RunWorkflowModal,
26097
+ TimelineLane,
26551
26098
  {
26552
- opened: runModalOpen,
26553
- onClose: closeRunModal,
26554
- list,
26555
- actions: runModalActions,
26556
- selection: EMPTY_SELECTION2,
26557
- initialResourceId: initialRunResourceId,
26558
- title: "Run build step",
26559
- description: "Run the selected bounded build workflow for this list.",
26560
- lockResourceSelection: true,
26561
- onSubmitted: (_, executionId) => {
26562
- showSuccessNotification(`Workflow run started: ${executionId}`);
26563
- }
26099
+ title: "Contact pipeline",
26100
+ entity: "contact",
26101
+ total: progress.totalMembers,
26102
+ stages: contactStages,
26103
+ progress,
26104
+ emptyText: "No contact stages are configured, runnable, or recorded yet."
26564
26105
  }
26565
26106
  )
26566
- ] });
26107
+ ] }) });
26108
+ }
26109
+ function formatDateTime5(value) {
26110
+ if (!value) return "Not yet";
26111
+ return new Date(value).toLocaleString("en-US", {
26112
+ month: "short",
26113
+ day: "numeric",
26114
+ year: "numeric",
26115
+ hour: "numeric",
26116
+ minute: "2-digit"
26117
+ });
26118
+ }
26119
+ function getStatusColor7(status) {
26120
+ switch (status) {
26121
+ case "completed":
26122
+ case "success":
26123
+ case "launched":
26124
+ return "green";
26125
+ case "running":
26126
+ case "pending":
26127
+ case "enriching":
26128
+ return "blue";
26129
+ case "failed":
26130
+ case "error":
26131
+ case "archived":
26132
+ return "red";
26133
+ case "closing":
26134
+ return "yellow";
26135
+ default:
26136
+ return "gray";
26137
+ }
26138
+ }
26139
+ function ListBuilderPage({ listId }) {
26140
+ const navigate = useNavigate();
26141
+ const actions = useListActions();
26142
+ const listQuery = useList(listId);
26143
+ const progressQuery = useListProgress(listId);
26144
+ const executionsQuery = useListExecutions(listId);
26145
+ const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
26146
+ const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
26147
+ const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
26148
+ const openListDetail = () => {
26149
+ void navigate({ to: "/lead-gen/lists/$listId", params: { listId } });
26150
+ };
26151
+ const copyListCommand = (id) => {
26152
+ void navigator.clipboard.writeText(`/acquisition --lead-gen list ${id}`);
26153
+ showSuccessNotification("Copied list command to clipboard");
26154
+ };
26155
+ const backButton = /* @__PURE__ */ jsx(
26156
+ Button,
26157
+ {
26158
+ variant: "light",
26159
+ size: "xs",
26160
+ leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
26161
+ onClick: () => navigate({ to: "/lead-gen/lists" }),
26162
+ children: "Lists"
26163
+ }
26164
+ );
26165
+ if (isLoading) {
26166
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
26167
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
26168
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
26169
+ ] }) }) });
26170
+ }
26171
+ if (error) {
26172
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
26173
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
26174
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load list builder" }) })
26175
+ ] }) }) });
26176
+ }
26177
+ if (!listQuery.data || !progressQuery.data) {
26178
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
26179
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Not Found", caption: "The requested lead-gen list is unavailable." }),
26180
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 240, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "The requested list could not be found." }) }) })
26181
+ ] }) }) });
26182
+ }
26183
+ const list = listQuery.data;
26184
+ const progress = progressQuery.data;
26185
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
26186
+ /* @__PURE__ */ jsx(
26187
+ PageTitleCaption,
26188
+ {
26189
+ title: list.name,
26190
+ caption: list.description ?? "Lead-gen list builder",
26191
+ rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
26192
+ /* @__PURE__ */ jsx(Button, { size: "xs", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), onClick: openListDetail, children: "Open Build" }),
26193
+ backButton
26194
+ ] })
26195
+ }
26196
+ ),
26197
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
26198
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
26199
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor7(list.status), children: list.status }),
26200
+ /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
26201
+ "Created ",
26202
+ formatDateTime5(list.createdAt)
26203
+ ] })
26204
+ ] }),
26205
+ /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: () => copyListCommand(list.id), style: { cursor: "pointer" }, children: [
26206
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
26207
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
26208
+ ] }) })
26209
+ ] }),
26210
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2, lg: 4 }, children: [
26211
+ /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
26212
+ /* @__PURE__ */ jsx(StatCard, { label: "Contacts", value: progress.totalMembers, icon: IconUsers }),
26213
+ /* @__PURE__ */ jsx(StatCard, { label: "Workflows", value: actions.length, icon: IconBolt }),
26214
+ /* @__PURE__ */ jsx(StatCard, { label: "Runs", value: executions.length, icon: IconPlayerPlay })
26215
+ ] }),
26216
+ /* @__PURE__ */ jsx(PipelineFunnel, { progress, pipelineConfig: list.pipelineConfig, actions }),
26217
+ /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId })
26218
+ ] }) }) });
26567
26219
  }
26568
26220
  var PAGE_SIZE_DEFAULT3 = 20;
26569
26221
  function LeadGenCompaniesPage() {
@@ -41446,4 +41098,4 @@ function createUseOrgInitialization(useOrganizations, useApiClient2) {
41446
41098
  };
41447
41099
  }
41448
41100
 
41449
- export { AbsoluteScheduleForm, AccountSettings, ActionModal, ActivityCard, ActivityFeed, ActivityFeedWidget, ActivityFilters, ActivityLog, ActivityTable, AgentDefinitionDisplay, AgentExecutionLogs, AgentExecutionPanel, AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, AgentSessionGroup, AllTasksPage, ApiKeyDisplayModal, ApiKeyList, ApiKeyService, ApiKeySettings, AppErrorBoundary, AppearanceSettings, BaseEdge, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, BaseNode, Breadcrumbs, BusinessImpactCard, CRM_ITEMS, Can, Checklist, CheckpointGroup, CollapsibleJsonSection, CommandQueueDetailPage, CommandQueuePage, CommandQueueShell, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewPage, CompanyDetailPage, ConfigCard, ConfirmationInputModal, ConfirmationModal, ContactDetailPage, ContentSections, ContextUsageBadge, ContractDisplay, ConversationThread, CostAnalytics, CostBreakdownCard, CostByModelTable, CostMetricsCard, CrashErrorFallback, CreateApiKeyModal, CreateCredentialModal, CreateDeliveryEntityModal, CreateRoleModal, CreateScheduleModal, CreateWebhookEndpointModal, CredentialList, CredentialService, CredentialSettings, CrmActionsProvider, CrmOverview, CrmSettingsPage, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, CustomModal, DEAL_STAGES, DEAL_STAGE_COLORS, DEAL_STAGE_OPTIONS, DEFAULT_KANBAN_CONFIG, DELIVERY_COMMUNICATION_ITEMS, DELIVERY_PROJECT_ITEMS, DELIVERY_WORK_ITEMS, Dashboard, DashboardOperationsOverview, DealDetailPage, DealKanbanCard, DealsListPage, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentService, DeploymentSettings, DeploymentStatusBadge, EMPTY_LIST_ACTIONS, EditApiKeyModal, EditCredentialModal, EditWebhookEndpointModal, ElevasisCoreProvider, ElevasisSystemsProvider, ElevasisUIProvider, EmptyVisualizer, ErrorAnalysisCard, ErrorBreakdownTable, ErrorDetailsModal, ErrorReportCard, ExecuteWorkflowModal, ExecutionBreakdownTable, ExecutionErrorSection, ExecutionHealth, ExecutionHealthCard, ExecutionLogsFilters, ExecutionLogsPage, ExecutionLogsTable, ExecutionPanel, ExecutionStats, ExecutionStatusBadge, FilterBar, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, HealthStatusCard, KanbanBoard, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, ListActionsProvider, ListBuilderIndexPage, ListBuilderPage, LogEntry, LogGroup, MdxRenderer, MemberAccessModal, MemberConfigModal, MembershipFeaturePanel, MembershipStatusBadge, MetricsStrip, MilestoneTimeline, MyTasksPanel, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, NoAccessState, NotificationBell, NotificationCenter, NotificationItem, NotificationList, NotificationPanel, NotificationProvider, OAuthConnectModal, OAuthIntegrationsCard, ORGANIZATION_GRAPH_NODE_KIND_LABELS, ORPHAN_STAGE_ORDER, OperationsOverview, OperationsService, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, OrgMembersList, OrganizationGraphPage, OrganizationMembershipService, OrganizationMembershipsList, OrganizationProvider, OrganizationSettings, OrganizationSwitcher, OrganizationSwitcherConnected, PIPELINE_FUNNEL_ORDER, PermissionMatrix, PipelineFunnelWidget, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecentExecutionsByResource, RecurringScheduleForm, RelativeScheduleForm, ResourceDefinitionSection, ResourceDetailPage, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceHealthChart, ResourceHealthPanel, ResourceNotFoundState, ResourceOverview, ResourcesPage, ResourcesSidebar, RichTextEditor, RoleBadge, RunResourceButton, RunWorkflowModal, SAVED_VIEW_PRESETS, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SessionChatArea, SessionChatInterface, SessionChatPage, SessionDetailsSidebar, SessionExecutionLogs, SessionHeader, SessionListItem, SessionMemory, SessionsPage, SessionsSidebar, SortableHeader, StepConfigForm, SystemOpsView, SystemShell, TabSection, TableSelectionToolbar, TaskCard, TaskScheduler, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, ToolsListDisplay, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, UnresolvedErrorsTeaser, UpcomingMilestonesPage, VisualizerContainer, WebhookEndpointList, WebhookEndpointService, WebhookEndpointSettings, WebhookUrlDisplayModal, WorkflowDefinitionDisplay, WorkflowExecutionLogs, WorkflowExecutionPanel, WorkflowExecutionTimeline, ZodFormRenderer, acquisitionListKeys, aggregateSystemMetrics, buildErrorReport, calculateProgress, clientsKeys, collectResourceFilterFacets, companyKeys, contactKeys, createFeatureAccessHook, createOrganizationsSlice, createTestSystemsProvider, createUseOrgInitialization, createUseOrganizations, crmManifest, crmPrioritySettingsKeys, dealKeys, dealNoteKeys, dealTaskKeys, deliveryManifest, deriveBusinessProgress, executionsKeys, filterByDomainFilters, findListActionByAction, formatDate3 as formatDate, formatDealStageLabel, formatResourceAttribution, formatStatusLabel, getEnrichmentColor, getEnrichmentStatus, getExecutionStatusConfig, getGraphBackgroundStyles, getHealthColor, getIcon, getListActionWorkflowId, getLogLevelConfig, getResourceFilterFacetIds, getStateKeyColor, getStatusColor4 as getStatusColor, getStepActionLabel, iconMap, isSessionCapable, labelResourceFilterFacet, leadGenArtifactKeys, leadGenListCompanyKeys, leadGenListMemberKeys, leadGenManifest, mdxComponents, milestoneKeys, milestoneStatusColors, monitoringManifest, noteKeys, noteTypeColors, operationsKeys, operationsManifest, projectActivityKeys, projectKeys, projectStatusColors, requestsKeys, resolveBuildPlanSteps, resolveBuildState, scheduleKeys, sessionsKeys, settingsManifest, showApiErrorNotification, showAuthError, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification, sortData, sortStageKeys, taskKeys, taskStatusColors, taskTypeColors, useActivateDeployment, useActivities, useActivitiesRealtime, useActivityFilters, useActivityTrend, useAddCompaniesToList, useAddContactsToList, useArchiveSession, useArchivedLogs, useArtifacts, useAssignRole, useBatchDelete, useBatchTelemetry, useBatchedResourcesHealth, useBreadcrumbs, useBulkDeleteExecutions, useBusinessImpact, useCancelExecution, useCancelSchedule, useCheckpointTasks, useClient, useClientStatus, useClients, useCommandQueue, useCommandQueueTotals, useCommandViewData, useCommandViewDomainFilters, useCommandViewStats, useCommandViewStore, useCompanies, useCompany, useCompanyFacets, useCompleteDealTask, useContact, useContacts, useCostBreakdown, useCostByModel, useCostSummary, useCostTrends, useCreateApiKey, useCreateArtifact, useCreateClient, useCreateCompany, useCreateContact, useCreateCredential, useCreateDealNote, useCreateDealTask, useCreateList, useCreateMilestone, useCreateNote, useCreateOrgRole, useCreateProject, useCreateSchedule, useCreateSession, useCreateTask, useCreateWebhookEndpoint, useCredentials, useCrmActions, useCrmPipelineSummary, useCrmPrioritySettings, useCrmQuickMetrics, useDashboardMetrics, useDeactivateDeployment, useDeactivateMembership, useDealDetail, useDealNotes, useDealTasks, useDealTasksDue, useDeals, useDealsLookup, useDealsSummary, useDeleteApiKey, useDeleteClient, useDeleteCompanies, useDeleteContacts, useDeleteCredential, useDeleteDeal, useDeleteDeployment, useDeleteExecution, useDeleteList, useDeleteLists, useDeleteMilestone, useDeleteOrgRole, useDeleteProject, useDeleteRequest, useDeleteSchedule, useDeleteSession, useDeleteTask, useDeleteTask2, useDeleteWebhookEndpoint, useDeriveActions, useEffectivePermissions, useElevasisSystems, useErrorAnalysis, useErrorDetail, useErrorDetails, useErrorDistribution, useErrorNotification, useExecuteAction, useExecuteAsync, useExecuteResource, useExecution, useExecutionHealth, useExecutionLogSSE, useExecutionLogs, useExecutionLogsFilters, useExecutionPanelState, useExecutionSSE, useExecutions, useGetExecutionHistory, useGetSchedule, useGraphBackgroundStyles, useGraphTheme, useHasPermission, useInFlightExecutions, useList, useListActions, useListApiKeys, useListDeployments, useListExecutions, useListMember, useListMembers, useListProgress, useListRecords, useListSchedules, useListWebhookEndpoints, useLists, useListsTelemetry, useMarkAllAsRead, useMarkAsRead, useMilestones, useNewKnowledgeMapLayout, useNotificationAdapter, useNotificationCount, useNotifications, useOptionalElevasisSystems, useOrgRoles, useOrganizationMembers, useOrganizationPermissions, usePaginationState, usePatchTask, usePauseSchedule, usePermissionCatalog, useProject, useProjectActivities, useProjectMilestones, useProjectNotes, useProjectRealtime, useProjectTasks, useProjects, useReactivateMembership, useRecentCrmActivity, useRecentExecutionsByResource, useRemoveCompaniesFromList, useRequest, useRequestsList, useResetCrmPrioritySettings, useResolveAllErrors, useResolveError, useResolveErrorsByExecution, useResolvedOrganizationModel, useResourceDefinition, useResourceErrors, useResourceExecutions, useResourceSearch, useResources, useResourcesDomainFilters, useResourcesHealth, useResumeSchedule, useRetryExecution, useRevokeRole, useSSEConnection, useScheduledTasks, useSession, useSessionExecution, useSessionExecutions, useSessionMessages, useSessionWebSocket, useSessions, useSortedData, useStatusFilter, useSubmitAction, useSuccessNotification, useSystemHealth, useTableSelection, useTableSort, useTasks, useTestNotification, useTimeRangeDates, useTopFailingResources, useTransitionItem, useTransitionListCompany, useTransitionListMember, useTransitionState, useUnresolveError, useUnresolvedErrors, useUpdateAnchor, useUpdateApiKey, useUpdateClient, useUpdateCompany, useUpdateContact, useUpdateCredential, useUpdateCrmPrioritySettings, useUpdateList, useUpdateListConfig, useUpdateListStatus, useUpdateMemberConfig, useUpdateMilestone, useUpdateOrgRole, useUpdateProject, useUpdateRequestStatus, useUpdateSchedule, useUpdateTask, useUpdateWebhookEndpoint, useUserMemberships, useVerifyCredential, useVisibleResources, useWarningNotification, useWorkflowExecution };
41101
+ export { AbsoluteScheduleForm, AccountSettings, ActionModal, ActivityCard, ActivityFeed, ActivityFeedWidget, ActivityFilters, ActivityLog, ActivityTable, AgentDefinitionDisplay, AgentExecutionLogs, AgentExecutionPanel, AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, AgentSessionGroup, AllTasksPage, ApiKeyDisplayModal, ApiKeyList, ApiKeyService, ApiKeySettings, AppErrorBoundary, AppearanceSettings, BaseEdge, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, BaseNode, Breadcrumbs, BusinessImpactCard, CRM_ITEMS, Can, Checklist, CheckpointGroup, CollapsibleJsonSection, CommandQueueDetailPage, CommandQueuePage, CommandQueueShell, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewPage, CompanyDetailPage, ConfigCard, ConfirmationInputModal, ConfirmationModal, ContactDetailPage, ContentSections, ContextUsageBadge, ContractDisplay, ConversationThread, CostAnalytics, CostBreakdownCard, CostByModelTable, CostMetricsCard, CrashErrorFallback, CreateApiKeyModal, CreateCredentialModal, CreateDeliveryEntityModal, CreateRoleModal, CreateScheduleModal, CreateWebhookEndpointModal, CredentialList, CredentialService, CredentialSettings, CrmActionsProvider, CrmOverview, CrmSettingsPage, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, CustomModal, DEAL_STAGES, DEAL_STAGE_COLORS, DEAL_STAGE_OPTIONS, DEFAULT_KANBAN_CONFIG, DELIVERY_COMMUNICATION_ITEMS, DELIVERY_PROJECT_ITEMS, DELIVERY_WORK_ITEMS, Dashboard, DashboardOperationsOverview, DealDetailPage, DealKanbanCard, DealsListPage, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentService, DeploymentSettings, DeploymentStatusBadge, EMPTY_LIST_ACTIONS, EditApiKeyModal, EditCredentialModal, EditWebhookEndpointModal, ElevasisCoreProvider, ElevasisSystemsProvider, ElevasisUIProvider, EmptyVisualizer, ErrorAnalysisCard, ErrorBreakdownTable, ErrorDetailsModal, ErrorReportCard, ExecuteWorkflowModal, ExecutionBreakdownTable, ExecutionErrorSection, ExecutionHealth, ExecutionHealthCard, ExecutionLogsFilters, ExecutionLogsPage, ExecutionLogsTable, ExecutionPanel, ExecutionStats, ExecutionStatusBadge, FilterBar, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, HealthStatusCard, KanbanBoard, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, ListActionsProvider, ListBuilderIndexPage, ListBuilderPage, LogEntry, LogGroup, MdxRenderer, MemberAccessModal, MemberConfigModal, MembershipFeaturePanel, MembershipStatusBadge, MetricsStrip, MilestoneTimeline, MyTasksPanel, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, NoAccessState, NotificationBell, NotificationCenter, NotificationItem, NotificationList, NotificationPanel, NotificationProvider, OAuthConnectModal, OAuthIntegrationsCard, ORGANIZATION_GRAPH_NODE_KIND_LABELS, ORPHAN_STAGE_ORDER, OperationsOverview, OperationsService, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, OrgMembersList, OrganizationGraphPage, OrganizationMembershipService, OrganizationMembershipsList, OrganizationProvider, OrganizationSettings, OrganizationSwitcher, OrganizationSwitcherConnected, PIPELINE_FUNNEL_ORDER, PermissionMatrix, PipelineFunnelWidget, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecentExecutionsByResource, RecurringScheduleForm, RelativeScheduleForm, ResourceDefinitionSection, ResourceDetailPage, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceHealthChart, ResourceHealthPanel, ResourceNotFoundState, ResourceOverview, ResourcesPage, ResourcesSidebar, RichTextEditor, RoleBadge, RunResourceButton, SAVED_VIEW_PRESETS, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SessionChatArea, SessionChatInterface, SessionChatPage, SessionDetailsSidebar, SessionExecutionLogs, SessionHeader, SessionListItem, SessionMemory, SessionsPage, SessionsSidebar, SortableHeader, StepConfigForm, SystemOpsView, SystemShell, TabSection, TableSelectionToolbar, TaskCard, TaskScheduler, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, ToolsListDisplay, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, UnresolvedErrorsTeaser, UpcomingMilestonesPage, VisualizerContainer, WebhookEndpointList, WebhookEndpointService, WebhookEndpointSettings, WebhookUrlDisplayModal, WorkflowDefinitionDisplay, WorkflowExecutionLogs, WorkflowExecutionPanel, WorkflowExecutionTimeline, ZodFormRenderer, acquisitionListKeys, aggregateSystemMetrics, buildErrorReport, calculateProgress, clientsKeys, collectResourceFilterFacets, companyKeys, contactKeys, createFeatureAccessHook, createOrganizationsSlice, createTestSystemsProvider, createUseOrgInitialization, createUseOrganizations, crmManifest, crmPrioritySettingsKeys, dealKeys, dealNoteKeys, dealTaskKeys, deliveryManifest, deriveBusinessProgress, executionsKeys, filterByDomainFilters, findListActionByAction, formatDate3 as formatDate, formatDealStageLabel, formatResourceAttribution, formatStatusLabel, getEnrichmentColor, getEnrichmentStatus, getExecutionStatusConfig, getGraphBackgroundStyles, getHealthColor, getIcon, getListActionWorkflowId, getLogLevelConfig, getResourceFilterFacetIds, getStateKeyColor, getStatusColor4 as getStatusColor, getStepActionLabel, iconMap, isSessionCapable, labelResourceFilterFacet, leadGenArtifactKeys, leadGenListCompanyKeys, leadGenListMemberKeys, leadGenManifest, mdxComponents, milestoneKeys, milestoneStatusColors, monitoringManifest, noteKeys, noteTypeColors, operationsKeys, operationsManifest, projectActivityKeys, projectKeys, projectStatusColors, requestsKeys, resolveBuildPlanSteps, resolveBuildState, scheduleKeys, sessionsKeys, settingsManifest, showApiErrorNotification, showAuthError, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification, sortData, sortStageKeys, taskKeys, taskStatusColors, taskTypeColors, useActivateDeployment, useActivities, useActivitiesRealtime, useActivityFilters, useActivityTrend, useAddCompaniesToList, useAddContactsToList, useArchiveSession, useArchivedLogs, useArtifacts, useAssignRole, useBatchDelete, useBatchTelemetry, useBatchedResourcesHealth, useBreadcrumbs, useBulkDeleteExecutions, useBusinessImpact, useCancelExecution, useCancelSchedule, useCheckpointTasks, useClient, useClientStatus, useClients, useCommandQueue, useCommandQueueTotals, useCommandViewData, useCommandViewDomainFilters, useCommandViewStats, useCommandViewStore, useCompanies, useCompany, useCompanyFacets, useCompleteDealTask, useContact, useContacts, useCostBreakdown, useCostByModel, useCostSummary, useCostTrends, useCreateApiKey, useCreateArtifact, useCreateClient, useCreateCompany, useCreateContact, useCreateCredential, useCreateDealNote, useCreateDealTask, useCreateList, useCreateMilestone, useCreateNote, useCreateOrgRole, useCreateProject, useCreateSchedule, useCreateSession, useCreateTask, useCreateWebhookEndpoint, useCredentials, useCrmActions, useCrmPipelineSummary, useCrmPrioritySettings, useCrmQuickMetrics, useDashboardMetrics, useDeactivateDeployment, useDeactivateMembership, useDealDetail, useDealNotes, useDealTasks, useDealTasksDue, useDeals, useDealsLookup, useDealsSummary, useDeleteApiKey, useDeleteClient, useDeleteCompanies, useDeleteContacts, useDeleteCredential, useDeleteDeal, useDeleteDeployment, useDeleteExecution, useDeleteList, useDeleteLists, useDeleteMilestone, useDeleteOrgRole, useDeleteProject, useDeleteRequest, useDeleteSchedule, useDeleteSession, useDeleteTask, useDeleteTask2, useDeleteWebhookEndpoint, useDeriveActions, useEffectivePermissions, useElevasisSystems, useErrorAnalysis, useErrorDetail, useErrorDetails, useErrorDistribution, useErrorNotification, useExecuteAction, useExecuteAsync, useExecuteResource, useExecution, useExecutionHealth, useExecutionLogSSE, useExecutionLogs, useExecutionLogsFilters, useExecutionPanelState, useExecutionSSE, useExecutions, useGetExecutionHistory, useGetSchedule, useGraphBackgroundStyles, useGraphTheme, useHasPermission, useInFlightExecutions, useLeadGenConfig, useList, useListActions, useListApiKeys, useListDeployments, useListExecutions, useListMember, useListMembers, useListProgress, useListRecords, useListSchedules, useListWebhookEndpoints, useLists, useListsTelemetry, useMarkAllAsRead, useMarkAsRead, useMilestones, useNewKnowledgeMapLayout, useNotificationAdapter, useNotificationCount, useNotifications, useOptionalElevasisSystems, useOrgRoles, useOrganizationMembers, useOrganizationPermissions, usePaginationState, usePatchTask, usePauseSchedule, usePermissionCatalog, useProject, useProjectActivities, useProjectMilestones, useProjectNotes, useProjectRealtime, useProjectTasks, useProjects, useReactivateMembership, useRecentCrmActivity, useRecentExecutionsByResource, useRemoveCompaniesFromList, useRequest, useRequestsList, useResetCrmPrioritySettings, useResolveAllErrors, useResolveError, useResolveErrorsByExecution, useResolvedOrganizationModel, useResourceDefinition, useResourceErrors, useResourceExecutions, useResourceSearch, useResources, useResourcesDomainFilters, useResourcesHealth, useResumeSchedule, useRetryExecution, useRevokeRole, useSSEConnection, useScheduledTasks, useSession, useSessionExecution, useSessionExecutions, useSessionMessages, useSessionWebSocket, useSessions, useSortedData, useStatusFilter, useSubmitAction, useSuccessNotification, useSystemHealth, useTableSelection, useTableSort, useTasks, useTestNotification, useTimeRangeDates, useTopFailingResources, useTransitionItem, useTransitionListCompany, useTransitionListMember, useTransitionState, useUnresolveError, useUnresolvedErrors, useUpdateAnchor, useUpdateApiKey, useUpdateClient, useUpdateCompany, useUpdateContact, useUpdateCredential, useUpdateCrmPrioritySettings, useUpdateList, useUpdateListConfig, useUpdateListStatus, useUpdateMemberConfig, useUpdateMilestone, useUpdateOrgRole, useUpdateProject, useUpdateRequestStatus, useUpdateSchedule, useUpdateTask, useUpdateWebhookEndpoint, useUserMemberships, useVerifyCredential, useVisibleResources, useWarningNotification, useWorkflowExecution };