Add server control buttons to dashboard with confirmation dialogs
All checks were successful
Deploy GSM / deploy (push) Successful in 24s
All checks were successful
Deploy GSM / deploy (push) Successful in 24s
- Add ConfirmModal component for stop/restart confirmations - Add start/stop/restart buttons to ServerCard (moderator/admin only) - Add confirmation dialogs to ServerDetail for stop/restart actions - Add btn-sm CSS class for smaller buttons Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
23
gsm-frontend/src/components/ConfirmModal.jsx
Normal file
23
gsm-frontend/src/components/ConfirmModal.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
export default function ConfirmModal({ title, message, confirmText, cancelText, onConfirm, onCancel, variant = 'danger' }) {
|
||||
const confirmBtnClass = variant === 'danger' ? 'btn btn-destructive' : 'btn btn-primary'
|
||||
|
||||
return (
|
||||
<div className="modal-backdrop fade-in" onClick={onCancel}>
|
||||
<div className="modal fade-in-scale max-w-md" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="p-6">
|
||||
<h2 className="text-lg font-semibold text-white mb-2">{title}</h2>
|
||||
<p className="text-neutral-400 mb-6">{message}</p>
|
||||
|
||||
<div className="flex gap-3 justify-end">
|
||||
<button onClick={onCancel} className="btn btn-ghost">
|
||||
{cancelText || 'Abbrechen'}
|
||||
</button>
|
||||
<button onClick={onConfirm} className={confirmBtnClass}>
|
||||
{confirmText || 'Bestätigen'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { serverAction } from '../api'
|
||||
import ConfirmModal from './ConfirmModal'
|
||||
|
||||
const serverInfo = {
|
||||
minecraft: {
|
||||
address: 'minecraft.zeasy.dev',
|
||||
@@ -64,8 +68,10 @@ const getServerInfo = (serverName) => {
|
||||
return null
|
||||
}
|
||||
|
||||
export default function ServerCard({ server, onClick, isAuthenticated, isGuest, displaySettings }) {
|
||||
export default function ServerCard({ server, onClick, isAuthenticated, isGuest, displaySettings, isModerator, token, onServerAction }) {
|
||||
const defaultInfo = getServerInfo(server.name)
|
||||
const [confirmAction, setConfirmAction] = useState(null)
|
||||
const [actionLoading, setActionLoading] = useState(false)
|
||||
|
||||
// Merge default info with database display settings (database takes priority)
|
||||
const info = defaultInfo ? {
|
||||
@@ -119,6 +125,30 @@ export default function ServerCard({ server, onClick, isAuthenticated, isGuest,
|
||||
|
||||
const isClickable = !isUnreachable && !isGuest && onClick
|
||||
|
||||
const handleAction = async (action) => {
|
||||
setActionLoading(true)
|
||||
try {
|
||||
await serverAction(token, server.id, action)
|
||||
if (onServerAction) onServerAction()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
} finally {
|
||||
setActionLoading(false)
|
||||
setConfirmAction(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleActionClick = (action, e) => {
|
||||
e.stopPropagation()
|
||||
if (action === 'start') {
|
||||
handleAction(action)
|
||||
} else {
|
||||
setConfirmAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
const isActionDisabled = actionLoading || server.status === 'starting' || server.status === 'stopping'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={isUnreachable ? "card p-5 opacity-50 cursor-not-allowed" : (isClickable ? "card card-clickable p-5" : "card p-5")}
|
||||
@@ -240,6 +270,55 @@ export default function ServerCard({ server, onClick, isAuthenticated, isGuest,
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Server Controls - only for moderators */}
|
||||
{isModerator && !isUnreachable && (
|
||||
<div className="mt-3 pt-3 border-t border-neutral-800">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(server.status === 'online' || (server.running && server.status !== 'starting' && server.status !== 'stopping')) ? (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => handleActionClick('stop', e)}
|
||||
disabled={isActionDisabled}
|
||||
className="btn btn-destructive btn-sm"
|
||||
>
|
||||
{server.status === 'stopping' ? 'Stoppt...' : 'Stoppen'}
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => handleActionClick('restart', e)}
|
||||
disabled={isActionDisabled}
|
||||
className="btn btn-secondary btn-sm"
|
||||
>
|
||||
{server.status === 'starting' ? 'Startet...' : 'Neustarten'}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={(e) => handleActionClick('start', e)}
|
||||
disabled={isActionDisabled}
|
||||
className="btn btn-primary btn-sm"
|
||||
>
|
||||
{server.status === 'starting' ? 'Startet...' : 'Starten'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Confirmation Modal */}
|
||||
{confirmAction && (
|
||||
<ConfirmModal
|
||||
title={confirmAction === 'stop' ? 'Server stoppen?' : 'Server neustarten?'}
|
||||
message={confirmAction === 'stop'
|
||||
? `Bist du sicher, dass du ${server.name} stoppen möchtest?`
|
||||
: `Bist du sicher, dass du ${server.name} neustarten möchtest?`
|
||||
}
|
||||
confirmText={confirmAction === 'stop' ? 'Stoppen' : 'Neustarten'}
|
||||
variant={confirmAction === 'stop' ? 'danger' : 'primary'}
|
||||
onConfirm={() => handleAction(confirmAction)}
|
||||
onCancel={() => setConfirmAction(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user