173 lines
5.8 KiB
JavaScript
173 lines
5.8 KiB
JavaScript
import { useState, useEffect } from 'react'
|
|
import { useUser } from '../context/UserContext'
|
|
import { getActivityLog } from '../api'
|
|
|
|
const actionLabels = {
|
|
server_start: 'Server gestartet',
|
|
server_stop: 'Server gestoppt',
|
|
server_restart: 'Server neugestartet',
|
|
rcon_command: 'RCON Befehl',
|
|
autoshutdown_config: 'Auto-Shutdown geändert',
|
|
zomboid_config: 'Config geändert',
|
|
factorio_world_create: 'Welt erstellt',
|
|
factorio_world_delete: 'Welt gelöscht'
|
|
}
|
|
|
|
const actionIcons = {
|
|
server_start: '▶️',
|
|
server_stop: '⏹️',
|
|
server_restart: '🔄',
|
|
rcon_command: '💻',
|
|
autoshutdown_config: '⏱️',
|
|
zomboid_config: '📝',
|
|
factorio_world_create: '🌍',
|
|
factorio_world_delete: '🗑️'
|
|
}
|
|
|
|
const serverLabels = {
|
|
minecraft: 'Minecraft',
|
|
factorio: 'Factorio',
|
|
zomboid: 'Project Zomboid',
|
|
vrising: 'V Rising'
|
|
}
|
|
|
|
function getAvatarUrl(discordId, avatar) {
|
|
if (!discordId || !avatar) return null
|
|
return `https://cdn.discordapp.com/avatars/${discordId}/${avatar}.png?size=32`
|
|
}
|
|
|
|
function getDiscordProfileUrl(discordId) {
|
|
return `https://discord.com/users/${discordId}`
|
|
}
|
|
|
|
export default function ActivityLog({ onClose }) {
|
|
const { token } = useUser()
|
|
const [logs, setLogs] = useState([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState('')
|
|
|
|
useEffect(() => {
|
|
const fetchLogs = async () => {
|
|
try {
|
|
const data = await getActivityLog(token, 100)
|
|
setLogs(data)
|
|
setError('')
|
|
} catch (err) {
|
|
setError('Fehler beim Laden des Activity Logs')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
fetchLogs()
|
|
}, [token])
|
|
|
|
const formatDate = (dateStr) => {
|
|
const date = new Date(dateStr + 'Z')
|
|
const now = new Date()
|
|
const diff = now - date
|
|
|
|
if (diff < 60000) return 'Gerade eben'
|
|
if (diff < 3600000) return Math.floor(diff / 60000) + ' Min'
|
|
if (diff < 86400000) return Math.floor(diff / 3600000) + ' Std'
|
|
|
|
return date.toLocaleDateString('de-DE', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className="modal-backdrop fade-in" onClick={onClose}>
|
|
<div className="modal fade-in-scale" style={{ maxWidth: '42rem' }} onClick={(e) => e.stopPropagation()}>
|
|
<div className="modal-header">
|
|
<h2 className="modal-title">Activity Log</h2>
|
|
<button onClick={onClose} className="btn btn-ghost">
|
|
Schließen
|
|
</button>
|
|
</div>
|
|
|
|
<div className="modal-body" style={{ maxHeight: '60vh', overflowY: 'auto' }}>
|
|
{error && (
|
|
<div className="alert alert-error mb-4">{error}</div>
|
|
)}
|
|
|
|
{loading ? (
|
|
<div className="text-center py-4 text-neutral-400">Laden...</div>
|
|
) : logs.length === 0 ? (
|
|
<div className="text-center py-4 text-neutral-500">Noch keine Aktivitäten</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{logs.map((log) => {
|
|
const avatarUrl = getAvatarUrl(log.discord_id, log.avatar)
|
|
const profileUrl = log.discord_id ? getDiscordProfileUrl(log.discord_id) : null
|
|
|
|
return (
|
|
<div key={log.id} className="card p-3 flex items-start gap-3">
|
|
{/* Avatar or Action Icon */}
|
|
{avatarUrl ? (
|
|
<a
|
|
href={profileUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex-shrink-0"
|
|
>
|
|
<img
|
|
src={avatarUrl}
|
|
alt=""
|
|
className="w-8 h-8 rounded-full hover:ring-2 hover:ring-blue-500 transition-all"
|
|
/>
|
|
</a>
|
|
) : (
|
|
<div className="w-8 h-8 rounded-full bg-neutral-700 flex items-center justify-center flex-shrink-0">
|
|
<span className="text-neutral-400 text-xs">
|
|
{log.username?.charAt(0)?.toUpperCase()}
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center gap-2 flex-wrap">
|
|
{profileUrl ? (
|
|
<a
|
|
href={profileUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-white font-medium hover:text-blue-400 transition-colors"
|
|
>
|
|
{log.username}
|
|
</a>
|
|
) : (
|
|
<span className="text-white font-medium">{log.username}</span>
|
|
)}
|
|
<span className="text-neutral-500">
|
|
{actionIcons[log.action] || '📋'} {actionLabels[log.action] || log.action}
|
|
</span>
|
|
{log.target && (
|
|
<span className="text-blue-400">
|
|
{serverLabels[log.target] || log.target}
|
|
</span>
|
|
)}
|
|
</div>
|
|
{log.details && (
|
|
<div className="text-sm text-neutral-500 mt-1 truncate font-mono">
|
|
{log.details}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="text-xs text-neutral-500 whitespace-nowrap">
|
|
{formatDate(log.created_at)}
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|