diff --git a/apps/docs/content/docs/en/enterprise/data-drains.mdx b/apps/docs/content/docs/en/enterprise/data-drains.mdx index c1e5ced5876..620d6a13027 100644 --- a/apps/docs/content/docs/en/enterprise/data-drains.mdx +++ b/apps/docs/content/docs/en/enterprise/data-drains.mdx @@ -15,6 +15,10 @@ Drains are independent of [Data Retention](/enterprise/data-retention) but desig Go to **Settings → Enterprise → Data Drains** in your workspace, then click **New drain**. +![Data Drains settings page showing two configured drains — one exporting workflow logs to Amazon S3 daily, another exporting Copilot chats to an HTTPS webhook hourly](/static/enterprise/data-drains-list.png) + +![New data drain dialog with fields for name, source, cadence, destination, and S3 credentials](/static/enterprise/data-drains-new.png) + Each drain has four pieces: 1. A **source** — the category of data to export diff --git a/apps/docs/public/static/enterprise/data-drains-list.png b/apps/docs/public/static/enterprise/data-drains-list.png new file mode 100644 index 00000000000..b18af7cade5 Binary files /dev/null and b/apps/docs/public/static/enterprise/data-drains-list.png differ diff --git a/apps/docs/public/static/enterprise/data-drains-new.png b/apps/docs/public/static/enterprise/data-drains-new.png new file mode 100644 index 00000000000..4d85d0fc682 Binary files /dev/null and b/apps/docs/public/static/enterprise/data-drains-new.png differ diff --git a/apps/sim/ee/data-drains/components/data-drains-settings.tsx b/apps/sim/ee/data-drains/components/data-drains-settings.tsx index e917d465b6f..740a4f163d5 100644 --- a/apps/sim/ee/data-drains/components/data-drains-settings.tsx +++ b/apps/sim/ee/data-drains/components/data-drains-settings.tsx @@ -3,6 +3,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' +import { ChevronDown, Plus, Search } from 'lucide-react' import { Badge, Button, @@ -19,7 +20,6 @@ import { ModalContent, ModalFooter, ModalHeader, - ModalTitle, MoreHorizontal, Switch, Table, @@ -30,6 +30,8 @@ import { TableRow, toast, } from '@/components/emcn' +import { S3Icon } from '@/components/icons' +import { Input as BaseInput } from '@/components/ui' import type { CreateDataDrainBody, DataDrain, DataDrainRun } from '@/lib/api/contracts/data-drains' import { useSession } from '@/lib/auth/auth-client' import { cn } from '@/lib/core/utils/cn' @@ -70,9 +72,15 @@ const CADENCE_LABELS: Record<(typeof CADENCE_TYPES)[number], string> = { const SOURCE_OPTIONS = SOURCE_TYPES.map((t) => ({ value: t, label: SOURCE_LABELS[t] })) const CADENCE_OPTIONS = CADENCE_TYPES.map((t) => ({ value: t, label: CADENCE_LABELS[t] })) +function getDestinationIcon(type: (typeof DESTINATION_TYPES)[number]) { + if (type !== 's3') return null + return +} + const DESTINATION_OPTIONS = DESTINATION_TYPES.map((t) => ({ value: t, label: DESTINATION_LABELS[t], + iconElement: getDestinationIcon(t), })) export function DataDrainsSettings() { @@ -89,6 +97,19 @@ export function DataDrainsSettings() { const [createOpen, setCreateOpen] = useState(false) const [expandedDrainId, setExpandedDrainId] = useState(null) + const [searchTerm, setSearchTerm] = useState('') + + const query = searchTerm.trim().toLowerCase() + const filteredDrains = !query + ? (drains ?? []) + : (drains ?? []).filter((drain) => + [ + drain.name, + SOURCE_LABELS[drain.source], + DESTINATION_LABELS[drain.destinationType], + CADENCE_LABELS[drain.scheduleCadence], + ].some((value) => value.toLowerCase().includes(query)) + ) if (sessionPending || orgsLoading || drainsLoading) { return @@ -111,61 +132,75 @@ export function DataDrainsSettings() { } return ( -
+
Drains continuously export Sim data to your own storage on a schedule. Combine with Data Retention to satisfy long-term compliance archives. -
-
- {drains?.length ?? 0} drain{(drains?.length ?? 0) === 1 ? '' : 's'} +
+
+ + setSearchTerm(e.target.value)} + className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0' + />
- {drainsError ? ( - - Failed to load data drains: {toError(drainsError).message} - - ) : drains && drains.length > 0 ? ( - - - - Name - Source - Destination - Cadence - Last run - Enabled - - - - - {drains.map((drain) => ( - - setExpandedDrainId(expandedDrainId === drain.id ? null : drain.id) - } - /> - ))} - -
- ) : ( -
-
No drains yet
-
- Create a drain to start exporting workflow logs, audit events, and copilot data to S3 or - your own webhook. +
+ {drainsError ? ( + + Failed to load data drains: {toError(drainsError).message} + + ) : drains && drains.length > 0 ? ( + filteredDrains.length > 0 ? ( + + + + Name + Source + Destination + Cadence + Last run + Enabled + + + + + {filteredDrains.map((drain) => ( + + setExpandedDrainId(expandedDrainId === drain.id ? null : drain.id) + } + /> + ))} + +
+ ) : ( +
+ No results for "{searchTerm.trim()}" +
+ ) + ) : ( +
+ Click "New drain" above to get started
-
- )} + )} +
{createOpen && ( setCreateOpen(false)} /> @@ -231,7 +266,17 @@ function DrainRow({ drain, organizationId, expanded, onToggleExpand }: DrainRowP return ( <> - {drain.name} + +
+ + {drain.name} +
+
{SOURCE_LABELS[drain.source]} @@ -288,7 +333,7 @@ function DrainRunsPanel({ organizationId, drainId }: DrainRunsPanelProps) { const { data: runs, isLoading } = useDataDrainRuns(organizationId, drainId, 10) if (isLoading) { - return
Loading runs…
+ return
Loading runs...
} if (!runs || runs.length === 0) { return
No runs yet.
@@ -378,47 +423,57 @@ function CreateDrainModal({ organizationId, onClose }: CreateDrainModalProps) { return ( !open && onClose()}> - - - New data drain - - - - setName(e.target.value)} - placeholder='Workflow logs to S3' - /> - - - setSource(v as (typeof SOURCE_TYPES)[number])} - options={SOURCE_OPTIONS} - dropdownWidth='trigger' - /> - - - setCadence(v as (typeof CADENCE_TYPES)[number])} - options={CADENCE_OPTIONS} - dropdownWidth='trigger' - /> - - - handleDestinationChange(v as (typeof DESTINATION_TYPES)[number])} - options={DESTINATION_OPTIONS} - dropdownWidth='trigger' - /> - + + New data drain + +
+ + setName(e.target.value)} + placeholder='Workflow logs export' + /> + + + setSource(v as (typeof SOURCE_TYPES)[number])} + options={SOURCE_OPTIONS} + dropdownWidth='trigger' + /> + + + setCadence(v as (typeof CADENCE_TYPES)[number])} + options={CADENCE_OPTIONS} + dropdownWidth='trigger' + /> + + + handleDestinationChange(v as (typeof DESTINATION_TYPES)[number])} + options={DESTINATION_OPTIONS} + dropdownWidth='trigger' + overlayContent={ +
+ {getDestinationIcon(destinationType)} + + {DESTINATION_LABELS[destinationType]} + +
+ } + /> +
+
- +
+ +
-