diff --git a/src/components/Workflows/WorkflowsTable.module.css b/src/components/Workflows/WorkflowsTable.module.css index d099e03..164e10e 100644 --- a/src/components/Workflows/WorkflowsTable.module.css +++ b/src/components/Workflows/WorkflowsTable.module.css @@ -44,13 +44,13 @@ .workflowId { font-family: 'Courier New', monospace; font-size: 0.9em; - color: var(--color-text-secondary, #666); + color: var(--color-gray); cursor: help; } .workflowName { font-weight: 500; - color: var(--color-text-primary, #333); + color: var(--color-text); } .statusBadge { @@ -65,21 +65,18 @@ } .status-running { - background-color: #d4edda; - color: #155724; - border: 1px solid #c3e6cb; + background-color: var(--color-gray); + color: white; } .status-completed { - background-color: #d1ecf1; - color: #0c5460; - border: 1px solid #bee5eb; + background-color: var(--color-secondary); + color: white; } .status-failed { - background-color: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; + background-color: var(--color-red); + color: white; } .status-stopped { @@ -95,11 +92,10 @@ } .roundNumber { - font-weight: 600; - color: var(--color-primary, #007bff); - background-color: var(--color-primary-light, #e3f2fd); + font-weight: 400; + color: var(--color-text); + background-color: var(--color-bg); padding: 0.25rem 0.5rem; - border-radius: 8px; font-size: 0.9em; min-width: 24px; text-align: center; @@ -108,8 +104,8 @@ .messageCount { font-weight: 500; - color: var(--color-text-secondary, #666); - background-color: var(--color-bg-secondary, #f8f9fa); + color: var(--color-text); + background-color: var(--color-bg); padding: 0.25rem 0.5rem; border-radius: 8px; font-size: 0.9em; @@ -139,7 +135,7 @@ /* Dark mode support */ @media (prefers-color-scheme: dark) { .workflowId { - color: #adb5bd; + color: var(--color-gray); } .workflowName { @@ -147,43 +143,33 @@ } .status-running { - background-color: #155724; - color: #d4edda; + background-color: var(--color-gray); + color: white; } .status-completed { - background-color: #0c5460; - color: #d1ecf1; + background-color: var(--color-secondary); + color: white; } .status-failed { - background-color: #721c24; - color: #f8d7da; + background-color: var(--color-red); + color: white; } .status-stopped { - background-color: #495057; - color: #f5f5f5; + background-color: var(--color-primary); + color: white; } - .status-pending { - background-color: #856404; - color: #fff3cd; - } .roundNumber { - background-color: #1e3a8a; - color: #bfdbfe; + background-color: var(--color-bg); + color: var(--color-text); } .messageCount { - background-color: #374151; - color: #d1d5db; - } - - .errorState { - background-color: #2d1b1e; - border-color: #5c2b33; - color: #f5c6cb; + background-color: var(--color-gray); + color: white; } } diff --git a/src/components/Workflows/WorkflowsTable.tsx b/src/components/Workflows/WorkflowsTable.tsx index d7c7e04..af4a34c 100644 --- a/src/components/Workflows/WorkflowsTable.tsx +++ b/src/components/Workflows/WorkflowsTable.tsx @@ -1,318 +1,22 @@ -import { useMemo, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { IoIosTrash, IoIosPlay } from 'react-icons/io'; -import { MdModeEdit } from 'react-icons/md'; -import { FormGenerator, ColumnConfig } from '../FormGenerator/FormGenerator'; -import { useWorkflows, useWorkflowOperations, Workflow } from '../../hooks/useWorkflows'; -import { useLanguage } from '../../contexts/LanguageContext'; +import { FormGenerator } from '../FormGenerator/FormGenerator'; import { Popup, EditForm } from '../Popup'; -import type { EditFieldConfig } from '../Popup/EditForm'; +import { useWorkflowsLogic } from './workflowsLogic'; +import { WorkflowsTableProps } from './workflowsTypes'; import styles from './WorkflowsTable.module.css'; -interface WorkflowsTableProps { - className?: string; -} - function WorkflowsTable({ className = '' }: WorkflowsTableProps) { - const { workflows, loading, error, refetch } = useWorkflows(); - const navigate = useNavigate(); - - // Debug: Log workflow data to see the actual structure - console.log('Workflows data:', workflows); - const { - deleteWorkflow, - updateWorkflow, - deletingWorkflows - } = useWorkflowOperations(); - const { t } = useLanguage(); - - // Edit modal state - const [editModalOpen, setEditModalOpen] = useState(false); - const [editingWorkflow, setEditingWorkflow] = useState(null); - - // Configure edit fields for workflow name editing - const editWorkflowFields: EditFieldConfig[] = useMemo(() => [ - { - key: 'name', - label: t('workflows.field.name', 'Workflow Name'), - type: 'string', - editable: true, - required: true, - validator: (value: string) => { - if (!value || value.trim() === '') { - return t('workflows.validation.nameRequired', 'Workflow name cannot be empty'); - } - if (value.length > 100) { - return t('workflows.validation.nameTooLong', 'Workflow name cannot exceed 100 characters'); - } - return null; - } - } - ], [t]); - - // Configure columns for the workflows table - const columns: ColumnConfig[] = useMemo(() => [ - { - key: 'id', - label: t('workflows.column.id'), - type: 'string', - width: 180, - minWidth: 150, - maxWidth: 250, - sortable: true, - filterable: true, - searchable: true, - formatter: (value: string) => ( - - {value.length > 8 ? `${value.substring(0, 8)}...` : value} - - ) - }, - { - key: 'name', - label: t('workflows.column.name'), - type: 'string', - width: 200, - minWidth: 150, - maxWidth: 300, - sortable: true, - filterable: true, - searchable: true, - formatter: (value: string | undefined) => ( - - {value || t('workflows.unnamed')} - - ) - }, - { - key: 'status', - label: t('workflows.column.status'), - type: 'enum', - width: 120, - minWidth: 100, - maxWidth: 150, - sortable: true, - filterable: true, - filterOptions: ['running', 'completed', 'failed', 'stopped', 'pending'], - formatter: (value: string) => ( - - {t(`workflows.status.${value}`, value)} - - ) - }, - { - key: 'currentRound', - label: t('workflows.column.round'), - type: 'number', - width: 80, - minWidth: 60, - maxWidth: 100, - sortable: true, - filterable: true, - formatter: (value: number | undefined) => ( - - {value || 1} - - ) - }, - { - key: 'startedAt', - label: t('workflows.column.started'), - type: 'date', - width: 140, - minWidth: 120, - maxWidth: 180, - sortable: true, - filterable: true, - formatter: (value: string | undefined) => { - if (!value) return '-'; - try { - const date = new Date(value); - return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); - } catch { - return value; - } - } - }, - { - key: 'lastActivity', - label: t('workflows.column.lastActivity'), - type: 'date', - width: 140, - minWidth: 120, - maxWidth: 180, - sortable: true, - filterable: true, - formatter: (value: string | undefined) => { - if (!value) return '-'; - try { - const date = new Date(value); - return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); - } catch { - return value; - } - } - }, - { - key: 'messages', - label: t('workflows.column.messages'), - type: 'number', - width: 100, - minWidth: 80, - maxWidth: 120, - sortable: true, - filterable: false, - formatter: (value: any[] | undefined) => ( - - {value?.length || 0} - - ) - } - ], [t]); - - // Handle workflow actions - - const handleDeleteWorkflow = async (workflow: Workflow) => { - const workflowName = workflow.name || workflow.id; - if (window.confirm(t('workflows.delete.confirm').replace('{name}', workflowName))) { - const success = await deleteWorkflow(workflow.id); - if (success) { - refetch(); // Refresh the workflows list - } - } - }; - - // Handle single workflow deletion for bulk delete - const handleDeleteSingle = async (workflow: Workflow) => { - const workflowName = workflow.name || workflow.id; - if (window.confirm(t('workflows.delete.confirm', 'Are you sure you want to delete "{name}"?').replace('{name}', workflowName))) { - const success = await deleteWorkflow(workflow.id); - if (success) { - refetch(); // Refresh the workflows list - } else { - console.error('Delete failed for workflow:', workflow.id); - } - } - }; - - // Handle multiple workflow deletion - const handleDeleteMultiple = async (workflowsToDelete: Workflow[]) => { - const workflowCount = workflowsToDelete.length; - if (window.confirm(t('workflows.delete.confirmMultiple', 'Are you sure you want to delete {count} workflows?').replace('{count}', workflowCount.toString()))) { - // Start all delete operations simultaneously - const deletePromises = workflowsToDelete.map(async (workflow) => { - try { - const success = await deleteWorkflow(workflow.id); - return { workflowId: workflow.id, success }; - } catch (error) { - console.error('Failed to delete workflow:', workflow.id, error); - return { workflowId: workflow.id, success: false }; - } - }); - - // Wait for all deletions to complete - const results = await Promise.all(deletePromises); - - // Check if any deletions failed - const failedDeletions = results.filter(result => !result.success); - if (failedDeletions.length > 0) { - console.error('Some workflow deletions failed:', failedDeletions); - } - - // Refresh the workflow list regardless of individual failures - refetch(); - } - }; - - // Handle edit workflow - const handleEditWorkflow = (workflow: Workflow) => { - setEditingWorkflow(workflow); - setEditModalOpen(true); - }; - - // Handle save workflow - const handleSaveWorkflow = async (updatedWorkflow: Workflow) => { - if (!editingWorkflow) return; - - try { - // Call API to update workflow name - const result = await updateWorkflow(editingWorkflow.id, { - name: updatedWorkflow.name - }); - - if (result.success) { - // Close modal - setEditModalOpen(false); - setEditingWorkflow(null); - - // Refresh workflow list - await refetch(); - } else { - console.error('Failed to update workflow:', result.error); - // TODO: Show error message to user - } - } catch (error) { - console.error('Failed to update workflow:', error); - // TODO: Show error message to user - } - }; - - // Handle cancel edit - const handleCancelEdit = () => { - setEditModalOpen(false); - setEditingWorkflow(null); - }; - - // Handle play workflow - navigate to dashboard with workflow ID - const handlePlayWorkflow = (workflow: Workflow) => { - // Navigate to dashboard with workflow ID as URL parameter - navigate(`/dashboard?workflowId=${workflow.id}`); - }; - - // Configure action buttons - const actions = useMemo(() => [ - { - label: t('workflows.action.play'), - icon: (_row: Workflow) => { - return ; - }, - onClick: (row: Workflow) => { - handlePlayWorkflow(row); - } - }, - { - label: t('workflows.action.edit'), - icon: (_row: Workflow) => { - return ; - }, - onClick: (row: Workflow) => { - handleEditWorkflow(row); - } - }, - { - label: t('workflows.action.delete'), - icon: (_row: Workflow) => { - return ; - }, - onClick: (row: Workflow) => { - if (!deletingWorkflows.has(row.id)) { - handleDeleteWorkflow(row); - } - } - }, - - ], [t, deletingWorkflows, handleDeleteWorkflow, handleEditWorkflow, handlePlayWorkflow]); + const logic = useWorkflowsLogic(); // Show error state - if (error) { + if (logic.error) { return (
-

{t('workflows.error.loading')} {error}

-
@@ -322,20 +26,20 @@ function WorkflowsTable({ className = '' }: WorkflowsTableProps) { return (
{ + onRowClick={(workflow: any) => { // TODO: Navigate to workflow detail view console.log('Clicked workflow:', workflow); }} @@ -343,19 +47,19 @@ function WorkflowsTable({ className = '' }: WorkflowsTableProps) { {/* Edit Workflow Modal */} - {editingWorkflow && ( + {logic.editingWorkflow && ( )} diff --git a/src/components/Workflows/index.ts b/src/components/Workflows/index.ts index e7b0989..dc20a54 100644 --- a/src/components/Workflows/index.ts +++ b/src/components/Workflows/index.ts @@ -1 +1,3 @@ -export { default as WorkflowsTable } from './WorkflowsTable'; \ No newline at end of file +export { default as WorkflowsTable } from './WorkflowsTable'; +export { useWorkflowsLogic } from './workflowsLogic'; +export * from './workflowsTypes'; \ No newline at end of file diff --git a/src/components/Workflows/workflowsLogic.tsx b/src/components/Workflows/workflowsLogic.tsx new file mode 100644 index 0000000..4bc079a --- /dev/null +++ b/src/components/Workflows/workflowsLogic.tsx @@ -0,0 +1,454 @@ +import { useState, useEffect, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { IoIosTrash, IoIosPlay } from 'react-icons/io'; +import { MdModeEdit } from 'react-icons/md'; + +import { useWorkflows, useWorkflowOperations, Workflow } from '../../hooks/useWorkflows'; +import { useApiRequest } from '../../hooks/useApi'; +import { useLanguage } from '../../contexts/LanguageContext'; +import type { EditFieldConfig } from '../Popup/EditForm'; + +import type { + WorkflowsLogicReturn, + WorkflowMessageCounts, + WorkflowActionConfig, + WorkflowColumnConfig +} from './workflowsTypes'; +import styles from './WorkflowsTable.module.css'; + +export function useWorkflowsLogic(): WorkflowsLogicReturn { + const { workflows, loading, error, refetch } = useWorkflows(); + const navigate = useNavigate(); + const { t } = useLanguage(); + + // State to track message counts for each workflow + const [workflowMessageCounts, setWorkflowMessageCounts] = useState({}); + const { request } = useApiRequest(); + + // Debug: Log workflow data to see the actual structure + console.log('Workflows data:', workflows); + if (workflows && workflows.length > 0) { + const firstWorkflow = workflows[0]; + console.log('First workflow object:', firstWorkflow); + console.log('First workflow keys:', Object.keys(firstWorkflow)); + console.log('First workflow stats:', firstWorkflow.stats); + if (firstWorkflow.stats) { + console.log('Stats keys:', Object.keys(firstWorkflow.stats)); + console.log('Stats object:', firstWorkflow.stats); + } + console.log('First workflow messages array:', firstWorkflow.messages, 'length:', firstWorkflow.messages?.length); + } + + const { + deleteWorkflow, + updateWorkflow, + deletingWorkflows + } = useWorkflowOperations(); + + // Edit modal state + const [editModalOpen, setEditModalOpen] = useState(false); + const [editingWorkflow, setEditingWorkflow] = useState(null); + + // Function to fetch message count for a single workflow + const fetchMessageCount = async (workflowId: string) => { + try { + console.log(`Fetching messages for workflow: ${workflowId}`); + const messages = await request({ + url: `/api/workflows/${workflowId}/messages`, + method: 'get' + }); + + console.log(`Messages for ${workflowId}:`, messages, 'length:', messages?.length); + const count = Array.isArray(messages) ? messages.length : 0; + console.log(`Setting message count for ${workflowId}:`, count); + setWorkflowMessageCounts(prev => ({ + ...prev, + [workflowId]: count + })); + } catch (error) { + console.error(`Failed to fetch message count for workflow ${workflowId}:`, error); + // Set count to 0 for failed requests + setWorkflowMessageCounts(prev => ({ + ...prev, + [workflowId]: 0 + })); + } + }; + + // Effect to fetch message counts when workflows change + useEffect(() => { + if (workflows && workflows.length > 0) { + workflows.forEach(workflow => { + // Only fetch if we don't already have the count + if (!(workflow.id in workflowMessageCounts)) { + fetchMessageCount(workflow.id); + } + }); + } + }, [workflows]); // Don't include workflowMessageCounts to avoid infinite loop + + // Configure edit fields for workflow name editing + const editWorkflowFields: EditFieldConfig[] = useMemo(() => [ + { + key: 'name', + label: t('workflows.field.name', 'Workflow Name'), + type: 'string', + editable: true, + required: true, + validator: (value: string) => { + if (!value || value.trim() === '') { + return t('workflows.validation.nameRequired', 'Workflow name cannot be empty'); + } + if (value.length > 100) { + return t('workflows.validation.nameTooLong', 'Workflow name cannot exceed 100 characters'); + } + return null; + } + } + ], [t]); + + // Configure columns for the workflows table + const columns: WorkflowColumnConfig[] = useMemo(() => [ + { + key: 'id', + label: t('workflows.column.id'), + type: 'string', + width: 180, + minWidth: 150, + maxWidth: 250, + sortable: true, + filterable: true, + searchable: true, + formatter: (value: string) => ( + + {value.length > 8 ? `${value.substring(0, 8)}...` : value} + + ) + }, + { + key: 'name', + label: t('workflows.column.name'), + type: 'string', + width: 200, + minWidth: 150, + maxWidth: 300, + sortable: true, + filterable: true, + searchable: true, + formatter: (value: string | undefined) => ( + + {value || t('workflows.unnamed')} + + ) + }, + { + key: 'status', + label: t('workflows.column.status'), + type: 'enum', + width: 120, + minWidth: 100, + maxWidth: 150, + sortable: true, + filterable: true, + filterOptions: ['running', 'completed', 'failed', 'stopped', 'pending'], + formatter: (value: string) => ( + + {t(`workflows.status.${value}`, value)} + + ) + }, + { + key: 'currentRound', + label: t('workflows.column.round'), + type: 'number', + width: 80, + minWidth: 60, + maxWidth: 100, + sortable: true, + filterable: true, + formatter: (value: number | undefined) => ( + + {value || 1} + + ) + }, + { + key: 'startedAt', + label: t('workflows.column.started'), + type: 'date', + width: 140, + minWidth: 120, + maxWidth: 180, + sortable: true, + filterable: true, + formatter: (value: string | number | undefined) => { + if (!value) return '-'; + try { + let date: Date; + + // Handle Unix timestamp (as string or number) + if (typeof value === 'string' && /^\d+$/.test(value)) { + // Unix timestamp as string + date = new Date(parseInt(value) * 1000); + } else if (typeof value === 'number') { + // Unix timestamp as number + date = new Date(value * 1000); + } else { + // ISO string or other date format + date = new Date(value); + } + + // Check if date is valid + if (isNaN(date.getTime())) { + console.warn('Invalid startedAt date:', value); + return '-'; + } + return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + } catch (error) { + console.warn('Error parsing startedAt date:', value, error); + return '-'; + } + } + }, + { + key: 'lastActivity', + label: t('workflows.column.lastActivity'), + type: 'date', + width: 140, + minWidth: 120, + maxWidth: 180, + sortable: true, + filterable: true, + formatter: (value: string | number | undefined) => { + if (!value) return '-'; + try { + let date: Date; + + // Handle Unix timestamp (as string or number) + if (typeof value === 'string' && /^\d+$/.test(value)) { + // Unix timestamp as string + date = new Date(parseInt(value) * 1000); + } else if (typeof value === 'number') { + // Unix timestamp as number + date = new Date(value * 1000); + } else { + // ISO string or other date format + date = new Date(value); + } + + // Check if date is valid + if (isNaN(date.getTime())) { + console.warn('Invalid lastActivity date:', value); + return '-'; + } + return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + } catch (error) { + console.warn('Error parsing lastActivity date:', value, error); + return '-'; + } + } + }, + { + key: 'messages', + label: t('workflows.column.messages'), + type: 'number', + width: 100, + minWidth: 80, + maxWidth: 120, + sortable: true, + filterable: false, + formatter: (_value: any, row: any) => { + // Get message count from our fetched data, just like in Dashboard component + const workflowId = row?.id; + const messageCount = workflowId ? workflowMessageCounts[workflowId] : undefined; + + console.log(`Messages formatter for ${workflowId}:`, { + workflowId, + messageCount, + hasInCache: workflowId in workflowMessageCounts, + allCounts: workflowMessageCounts + }); + + // Show the count if available, otherwise show loading indicator or dash + let displayValue; + if (messageCount !== undefined) { + displayValue = messageCount; + } else if (workflowId && workflows.some(w => w.id === workflowId)) { + // We're still loading this count + displayValue = '...'; + } else { + displayValue = '-'; + } + + return ( + + {displayValue} + + ); + } + } + ], [t, workflowMessageCounts, workflows]); + + // Handle workflow actions + const handleDeleteWorkflow = async (workflow: Workflow) => { + const workflowName = workflow.name || workflow.id; + if (window.confirm(t('workflows.delete.confirm').replace('{name}', workflowName))) { + const success = await deleteWorkflow(workflow.id); + if (success) { + refetch(); // Refresh the workflows list + } + } + }; + + // Handle single workflow deletion for bulk delete + const handleDeleteSingle = async (workflow: Workflow) => { + const workflowName = workflow.name || workflow.id; + if (window.confirm(t('workflows.delete.confirm', 'Are you sure you want to delete "{name}"?').replace('{name}', workflowName))) { + const success = await deleteWorkflow(workflow.id); + if (success) { + refetch(); // Refresh the workflows list + } else { + console.error('Delete failed for workflow:', workflow.id); + } + } + }; + + // Handle multiple workflow deletion + const handleDeleteMultiple = async (workflowsToDelete: Workflow[]) => { + const workflowCount = workflowsToDelete.length; + if (window.confirm(t('workflows.delete.confirmMultiple', 'Are you sure you want to delete {count} workflows?').replace('{count}', workflowCount.toString()))) { + // Start all delete operations simultaneously + const deletePromises = workflowsToDelete.map(async (workflow) => { + try { + const success = await deleteWorkflow(workflow.id); + return { workflowId: workflow.id, success }; + } catch (error) { + console.error('Failed to delete workflow:', workflow.id, error); + return { workflowId: workflow.id, success: false }; + } + }); + + // Wait for all deletions to complete + const results = await Promise.all(deletePromises); + + // Check if any deletions failed + const failedDeletions = results.filter(result => !result.success); + if (failedDeletions.length > 0) { + console.error('Some workflow deletions failed:', failedDeletions); + } + + // Refresh the workflow list regardless of individual failures + refetch(); + } + }; + + // Handle edit workflow + const handleEditWorkflow = (workflow: Workflow) => { + setEditingWorkflow(workflow); + setEditModalOpen(true); + }; + + // Handle save workflow + const handleSaveWorkflow = async (updatedWorkflow: Workflow) => { + if (!editingWorkflow) return; + + try { + // Call API to update workflow name + const result = await updateWorkflow(editingWorkflow.id, { + name: updatedWorkflow.name + }); + + if (result.success) { + // Close modal + setEditModalOpen(false); + setEditingWorkflow(null); + + // Refresh workflow list + await refetch(); + + // Notify other components that workflows have been updated + window.dispatchEvent(new CustomEvent('workflowUpdated', { + detail: { workflowId: editingWorkflow.id, newName: updatedWorkflow.name } + })); + } else { + console.error('Failed to update workflow:', result.error); + // TODO: Show error message to user + } + } catch (error) { + console.error('Failed to update workflow:', error); + // TODO: Show error message to user + } + }; + + // Handle cancel edit + const handleCancelEdit = () => { + setEditModalOpen(false); + setEditingWorkflow(null); + }; + + // Handle play workflow - navigate to dashboard with workflow ID + const handlePlayWorkflow = (workflow: Workflow) => { + // Navigate to dashboard with workflow ID as URL parameter + navigate(`/dashboard?workflowId=${workflow.id}`); + }; + + // Configure action buttons + const actions: WorkflowActionConfig[] = useMemo(() => [ + { + label: t('workflows.action.play'), + icon: (_row: Workflow) => { + return ; + }, + onClick: (row: Workflow) => { + handlePlayWorkflow(row); + } + }, + { + label: t('workflows.action.edit'), + icon: (_row: Workflow) => { + return ; + }, + onClick: (row: Workflow) => { + handleEditWorkflow(row); + } + }, + { + label: t('workflows.action.delete'), + icon: (_row: Workflow) => { + return ; + }, + onClick: (row: Workflow) => { + if (!deletingWorkflows.has(row.id)) { + handleDeleteWorkflow(row); + } + } + }, + ], [t, deletingWorkflows, handleDeleteWorkflow, handleEditWorkflow, handlePlayWorkflow]); + + return { + // Data + workflows, + loading, + error, + workflowMessageCounts, + editModalOpen, + editingWorkflow, + editWorkflowFields, + + // Actions + handleDeleteSingle, + handleDeleteMultiple, + handleEditWorkflow, + handleSaveWorkflow, + handleCancelEdit, + handlePlayWorkflow, + + // Refetch function + refetch, + + // Additional data for rendering + columns: columns as any, + actions: actions as any + }; +} diff --git a/src/components/Workflows/workflowsTypes.ts b/src/components/Workflows/workflowsTypes.ts new file mode 100644 index 0000000..4e85880 --- /dev/null +++ b/src/components/Workflows/workflowsTypes.ts @@ -0,0 +1,59 @@ +import React from 'react'; +import { Workflow } from '../../hooks/useWorkflows'; + +export interface WorkflowsTableProps { + className?: string; +} + +export interface WorkflowMessageCounts { + [workflowId: string]: number; +} + +export interface WorkflowEditState { + editModalOpen: boolean; + editingWorkflow: Workflow | null; +} + +export interface WorkflowsLogicReturn { + // Data + workflows: Workflow[]; + loading: boolean; + error: string | null; + workflowMessageCounts: WorkflowMessageCounts; + editModalOpen: boolean; + editingWorkflow: Workflow | null; + editWorkflowFields: any[]; + columns: any[]; + actions: any[]; + + // Actions + handleDeleteSingle: (workflow: Workflow) => Promise; + handleDeleteMultiple: (workflows: Workflow[]) => Promise; + handleEditWorkflow: (workflow: Workflow) => void; + handleSaveWorkflow: (updatedWorkflow: Workflow) => Promise; + handleCancelEdit: () => void; + handlePlayWorkflow: (workflow: Workflow) => void; + + // Refetch function + refetch: () => Promise; +} + +export interface WorkflowActionConfig { + label: string; + icon: (row: Workflow) => React.ReactElement; + onClick: (row: Workflow) => void; +} + +export interface WorkflowColumnConfig { + key: string; + label: string; + type: string; + width: number; + minWidth: number; + maxWidth: number; + sortable: boolean; + filterable: boolean; + searchable?: boolean; + filterOptions?: string[]; + formatter: (value: any, row?: any) => React.ReactElement | string; +} diff --git a/src/pages/Home/Dashboard.tsx b/src/pages/Home/Dashboard.tsx index 6dd5709..ac6a36a 100644 --- a/src/pages/Home/Dashboard.tsx +++ b/src/pages/Home/Dashboard.tsx @@ -19,7 +19,7 @@ function Dashboard() { // Get workflow ID from URL parameters const workflowIdFromUrl = searchParams.get('workflowId'); const [workflowState, workflowActions] = useWorkflowManager(workflowIdFromUrl); - const { workflows, loading: workflowsLoading, error: workflowsError } = useWorkflows(); + const { workflows, loading: workflowsLoading, error: workflowsError, refetch: refetchWorkflows } = useWorkflows(); const handleWorkflowSelect = useCallback((workflowId: string) => { workflowActions.loadWorkflow(workflowId); @@ -44,6 +44,18 @@ function Dashboard() { return () => document.removeEventListener('mousedown', handleClickOutside); }, []); + // Listen for workflow updates from other components (like WorkflowsTable) + useEffect(() => { + const handleWorkflowUpdated = (event: CustomEvent) => { + console.log('Dashboard received workflow update event:', event.detail); + // Refetch workflows to update the dropdown + refetchWorkflows(); + }; + + window.addEventListener('workflowUpdated', handleWorkflowUpdated as EventListener); + return () => window.removeEventListener('workflowUpdated', handleWorkflowUpdated as EventListener); + }, [refetchWorkflows]); + const getWorkflowDisplayName = (workflow: Workflow) => workflow.name || `${workflow.id.substring(0, 8)}...`;