mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Generated by Spark: Add workflow filtering by status (success, failed, running)
This commit is contained in:
@@ -32,6 +32,9 @@ import {
|
||||
Copy,
|
||||
Pencil,
|
||||
Link,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
ArrowsClockwise,
|
||||
} from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { LazyInlineMonacoEditor } from '@/components/molecules/LazyInlineMonacoEditor'
|
||||
@@ -65,11 +68,24 @@ export function WorkflowDesigner({ workflows, onWorkflowsChange }: WorkflowDesig
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 })
|
||||
const [connectingFrom, setConnectingFrom] = useState<string | null>(null)
|
||||
const [statusFilter, setStatusFilter] = useState<'all' | 'success' | 'failed' | 'running'>('all')
|
||||
const canvasRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const selectedWorkflow = workflows.find((w) => w.id === selectedWorkflowId)
|
||||
const selectedNode = selectedWorkflow?.nodes.find((n) => n.id === selectedNodeId)
|
||||
|
||||
const filteredWorkflows = workflows.filter((workflow) => {
|
||||
if (statusFilter === 'all') return true
|
||||
return workflow.status === statusFilter
|
||||
})
|
||||
|
||||
const statusCounts = {
|
||||
all: workflows.length,
|
||||
success: workflows.filter((w) => w.status === 'success').length,
|
||||
failed: workflows.filter((w) => w.status === 'failed').length,
|
||||
running: workflows.filter((w) => w.status === 'running').length,
|
||||
}
|
||||
|
||||
const handleCreateWorkflow = () => {
|
||||
if (!newWorkflowName.trim()) {
|
||||
toast.error('Please enter a workflow name')
|
||||
@@ -325,9 +341,61 @@ export function WorkflowDesigner({ workflows, onWorkflowsChange }: WorkflowDesig
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="text-xs text-muted-foreground mb-2 block">Filter by Status</Label>
|
||||
<Select value={statusFilter} onValueChange={(value: any) => setStatusFilter(value)}>
|
||||
<SelectTrigger className="h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">
|
||||
<div className="flex items-center justify-between gap-2 w-full">
|
||||
<span>All Statuses</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{statusCounts.all}
|
||||
</Badge>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="success">
|
||||
<div className="flex items-center justify-between gap-2 w-full">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<CheckCircle size={14} weight="fill" className="text-green-500" />
|
||||
Success
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{statusCounts.success}
|
||||
</Badge>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="failed">
|
||||
<div className="flex items-center justify-between gap-2 w-full">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<XCircle size={14} weight="fill" className="text-red-500" />
|
||||
Failed
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{statusCounts.failed}
|
||||
</Badge>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="running">
|
||||
<div className="flex items-center justify-between gap-2 w-full">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<ArrowsClockwise size={14} weight="bold" className="text-blue-500" />
|
||||
Running
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{statusCounts.running}
|
||||
</Badge>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="flex-1">
|
||||
<div className="space-y-2">
|
||||
{workflows.map((workflow) => (
|
||||
{filteredWorkflows.map((workflow) => (
|
||||
<Card
|
||||
key={workflow.id}
|
||||
className={`cursor-pointer transition-all ${
|
||||
@@ -340,7 +408,7 @@ export function WorkflowDesigner({ workflows, onWorkflowsChange }: WorkflowDesig
|
||||
<CardHeader className="p-4">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<CardTitle className="text-sm truncate">{workflow.name}</CardTitle>
|
||||
<Badge
|
||||
variant={workflow.isActive ? 'default' : 'outline'}
|
||||
@@ -348,19 +416,55 @@ export function WorkflowDesigner({ workflows, onWorkflowsChange }: WorkflowDesig
|
||||
>
|
||||
{workflow.isActive ? 'Active' : 'Inactive'}
|
||||
</Badge>
|
||||
{workflow.status && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`text-xs flex items-center gap-1 ${
|
||||
workflow.status === 'success'
|
||||
? 'border-green-500 text-green-500'
|
||||
: workflow.status === 'failed'
|
||||
? 'border-red-500 text-red-500'
|
||||
: 'border-blue-500 text-blue-500'
|
||||
}`}
|
||||
>
|
||||
{workflow.status === 'success' && (
|
||||
<>
|
||||
<CheckCircle size={12} weight="fill" />
|
||||
Success
|
||||
</>
|
||||
)}
|
||||
{workflow.status === 'failed' && (
|
||||
<>
|
||||
<XCircle size={12} weight="fill" />
|
||||
Failed
|
||||
</>
|
||||
)}
|
||||
{workflow.status === 'running' && (
|
||||
<>
|
||||
<ArrowsClockwise size={12} weight="bold" className="animate-spin" />
|
||||
Running
|
||||
</>
|
||||
)}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{workflow.description && (
|
||||
<CardDescription className="text-xs mt-1 line-clamp-2">
|
||||
{workflow.description}
|
||||
</CardDescription>
|
||||
)}
|
||||
<div className="flex gap-2 mt-2">
|
||||
<div className="flex gap-2 mt-2 flex-wrap">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{workflow.nodes.length} nodes
|
||||
</Badge>
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{workflow.connections.length} connections
|
||||
</Badge>
|
||||
{workflow.lastRun && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
Last run: {new Date(workflow.lastRun).toLocaleDateString()}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -396,6 +500,18 @@ export function WorkflowDesigner({ workflows, onWorkflowsChange }: WorkflowDesig
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
{filteredWorkflows.length === 0 && workflows.length > 0 && (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center text-muted-foreground">
|
||||
<FlowArrow size={48} className="mx-auto mb-2 opacity-50" weight="duotone" />
|
||||
<p className="text-sm">No workflows match this filter</p>
|
||||
<Button size="sm" className="mt-2" onClick={() => setStatusFilter('all')}>
|
||||
Clear Filter
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{workflows.length === 0 && (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center text-muted-foreground">
|
||||
|
||||
@@ -188,8 +188,210 @@
|
||||
}
|
||||
],
|
||||
"isActive": true,
|
||||
"status": "success",
|
||||
"lastRun": 1704153600000,
|
||||
"createdAt": 1704067200000,
|
||||
"updatedAt": 1704067200000
|
||||
},
|
||||
{
|
||||
"id": "workflow-2",
|
||||
"name": "Email Notification Pipeline",
|
||||
"description": "Send automated email notifications to users",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node-4",
|
||||
"type": "trigger",
|
||||
"name": "Schedule Trigger",
|
||||
"position": { "x": 100, "y": 100 },
|
||||
"data": {
|
||||
"label": "Daily at 9 AM"
|
||||
},
|
||||
"config": {
|
||||
"triggerType": "schedule",
|
||||
"scheduleExpression": "0 9 * * *"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-5",
|
||||
"type": "database",
|
||||
"name": "Fetch Users",
|
||||
"position": { "x": 300, "y": 100 },
|
||||
"data": {
|
||||
"label": "Get Active Users"
|
||||
},
|
||||
"config": {
|
||||
"databaseQuery": "SELECT * FROM users WHERE active = true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-6",
|
||||
"type": "lambda",
|
||||
"name": "Send Emails",
|
||||
"position": { "x": 500, "y": 100 },
|
||||
"data": {
|
||||
"label": "Process Email Queue"
|
||||
},
|
||||
"config": {
|
||||
"lambdaCode": "// Send email logic"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"id": "conn-3",
|
||||
"source": "node-4",
|
||||
"target": "node-5"
|
||||
},
|
||||
{
|
||||
"id": "conn-4",
|
||||
"source": "node-5",
|
||||
"target": "node-6"
|
||||
}
|
||||
],
|
||||
"isActive": true,
|
||||
"status": "running",
|
||||
"lastRun": 1704240000000,
|
||||
"createdAt": 1704067200000,
|
||||
"updatedAt": 1704240000000
|
||||
},
|
||||
{
|
||||
"id": "workflow-3",
|
||||
"name": "Payment Processing",
|
||||
"description": "Handle payment transactions and update order status",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node-7",
|
||||
"type": "trigger",
|
||||
"name": "Payment Event",
|
||||
"position": { "x": 100, "y": 100 },
|
||||
"data": {
|
||||
"label": "Payment Received"
|
||||
},
|
||||
"config": {
|
||||
"triggerType": "webhook"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-8",
|
||||
"type": "api",
|
||||
"name": "Validate Payment",
|
||||
"position": { "x": 300, "y": 100 },
|
||||
"data": {
|
||||
"label": "Check Payment Gateway"
|
||||
},
|
||||
"config": {
|
||||
"httpMethod": "POST",
|
||||
"apiEndpoint": "https://api.stripe.com/v1/charges"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-9",
|
||||
"type": "condition",
|
||||
"name": "Payment Valid?",
|
||||
"position": { "x": 500, "y": 100 },
|
||||
"data": {
|
||||
"label": "Check Status"
|
||||
},
|
||||
"config": {
|
||||
"condition": "payment.status === 'succeeded'"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-10",
|
||||
"type": "database",
|
||||
"name": "Update Order",
|
||||
"position": { "x": 700, "y": 100 },
|
||||
"data": {
|
||||
"label": "Mark as Paid"
|
||||
},
|
||||
"config": {
|
||||
"databaseQuery": "UPDATE orders SET status = 'paid'"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"id": "conn-5",
|
||||
"source": "node-7",
|
||||
"target": "node-8"
|
||||
},
|
||||
{
|
||||
"id": "conn-6",
|
||||
"source": "node-8",
|
||||
"target": "node-9"
|
||||
},
|
||||
{
|
||||
"id": "conn-7",
|
||||
"source": "node-9",
|
||||
"target": "node-10"
|
||||
}
|
||||
],
|
||||
"isActive": true,
|
||||
"status": "failed",
|
||||
"lastRun": 1704226800000,
|
||||
"createdAt": 1704067200000,
|
||||
"updatedAt": 1704226800000
|
||||
},
|
||||
{
|
||||
"id": "workflow-4",
|
||||
"name": "Data Backup Task",
|
||||
"description": "Automated database backup and archival",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node-11",
|
||||
"type": "trigger",
|
||||
"name": "Nightly Backup",
|
||||
"position": { "x": 100, "y": 100 },
|
||||
"data": {
|
||||
"label": "Every Night at 2 AM"
|
||||
},
|
||||
"config": {
|
||||
"triggerType": "schedule",
|
||||
"scheduleExpression": "0 2 * * *"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-12",
|
||||
"type": "database",
|
||||
"name": "Export Data",
|
||||
"position": { "x": 300, "y": 100 },
|
||||
"data": {
|
||||
"label": "Create Backup"
|
||||
},
|
||||
"config": {
|
||||
"databaseQuery": "pg_dump database"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-13",
|
||||
"type": "lambda",
|
||||
"name": "Upload to S3",
|
||||
"position": { "x": 500, "y": 100 },
|
||||
"data": {
|
||||
"label": "Store Backup"
|
||||
},
|
||||
"config": {
|
||||
"lambdaCode": "// S3 upload logic"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"id": "conn-8",
|
||||
"source": "node-11",
|
||||
"target": "node-12"
|
||||
},
|
||||
{
|
||||
"id": "conn-9",
|
||||
"source": "node-12",
|
||||
"target": "node-13"
|
||||
}
|
||||
],
|
||||
"isActive": false,
|
||||
"status": "success",
|
||||
"lastRun": 1704171600000,
|
||||
"createdAt": 1704067200000,
|
||||
"updatedAt": 1704171600000
|
||||
}
|
||||
],
|
||||
"project-lambdas": [
|
||||
|
||||
@@ -237,6 +237,8 @@ export interface Workflow {
|
||||
nodes: WorkflowNode[]
|
||||
connections: WorkflowConnection[]
|
||||
isActive: boolean
|
||||
status?: 'success' | 'failed' | 'running'
|
||||
lastRun?: number
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user