diff --git a/frontend/src/templates/TablePageTemplate.tsx b/frontend/src/templates/TablePageTemplate.tsx index ac44dc3b..ad1fa375 100644 --- a/frontend/src/templates/TablePageTemplate.tsx +++ b/frontend/src/templates/TablePageTemplate.tsx @@ -28,12 +28,10 @@ import Input from '../components/form/input/InputField'; import SelectDropdown from '../components/form/SelectDropdown'; import { Dropdown } from '../components/ui/dropdown/Dropdown'; import { DropdownItem } from '../components/ui/dropdown/DropdownItem'; -import Alert from '../components/ui/alert/Alert'; import AlertModal from '../components/ui/alert/AlertModal'; import { ChevronDownIcon, MoreDotIcon, PlusIcon } from '../icons'; import { useHeaderMetrics } from '../context/HeaderMetricsContext'; import { useToast } from '../components/ui/toast/ToastContainer'; -import { pageNotifications } from '../config/pages/notifications.config'; import { getDeleteModalConfig } from '../config/pages/delete-modal.config'; import { getBulkActionModalConfig } from '../config/pages/bulk-action-modal.config'; import { getTableActionsConfig } from '../config/pages/table-actions.config'; @@ -102,7 +100,7 @@ interface TablePageTemplateProps { onCreate?: () => void; createLabel?: string; onCreateIcon?: ReactNode; // Icon for create button - onExport?: () => void; + onExportCSV?: () => void; // CSV export button handler onExportIcon?: ReactNode; // Icon for export button onImport?: () => void; onImportIcon?: ReactNode; // Icon for import button @@ -146,7 +144,7 @@ export default function TablePageTemplate({ subtitle, columns, data, - loading = false, + loading: _loading = false, // Unused - component uses showContent for loading state showContent = true, filters = [], filterValues = {}, @@ -157,7 +155,7 @@ export default function TablePageTemplate({ onCreate, createLabel = '+ Add', onCreateIcon, - onExport, + onExportCSV, onExportIcon, onImport, onImportIcon, @@ -172,17 +170,17 @@ export default function TablePageTemplate({ onBulkUpdateStatus, onBulkAction, onRowAction, + onExport, getItemDisplayName = (row: any) => row.name || row.keyword || row.title || String(row.id), className = '', }: TablePageTemplateProps) { const location = useLocation(); const [isBulkActionsDropdownOpen, setIsBulkActionsDropdownOpen] = useState(false); const [openRowActions, setOpenRowActions] = useState>(new Map()); - const rowActionButtonRefs = React.useRef>>(new Map()); + const rowActionButtonRefs = React.useRef>>(new Map()); const bulkActionsButtonRef = React.useRef(null); // Get notification config for current page - const notificationConfig = pageNotifications[location.pathname]; const deleteModalConfig = getDeleteModalConfig(location.pathname); const bulkActionModalConfig = getBulkActionModalConfig(location.pathname); const tableActionsConfig = getTableActionsConfig(location.pathname); @@ -303,7 +301,7 @@ export default function TablePageTemplate({ } else if (actionKey === 'delete' && onDelete && deleteModalConfig) { handleDeleteClick(row); } else if (actionKey === 'export' && onExport) { - onExport(row); + await onExport(row); } else if (onRowAction) { // For custom row actions, use onRowAction with full row object onRowAction(actionKey, row).catch((error: any) => { @@ -649,7 +647,7 @@ export default function TablePageTemplate({ 0} onClose={() => setIsBulkActionsDropdownOpen(false)} - anchorRef={bulkActionsButtonRef} + anchorRef={bulkActionsButtonRef as React.RefObject} placement="bottom-left" className="w-48 p-2" > @@ -683,12 +681,12 @@ export default function TablePageTemplate({ {/* Action Buttons - Right aligned */}
- {onExport && ( + {onExportCSV && ( @@ -747,16 +745,24 @@ export default function TablePageTemplate({ key={column.key} isHeader className={`px-5 py-3 font-medium text-gray-500 text-${column.align || 'start'} text-theme-xs dark:text-gray-400 ${column.sortable ? 'cursor-pointer hover:text-gray-700 dark:hover:text-gray-300' : ''} ${isLastColumn && rowActions.length > 0 ? 'pr-16' : ''}`} - onClick={() => column.sortable && handleSort(column)} > - {column.label} - {getSortIcon(column)} + {column.sortable ? ( +
handleSort(column)} className="flex items-center"> + {column.label} + {getSortIcon(column)} +
+ ) : ( + <> + {column.label} + {getSortIcon(column)} + + )} ); })} - + {!showContent ? ( // Loading Skeleton Rows - Always show 10 rows to match page size Array.from({ length: 10 }).map((_, index) => ( @@ -769,7 +775,6 @@ export default function TablePageTemplate({ )) ) : data.length === 0 ? null : ( data.map((row, index) => { - const rowId = row.id?.toString() || index.toString(); // Use consistent key for expandedRows (number or index) const rowKey = row.id || index; const isRowExpanded = expandedRows.has(rowKey); @@ -810,8 +815,7 @@ export default function TablePageTemplate({ return ( {selection && ( @@ -830,7 +834,8 @@ export default function TablePageTemplate({ // Get or create ref for this row's actions button if (isLastColumn && rowActions.length > 0) { if (!rowActionButtonRefs.current.has(rowId)) { - rowActionButtonRefs.current.set(rowId, React.createRef()); + const ref = React.createRef(); + rowActionButtonRefs.current.set(rowId, ref); } } @@ -838,7 +843,6 @@ export default function TablePageTemplate({ 0 ? 'relative pr-16' : ''}`} - style={isLastColumn && rowActions.length > 0 ? { position: 'relative', overflow: 'visible' } : undefined} >
@@ -849,14 +853,15 @@ export default function TablePageTemplate({ )}
{column.toggleable && hasToggleContent && ( - { - e.stopPropagation(); - handleToggle(!isRowExpanded, rowKey); - }} - hasContent={hasToggleContent} - /> +
e.stopPropagation()}> + { + handleToggle(!isRowExpanded, rowKey); + }} + hasContent={hasToggleContent} + /> +
)}
@@ -901,7 +906,8 @@ export default function TablePageTemplate({ } // Multiple actions - use dropdown - const buttonRef = rowActionButtonRefs.current.get(rowId)!; + const buttonRef = rowActionButtonRefs.current.get(rowId); + if (!buttonRef) return null; const isOpen = openRowActions.get(rowId) || false; return ( @@ -936,11 +942,11 @@ export default function TablePageTemplate({ return newMap; }); }} - anchorRef={buttonRef} + anchorRef={buttonRef as React.RefObject} placement="right" className="w-48 p-2" > - {rowActions.map((action, actionIndex) => { + {rowActions.map((action) => { const isEdit = action.key === 'edit'; const isDelete = action.key === 'delete'; const isExport = action.key === 'export'; @@ -949,21 +955,21 @@ export default function TablePageTemplate({ const getIconWithColor = () => { if (!action.icon) return null; const iconElement = action.icon as React.ReactElement; - const existingClassName = iconElement.props?.className || ""; + const existingClassName = (iconElement.props as any)?.className || ""; const baseSize = existingClassName.includes("w-") ? "" : "w-5 h-5 "; if (isEdit) { return React.cloneElement(iconElement, { className: `${baseSize}text-blue-light-500 ${existingClassName}`.trim() - }); + } as any); } else if (isDelete) { return React.cloneElement(iconElement, { className: `${baseSize}text-error-500 ${existingClassName}`.trim() - }); + } as any); } else if (isExport) { return React.cloneElement(iconElement, { className: `${baseSize}text-gray-600 dark:text-gray-400 ${existingClassName}`.trim() - }); + } as any); } return action.icon; }; @@ -1090,276 +1096,6 @@ export default function TablePageTemplate({
)} - {/* AI Request Logs Section - DEPRECATED: Now using backend console logging */} - {/* */} ); } - -// AI Request Logs Component - DEPRECATED: Now using backend console logging -// All AI logging is now handled by backend console logging (ConsoleStepTracker) -/* -function AIRequestLogsSection() { - const { logs, clearLogs } = useAIRequestLogsStore(); - const [isExpanded, setIsExpanded] = useState(false); - - if (logs.length === 0) { - return null; - } - - const formatTime = (date: Date) => { - return new Intl.DateTimeFormat('en-US', { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }).format(date); - }; - - const getStatusColor = (status: string) => { - switch (status) { - case 'success': - return 'text-green-600 dark:text-green-400'; - case 'error': - return 'text-red-600 dark:text-red-400'; - case 'pending': - return 'text-yellow-600 dark:text-yellow-400'; - default: - return 'text-gray-600 dark:text-gray-400'; - } - }; - - const getStatusBadge = (status: string) => { - switch (status) { - case 'success': - return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'; - case 'error': - return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'; - case 'pending': - return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400'; - default: - return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400'; - } - }; - - return ( -
- {isExpanded && ( -
-
- {/* Header inside panel */} -
-
-

- AI Debug Logs -

- - {logs.length} - -
-
- - -
-
- - {/* Scrollable content */} -
-
- {logs.map((log) => ( - -
- {/* Header */} -
-
- - {log.function} - - - {log.status.toUpperCase()} - - {log.duration && ( - - {log.duration}ms - - )} -
- - {formatTime(log.timestamp)} - -
- -
- {log.endpoint} -
- - {/* Request/Response Details (Collapsible) */} -
- - Request/Response Details - -
-
- Request -
-
-                              {JSON.stringify(log.request.body || log.request.params || {}, null, 2)}
-                            
-
-
- {log.response && ( -
- - Response ({log.response.status}) - {log.response.errorType && ( - - {log.response.errorType} - - )} - -
- {log.response.error ? ( -
- {log.response.error} -
- ) : log.response.data ? ( -
-                                  {JSON.stringify(log.response.data, null, 2)}
-                                
- ) : null} -
-
- )} -
-
-
- - {/* AI Debug Steps */} -
-

- AI Debug Steps ({log.requestSteps.length + log.responseSteps.length} total steps) -

-
- {(() => { - // Combine and sort all steps by stepNumber - const allSteps = [ - ...log.requestSteps.map((step, idx) => ({ ...step, type: 'request', originalIdx: idx })), - ...log.responseSteps.map((step, idx) => ({ ...step, type: 'response', originalIdx: idx })) - ].sort((a, b) => (a.stepNumber || 0) - (b.stepNumber || 0)); - - return allSteps.length > 0 ? ( - allSteps.map((step, idx) => ( -
-
- - {step.stepNumber}. - - - {step.status.toUpperCase()} - - - {step.type === 'request' ? 'Request' : 'Response'} - - {step.duration && ( - - {step.duration}ms - - )} -
-
- {step.stepName} -
-
- {step.functionName} -
- {step.message && ( -
- {step.message} -
- )} - {step.error && ( -
- Error: {step.error} -
- )} -
- )) - ) : ( -
- No steps logged yet -
- ); - })()} -
-
-
- ))} -
-
-
-
- )} - - {!isExpanded && logs.length > 0 && ( -
-
- {/* Header when collapsed */} -
-
-

- AI Debug Logs -

- - {logs.length} - -
-
- - -
-
-
-
- Click "Expand" to view {logs.length} AI request log{logs.length !== 1 ? 's' : ''} -
-
-
-
- )} -
- ); -} -*/