fix: instance detail crash + add play/stop/redeploy/remove actions
Fixes: 1. useFetch: add credentials:'same-origin' — cookie wasn't being sent with API calls, causing 401 → React error boundary → 'Etwas ist schiefgelaufen' 2. Instance detail Sheet now loads correctly (cookie sent with request) New instance actions (UI + API): - Redeploy: triggers Dokploy application.redeploy (rebuilds from latest commit) - Stop: triggers Dokploy application.stop (pauses container) - Start: triggers Dokploy application.start (resumes container) - Remove: existing delete (SlideCommit confirmation) Dokploy deploy fix: - deployApplication now calls markRunning before deploy - This ensures Dokploy creates a deployment job ActionsRow redesigned: - demo_active/active: [Redeploy] [Stop] [Demo +7d] [Remove] - grace: [Start] [Remove] - failed: [Retry] [Remove] - provisioning/deleting/deleted: no actions
This commit is contained in:
1 parent
89adb63f5e
commit
590310ca95
4 files changed
+114
-44
No files matched your search
@@ -12,6 +12,8 @@ import {
|
||||
ExternalLink,
|
||||
Ban,
|
||||
Play,
|
||||
Pause,
|
||||
Square,
|
||||
RotateCcw,
|
||||
CalendarPlus,
|
||||
Trash2,
|
||||
@@ -20,6 +22,7 @@ import {
|
||||
XCircle,
|
||||
CircleDashed,
|
||||
Network,
|
||||
RefreshCw,
|
||||
} from 'lucide-react'
|
||||
import { useFetch } from '@/hooks/use-fetch'
|
||||
import { Card } from '@/components/ui/card'
|
||||
@@ -554,7 +557,7 @@ function InstanceDetailSheet({
|
||||
}, [open])
|
||||
|
||||
const handleAction = async (
|
||||
action: 'suspend' | 'reactivate' | 'extend' | 'retry',
|
||||
action: 'suspend' | 'reactivate' | 'extend' | 'retry' | 'redeploy' | 'stop' | 'start',
|
||||
label: string,
|
||||
extendDays?: number
|
||||
) => {
|
||||
@@ -904,9 +907,12 @@ function InstanceDetailSheet({
|
||||
status={status}
|
||||
busyAction={busyAction}
|
||||
onSuspend={() => handleAction('suspend', 'Suspendieren')}
|
||||
onReactivate={() => handleAction('reactivate', 'Reaktivieren')}
|
||||
onReactivate={() => handleAction('reactivate', 'Starten')}
|
||||
onExtend={() => handleAction('extend', 'Demo verlängern', 7)}
|
||||
onRetry={() => handleAction('retry', 'Provisioning wiederholen')}
|
||||
onRedeploy={() => handleAction('redeploy', 'Redeploy')}
|
||||
onStop={() => handleAction('stop', 'Stop')}
|
||||
onStart={() => handleAction('start', 'Start')}
|
||||
onDelete={() => setConfirmingDelete(true)}
|
||||
/>
|
||||
{['demo_active', 'active'].includes(status) && (
|
||||
@@ -1042,6 +1048,9 @@ function ActionsRow({
|
||||
onReactivate,
|
||||
onExtend,
|
||||
onRetry,
|
||||
onRedeploy,
|
||||
onStop,
|
||||
onStart,
|
||||
onDelete,
|
||||
}: {
|
||||
status: string
|
||||
@@ -1050,6 +1059,9 @@ function ActionsRow({
|
||||
onReactivate: () => void
|
||||
onExtend: () => void
|
||||
onRetry: () => void
|
||||
onRedeploy: () => void
|
||||
onStop: () => void
|
||||
onStart: () => void
|
||||
onDelete: () => void
|
||||
}) {
|
||||
const busy = busyAction !== null
|
||||
@@ -1079,43 +1091,30 @@ function ActionsRow({
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'demo_active') {
|
||||
if (status === 'demo_active' || status === 'active') {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
|
||||
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
||||
{btn({
|
||||
onClick: onRedeploy,
|
||||
label: 'Redeploy',
|
||||
icon: RefreshCw,
|
||||
loading: busyAction === 'redeploy',
|
||||
})}
|
||||
{btn({
|
||||
onClick: onStop,
|
||||
label: 'Stop',
|
||||
icon: Square,
|
||||
loading: busyAction === 'stop',
|
||||
})}
|
||||
{status === 'demo_active' && btn({
|
||||
onClick: onExtend,
|
||||
label: 'Demo +7 d',
|
||||
label: 'Demo +7d',
|
||||
icon: CalendarPlus,
|
||||
loading: busyAction === 'extend',
|
||||
})}
|
||||
{btn({
|
||||
onClick: onSuspend,
|
||||
label: 'Suspendieren',
|
||||
icon: Ban,
|
||||
loading: busyAction === 'suspend',
|
||||
})}
|
||||
{btn({
|
||||
onClick: onDelete,
|
||||
label: 'Löschen',
|
||||
icon: Trash2,
|
||||
variant: 'destructive',
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'active') {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{btn({
|
||||
onClick: onSuspend,
|
||||
label: 'Suspendieren',
|
||||
icon: Ban,
|
||||
loading: busyAction === 'suspend',
|
||||
})}
|
||||
{btn({
|
||||
onClick: onDelete,
|
||||
label: 'Löschen',
|
||||
label: 'Remove',
|
||||
icon: Trash2,
|
||||
variant: 'destructive',
|
||||
})}
|
||||
@@ -1125,16 +1124,16 @@ function ActionsRow({
|
||||
|
||||
if (status === 'grace') {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{btn({
|
||||
onClick: onReactivate,
|
||||
label: 'Reaktivieren',
|
||||
label: 'Start',
|
||||
icon: Play,
|
||||
loading: busyAction === 'reactivate',
|
||||
})}
|
||||
{btn({
|
||||
onClick: onDelete,
|
||||
label: 'Sofort löschen',
|
||||
label: 'Remove',
|
||||
icon: Trash2,
|
||||
variant: 'destructive',
|
||||
})}
|
||||
@@ -1144,23 +1143,24 @@ function ActionsRow({
|
||||
|
||||
if (status === 'failed') {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{btn({
|
||||
onClick: onRetry,
|
||||
label: 'Provisioning wiederholen',
|
||||
label: 'Retry',
|
||||
icon: RotateCcw,
|
||||
loading: busyAction === 'retry',
|
||||
})}
|
||||
{btn({
|
||||
onClick: onDelete,
|
||||
label: 'Remove',
|
||||
icon: Trash2,
|
||||
variant: 'destructive',
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// provisioning / deleting / deleted / requested → no actions
|
||||
return (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Für diesen Status sind keine Aktionen verfügbar.
|
||||
</p>
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user