1import React from "react";
2import { apiFetch } from "../../lib/api";
4export default function TabLogs({ agentId }) {
5 const [activityLogs, setActivityLogs] = React.useState([]);
6 const [agentLogs, setAgentLogs] = React.useState([]);
7 const [loading, setLoading] = React.useState(true);
8 const [activeTab, setActiveTab] = React.useState('activity'); // 'activity' or 'agent'
10 React.useEffect(() => {
15 apiFetch(`/agent/${agentId}/activity-logs`).catch(() => []),
16 apiFetch(`/agent/${agentId}/agent-logs`).catch(() => [])
17 ]).then(([activity, agent]) => {
18 setActivityLogs(activity);
20 }).finally(() => setLoading(false));
25 <div className="tab-container">
27 <div className="card">Loading logs...</div>
32 const safeActivityLogs = Array.isArray(activityLogs) ? activityLogs : [];
33 const safeAgentLogs = Array.isArray(agentLogs) ? agentLogs : [];
36 <div className="tab-container">
40 <div className="card" style={{ marginBottom: '16px', padding: '8px', display: 'flex', gap: '8px' }}>
42 className={`btn ${activeTab === 'activity' ? 'btn-primary' : 'btn-secondary'}`}
43 onClick={() => setActiveTab('activity')}
49 className={`btn ${activeTab === 'agent' ? 'btn-primary' : 'btn-secondary'}`}
50 onClick={() => setActiveTab('agent')}
57 {/* Activity Logs Tab */}
58 {activeTab === 'activity' && (
59 <div className="card">
60 <h3 style={{ marginTop: 0 }}>Activity Logs</h3>
61 <p style={{ color: '#888', fontSize: '14px', marginBottom: '16px' }}>
62 User actions: Remote sessions, script executions, file transfers, and system changes
64 <div className="log-box" style={{ maxHeight: '600px', overflow: 'auto' }}>
65 {safeActivityLogs.length === 0 ? (
66 <div style={{ padding: '20px', textAlign: 'center', color: '#888' }}>
67 No activity logs available yet
70 safeActivityLogs.map((log, i) => (
71 <div key={i} className="log-entry" style={{
73 borderBottom: '1px solid #eee',
76 <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>
77 <strong style={{ color: '#333' }}>{log.action || 'Unknown Action'}</strong>
78 <span style={{ color: '#888', fontSize: '12px' }}>
79 {log.timestamp ? new Date(log.timestamp).toLocaleString() : ''}
82 <div style={{ color: '#666', fontSize: '13px' }}>
83 {log.details || log.message || 'No details'}
86 <div style={{ color: '#888', fontSize: '12px', marginTop: '4px' }}>
97 {/* Agent Logs Tab */}
98 {activeTab === 'agent' && (
99 <div className="card">
100 <h3 style={{ marginTop: 0 }}>Agent Logs</h3>
101 <p style={{ color: '#888', fontSize: '14px', marginBottom: '16px' }}>
102 Agent internal logs: Heartbeats, errors, updates, and system events
104 <div className="log-box" style={{ maxHeight: '600px', overflow: 'auto', fontFamily: 'monospace' }}>
105 {safeAgentLogs.length === 0 ? (
106 <div style={{ padding: '20px', textAlign: 'center', color: '#888' }}>
107 No agent logs available yet
110 safeAgentLogs.map((log, i) => (
111 <div key={i} className="log-entry" style={{
113 borderBottom: '1px solid #eee',
115 whiteSpace: 'pre-wrap'
117 <span style={{ color: '#888' }}>
118 {log.timestamp ? new Date(log.timestamp).toLocaleString() : ''}
122 color: log.level === 'error' ? '#dc3545' :
123 log.level === 'warn' ? '#ffc107' : '#666'
125 [{log.level?.toUpperCase() || 'INFO'}]
128 {log.message || log.details || 'No message'}