import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@plunk/ui'; import type {Workflow} from '@plunk/db'; import type {PaginatedResponse} from '@plunk/types'; import {DashboardLayout} from '../../components/DashboardLayout'; import {network} from '../../lib/network'; import {formatRelativeTime} from '../../lib/dateUtils'; import {Calendar, Edit, Plus, Power, PowerOff, Search, Trash2, Workflow as WorkflowIcon} from 'lucide-react'; import {NextSeo} from 'next-seo'; import Link from 'next/link'; import {useState} from 'react'; import {toast} from 'sonner'; import useSWR from 'swr'; import {WorkflowSchemas} from '@plunk/shared'; import dayjs from 'dayjs'; export default function WorkflowsPage() { const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const [searchInput, setSearchInput] = useState(''); const [showCreateDialog, setShowCreateDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [workflowToDelete, setWorkflowToDelete] = useState(null); const {data, mutate, isLoading} = useSWR< PaginatedResponse >(`/workflows?page=${page}&pageSize=20${search ? `&search=${search}` : ''}`, {revalidateOnFocus: false}); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setSearch(searchInput); setPage(1); }; const handleDelete = async () => { if (!workflowToDelete) return; try { await network.fetch('DELETE', `/workflows/${workflowToDelete}`); toast.success('Workflow deleted successfully'); void mutate(); } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to delete workflow'); } finally { setWorkflowToDelete(null); } }; const handleToggleEnabled = async (workflowId: string, currentlyEnabled: boolean) => { try { await network.fetch('PATCH', `/workflows/${workflowId}`, { enabled: !currentlyEnabled, }); toast.success(`Workflow ${!currentlyEnabled ? 'enabled' : 'disabled'} successfully`); void mutate(); } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to toggle workflow'); } }; return ( <>
{/* Header */}

Workflows

Automate your email campaigns with powerful workflows.{' '} {data?.total ? `${data.total} total workflows` : ''}

{/* Search & Filters */}
setSearchInput(e.target.value)} className="pl-10" />
{search && ( )}
{/* Workflows Grid */}
{isLoading ? (

Loading workflows...

) : data?.data.length === 0 ? (

No workflows found

{search ? 'Try adjusting your search terms' : 'Get started by creating your first workflow'}

{!search && ( )}
) : ( <> {data?.data.map(workflow => (
{workflow.name} {workflow.enabled ? ( <> Active ) : ( <> Disabled )} {workflow.triggerConfig && typeof workflow.triggerConfig === 'object' && 'eventName' in workflow.triggerConfig && ( {String(workflow.triggerConfig.eventName)} )}
{workflow.description && ( {workflow.description} )}
{workflow._count?.steps ?? 0} steps
{workflow._count?.executions ?? 0}{' '} executions
Created {formatRelativeTime(workflow.createdAt)}
{dayjs(workflow.createdAt).format('DD MMMM YYYY, hh:mm')}
• Updated {formatRelativeTime(workflow.updatedAt)}
{dayjs(workflow.updatedAt).format('DD MMMM YYYY, hh:mm')}
))} {/* Pagination */} {data && data.totalPages > 1 && (

Showing {(page - 1) * data.pageSize + 1} to {Math.min(page * data.pageSize, data.total)} of{' '} {data.total} workflows

Page {page} of {data.totalPages}
)} )}
{/* Create Workflow Dialog */} mutate()} />
); } interface CreateWorkflowDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onSuccess: () => void; } function CreateWorkflowDialog({open, onOpenChange, onSuccess}: CreateWorkflowDialogProps) { const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [eventName, setEventName] = useState(''); const [allowReentry, setAllowReentry] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); // Fetch available event names const {data: eventNamesData} = useSWR<{eventNames: string[]}>(open ? '/events/names' : null, { revalidateOnFocus: false, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsSubmitting(true); try { const workflow = await network.fetch('POST', '/workflows', { name, description: description || undefined, eventName: eventName.trim(), allowReentry, enabled: false, }); toast.success('Workflow created successfully'); setName(''); setDescription(''); setEventName(''); setAllowReentry(false); onOpenChange(false); onSuccess(); // Redirect to the workflow editor window.location.href = `/workflows/${workflow.id}`; } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to create workflow'); } finally { setIsSubmitting(false); } }; return ( Create New Workflow
setName(e.target.value)} required placeholder="Welcome Email Sequence" />