diff --git a/app/src/components/admin/ControllerPage.js b/app/src/components/admin/ControllerPage.js
index 5277fa55..a97f92d2 100644
--- a/app/src/components/admin/ControllerPage.js
+++ b/app/src/components/admin/ControllerPage.js
@@ -47,7 +47,8 @@ const linesOptions = [
{key: 5000, text: '5000 lines', value: 5000},
];
-function ServiceRow({service, onAction, dockerized}) {
+// Custom hook for service row state and handlers
+function useServiceRow(service, onAction) {
const [loading, setLoading] = React.useState(false);
const [logsOpen, setLogsOpen] = React.useState(false);
const [logs, setLogs] = React.useState('');
@@ -145,7 +146,6 @@ function ServiceRow({service, onAction, dockerized}) {
};
const isRunning = service.status === 'running';
- const isStopped = service.status === 'stopped';
const statusColor = statusColors[service.status] || 'grey';
// Build view URL if service is viewable
@@ -157,21 +157,154 @@ function ServiceRow({service, onAction, dockerized}) {
viewUrl = `${protocol}://${host}:${service.port}${path}`;
}
+ return {
+ loading, logsOpen, setLogsOpen, logs, logsLoading, linesCount, countdown, logsRef,
+ handleAction, fetchLogs, handleViewLogs, handleLinesChange, handleDownloadLogs,
+ isRunning, statusColor, viewUrl
+ };
+}
+
+// Logs modal component (shared between mobile and desktop)
+function ServiceLogsModal({service, logsOpen, setLogsOpen, logs, logsLoading, logsRef, linesCount, countdown, handleViewLogs, handleLinesChange, handleDownloadLogs, fetchLogs}) {
+ return (
+ setLogsOpen(false)}
+ onOpen={handleViewLogs}
+ trigger={}
+ size='fullscreen'
+ >
+ Logs: {service.name}
+
+ {logsLoading ? (
+
+ ) : (
+
+ {logs}
+
+ )}
+
+
+ {countdown}s
+
+
+
+ );
+}
+
+// Mobile service row - simplified layout
+function MobileServiceRow({service, onAction}) {
+ const {
+ loading, logsOpen, setLogsOpen, logs, logsLoading, linesCount, countdown, logsRef,
+ handleAction, fetchLogs, handleViewLogs, handleLinesChange, handleDownloadLogs,
+ isRunning, statusColor, viewUrl
+ } = useServiceRow(service, onAction);
+
+ return (
+
+
+ {service.name}
+ {service.description && {service.description}
}
+
+ {service.status}
+
+
+
+ {isRunning ? (
+ handleAction('Stop', stopService)}
+ />
+ ) : (
+ handleAction('Start', startService)}
+ />
+ )}
+ handleAction('Restart', restartService)}
+ />
+
+ {viewUrl && (
+
+ )}
+
+
+ );
+}
+
+// Desktop service row - full layout with all columns
+function DesktopServiceRow({service, onAction, dockerized}) {
+ const {
+ loading, logsOpen, setLogsOpen, logs, logsLoading, linesCount, countdown, logsRef,
+ handleAction, fetchLogs, handleViewLogs, handleLinesChange, handleDownloadLogs,
+ isRunning, statusColor, viewUrl
+ } = useServiceRow(service, onAction);
+
return (
{service.name}
{service.description && {service.description}
}
-
- {service.port || '-'}
-
-
-
-
- {service.status}
-
-
+ {service.port || '-'}
+
+
+ {service.status}
+
{isRunning ? (
handleAction('Restart', restartService)}
/>
- setLogsOpen(false)}
- onOpen={handleViewLogs}
- trigger={}
- size='fullscreen'
- >
- Logs: {service.name}
-
- {logsLoading ? (
-
- ) : (
-
- {logs}
-
- )}
-
-
- {countdown}s
-
- fetchLogs(linesCount)} color='blue' icon='refresh' content='Refresh'/>
-
- setLogsOpen(false)}>Close
-
-
+
{viewUrl && (
{!dockerized && (
-
-
- {
- if (checked) {
- await handleAction('Enable', enableService);
- } else {
- await handleAction('Disable', disableService);
- }
- }}
- label={service.enabled ? 'Enabled' : 'Disabled'}
- />
-
-
+
+ {
+ if (checked) {
+ await handleAction('Enable', enableService);
+ } else {
+ await handleAction('Disable', disableService);
+ }
+ }}
+ label={service.enabled ? 'Enabled' : 'Disabled'}
+ />
+
)}
);
@@ -344,75 +448,77 @@ function ServicesSection() {
;
}
+ const restartButton = (colSpan) => (
+
+
+
+
+
+ {restarting ? 'Restarting...' : 'Restart All Services'}
+
+
+
+
+ );
+
return (
-
-
-
- Service
-
+ {/* Mobile table - 2 columns */}
+
+
+
+
+ Service
+ Actions
+
+
+
+ {services.map(service => (
+
+ ))}
+
+ {restartButton(2)}
+
+
+ {/* Desktop table - full columns */}
+
+
+
+
+ Service
Port
-
-
Status
-
- Actions
- {!dockerized && (
-
- Boot
-
- )}
-
-
-
- {services.map(service => (
-
- ))}
-
-
-
- {/* Mobile: 2 columns (Service, Actions) */}
-
-
-
-
- {restarting ? 'Restarting...' : 'Restart All Services'}
-
-
-
- {/* Tablet+: 4 columns (dockerized) or 5 columns (non-dockerized) */}
-
-
-
-
- {restarting ? 'Restarting...' : 'Restart All Services'}
-
-
-
-
-
-
+ Actions
+ {!dockerized && Boot}
+
+
+
+ {services.map(service => (
+
+ ))}
+
+ {restartButton(dockerized ? 4 : 5)}
+
+
{services.length === 0 && (
No services found. Services may not be available in this environment.
)}