282 lines
10 KiB
TypeScript
282 lines
10 KiB
TypeScript
import { useCallback } from 'react';
|
|
import { GenericPageData } from '../../pageInterface';
|
|
import { FaCog, FaPlus } from 'react-icons/fa';
|
|
import { useAutomations, useAutomationOperations } from '../../../../hooks/useAutomations';
|
|
|
|
// Helper function to convert attribute definitions to column config
|
|
const attributesToColumns = (attributes: any[]) => {
|
|
return attributes
|
|
.filter(attr => {
|
|
// Exclude template and complex fields from table display
|
|
const attrNameLower = attr.name.toLowerCase();
|
|
const excludedColumns = ['template', 'executionlogs', 'execution_logs'];
|
|
return !excludedColumns.includes(attrNameLower);
|
|
})
|
|
.map(attr => {
|
|
const attrNameLower = attr.name.toLowerCase();
|
|
const isDateField = attr.type === 'date' ||
|
|
/(date|Date|timestamp|Timestamp|created|Created|updated|Updated|at|At)$/i.test(attr.name);
|
|
|
|
const column: any = {
|
|
key: attr.name,
|
|
label: attr.label || attr.name,
|
|
type: attr.type || 'string',
|
|
width: attr.width || 200,
|
|
minWidth: attr.minWidth || 100,
|
|
maxWidth: attr.maxWidth || 400,
|
|
sortable: attr.sortable !== false,
|
|
filterable: isDateField ? false : (attr.filterable !== false),
|
|
searchable: attr.searchable !== false,
|
|
filterOptions: attr.filterOptions
|
|
};
|
|
|
|
// Format schedule field
|
|
if (attrNameLower === 'schedule') {
|
|
column.formatter = (value: any) => {
|
|
if (!value) return '-';
|
|
const scheduleLabels: Record<string, string> = {
|
|
'0 */4 * * *': 'Every 4 hours',
|
|
'0 22 * * *': 'Daily at 22:00',
|
|
'0 10 * * 1': 'Weekly Monday 10:00'
|
|
};
|
|
return scheduleLabels[value] || value;
|
|
};
|
|
}
|
|
|
|
// Format active field as badge
|
|
if (attrNameLower === 'active') {
|
|
column.type = 'boolean';
|
|
}
|
|
|
|
// Format placeholders as count
|
|
if (attrNameLower === 'placeholders') {
|
|
column.formatter = (value: any) => {
|
|
if (!value) return '-';
|
|
let obj;
|
|
if (typeof value === 'string') {
|
|
try {
|
|
obj = JSON.parse(value);
|
|
} catch {
|
|
return '-';
|
|
}
|
|
} else if (typeof value === 'object') {
|
|
obj = value;
|
|
} else {
|
|
return '-';
|
|
}
|
|
const count = Object.keys(obj).length;
|
|
return `${count} placeholder${count !== 1 ? 's' : ''}`;
|
|
};
|
|
}
|
|
|
|
return column;
|
|
});
|
|
};
|
|
|
|
// Hook factory function for automations data
|
|
const createAutomationsHook = () => {
|
|
return () => {
|
|
const {
|
|
automations,
|
|
loading,
|
|
error,
|
|
refetch,
|
|
removeOptimistically,
|
|
updateOptimistically,
|
|
attributes,
|
|
permissions,
|
|
pagination,
|
|
fetchAutomationById,
|
|
generateEditFieldsFromAttributes,
|
|
generateCreateFieldsFromAttributes,
|
|
ensureAttributesLoaded
|
|
} = useAutomations();
|
|
const {
|
|
handleAutomationDelete,
|
|
handleAutomationCreate,
|
|
handleAutomationUpdate,
|
|
handleAutomationExecute,
|
|
handleAutomationToggleActive,
|
|
deletingAutomations,
|
|
creatingAutomation,
|
|
executingAutomations,
|
|
deleteError,
|
|
createError,
|
|
updateError
|
|
} = useAutomationOperations();
|
|
|
|
const generatedColumns = attributes && attributes.length > 0
|
|
? attributesToColumns(attributes)
|
|
: undefined;
|
|
|
|
// Handle single automation deletion
|
|
const handleDeleteSingle = useCallback(async (automation: any) => {
|
|
const success = await handleAutomationDelete(automation.id);
|
|
if (success) {
|
|
refetch();
|
|
}
|
|
}, [handleAutomationDelete, refetch]);
|
|
|
|
// Handle multiple automation deletion
|
|
const handleDeleteMultiple = useCallback(async (selectedAutomations: any[]) => {
|
|
const results = await Promise.all(
|
|
selectedAutomations.map(a => handleAutomationDelete(a.id))
|
|
);
|
|
const allSuccessful = results.every(result => result);
|
|
if (allSuccessful) {
|
|
refetch();
|
|
}
|
|
}, [handleAutomationDelete, refetch]);
|
|
|
|
// Wrapped create handler
|
|
const wrappedHandleAutomationCreate = useCallback(async (formData: any) => {
|
|
return await handleAutomationCreate(formData);
|
|
}, [handleAutomationCreate]);
|
|
|
|
return {
|
|
data: automations,
|
|
loading,
|
|
error,
|
|
refetch,
|
|
removeOptimistically,
|
|
updateOptimistically,
|
|
// Operations
|
|
handleDelete: handleAutomationDelete,
|
|
handleDeleteMultiple,
|
|
handleAutomationCreate: wrappedHandleAutomationCreate,
|
|
handleAutomationUpdate,
|
|
handleAutomationExecute,
|
|
handleAutomationToggleActive,
|
|
// FormGenerator specific handlers
|
|
onDelete: handleDeleteSingle,
|
|
onDeleteMultiple: handleDeleteMultiple,
|
|
// Loading states
|
|
deletingAutomations,
|
|
creatingAutomation,
|
|
executingAutomations,
|
|
// Error states
|
|
deleteError,
|
|
createError,
|
|
updateError,
|
|
// Attributes and permissions
|
|
attributes,
|
|
permissions,
|
|
pagination,
|
|
columns: generatedColumns,
|
|
// Functions for EditActionButton
|
|
fetchAutomationById,
|
|
generateEditFieldsFromAttributes,
|
|
generateCreateFieldsFromAttributes,
|
|
ensureAttributesLoaded
|
|
};
|
|
};
|
|
};
|
|
|
|
export const automationsPageData: GenericPageData = {
|
|
id: 'workflows-automations',
|
|
path: 'workflows/automations',
|
|
name: 'automations.title',
|
|
description: 'automations.description',
|
|
|
|
// Parent page - under 'workflows' group
|
|
parentPath: 'workflows',
|
|
|
|
// Visual
|
|
icon: FaCog,
|
|
title: 'automations.title',
|
|
subtitle: 'automations.subtitle',
|
|
|
|
// Header buttons
|
|
headerButtons: [
|
|
{
|
|
id: 'new-automation',
|
|
label: 'automations.new_button',
|
|
icon: FaPlus,
|
|
variant: 'primary',
|
|
formConfig: {
|
|
fields: [], // Fields will be generated dynamically from attributes
|
|
popupTitle: 'automations.modal.create.title',
|
|
popupSize: 'large',
|
|
createOperationName: 'handleAutomationCreate',
|
|
successMessage: 'automations.create.success',
|
|
errorMessage: 'automations.create.error'
|
|
}
|
|
}
|
|
],
|
|
|
|
// Content sections - using generic table approach
|
|
content: [
|
|
{
|
|
id: 'automations-table',
|
|
type: 'table',
|
|
tableConfig: {
|
|
hookFactory: createAutomationsHook,
|
|
// Columns are generated dynamically from attributes via hookData.columns
|
|
actionButtons: [
|
|
{
|
|
type: 'play',
|
|
title: 'automations.action.execute',
|
|
idField: 'id',
|
|
operationName: 'handleAutomationExecute',
|
|
loadingStateName: 'executingAutomations',
|
|
disabled: (hookData: any) => {
|
|
if (!hookData?.permissions) return { disabled: false };
|
|
const hasExecute = hookData.permissions.update !== 'n' && hookData.permissions.view;
|
|
return { disabled: !hasExecute, message: 'No permission to execute automations' };
|
|
}
|
|
},
|
|
{
|
|
type: 'edit',
|
|
title: 'automations.action.edit',
|
|
idField: 'id',
|
|
nameField: 'label',
|
|
operationName: 'handleAutomationUpdate',
|
|
loadingStateName: 'updatingAutomations',
|
|
fetchItemFunctionName: 'fetchAutomationById',
|
|
disabled: (hookData: any) => {
|
|
if (!hookData?.permissions) return { disabled: false };
|
|
const hasUpdate = hookData.permissions.update !== 'n' && hookData.permissions.view;
|
|
return { disabled: !hasUpdate, message: 'No permission to edit automations' };
|
|
}
|
|
},
|
|
{
|
|
type: 'delete',
|
|
title: 'automations.action.delete',
|
|
idField: 'id',
|
|
operationName: 'handleDelete',
|
|
loadingStateName: 'deletingAutomations',
|
|
disabled: (hookData: any) => {
|
|
if (!hookData?.permissions) return { disabled: false };
|
|
const hasDelete = hookData.permissions.delete !== 'n' && hookData.permissions.view;
|
|
return { disabled: !hasDelete, message: 'No permission to delete automations' };
|
|
}
|
|
}
|
|
],
|
|
searchable: true,
|
|
filterable: true,
|
|
sortable: true,
|
|
resizable: true,
|
|
pagination: true,
|
|
pageSize: 10,
|
|
className: 'automations-table'
|
|
}
|
|
}
|
|
],
|
|
|
|
// Page behavior
|
|
persistent: false,
|
|
preload: false,
|
|
preserveState: true,
|
|
moduleEnabled: true,
|
|
|
|
// Lifecycle hooks
|
|
onActivate: async () => {
|
|
if (import.meta.env.DEV) console.log('Automations activated');
|
|
},
|
|
onLoad: async () => {
|
|
if (import.meta.env.DEV) console.log('Automations loaded');
|
|
},
|
|
onUnload: async () => {
|
|
if (import.meta.env.DEV) console.log('Automations unloaded');
|
|
}
|
|
};
|