Skip to content

Commit 962e287

Browse files
feat: add loading modal for container operations
- Add LoadingModal component with spinning circle animation - Show loading modal during start/stop/destroy container operations - Display current action being performed (e.g., 'Starting container 101...') - Close loading modal when operation completes (success or error) - Maintains consistent modal styling with existing components Fixes user experience by providing clear visual feedback during background operations instead of silent processing.
1 parent 3459fe3 commit 962e287

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

src/app/_components/InstalledScriptsTab.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Button } from './ui/button';
88
import { ScriptInstallationCard } from './ScriptInstallationCard';
99
import { ConfirmationModal } from './ConfirmationModal';
1010
import { ErrorModal } from './ErrorModal';
11+
import { LoadingModal } from './LoadingModal';
1112
import { getContrastColor } from '../../lib/colorUtils';
1213
import {
1314
DropdownMenu,
@@ -84,6 +85,12 @@ export function InstalledScriptsTab() {
8485
type?: 'error' | 'success';
8586
} | null>(null);
8687

88+
// Loading modal state
89+
const [loadingModal, setLoadingModal] = useState<{
90+
isOpen: boolean;
91+
action: string;
92+
} | null>(null);
93+
8794
// Fetch installed scripts
8895
const { data: scriptsData, refetch: refetchScripts, isLoading } = api.installedScripts.getAllInstalledScripts.useQuery();
8996
const { data: statsData } = api.installedScripts.getInstallationStats.useQuery();
@@ -247,6 +254,7 @@ export function InstalledScriptsTab() {
247254

248255
const controlContainerMutation = api.installedScripts.controlContainer.useMutation({
249256
onSuccess: (data, variables) => {
257+
setLoadingModal(null);
250258
setControllingScriptId(null);
251259

252260
if (data.success) {
@@ -287,6 +295,7 @@ export function InstalledScriptsTab() {
287295
},
288296
onError: (error) => {
289297
console.error('Container control error:', error);
298+
setLoadingModal(null);
290299
setControllingScriptId(null);
291300

292301
// Show detailed error message
@@ -302,6 +311,7 @@ export function InstalledScriptsTab() {
302311

303312
const destroyContainerMutation = api.installedScripts.destroyContainer.useMutation({
304313
onSuccess: (data) => {
314+
setLoadingModal(null);
305315
setControllingScriptId(null);
306316

307317
if (data.success) {
@@ -326,6 +336,7 @@ export function InstalledScriptsTab() {
326336
},
327337
onError: (error) => {
328338
console.error('Container destroy error:', error);
339+
setLoadingModal(null);
329340
setControllingScriptId(null);
330341

331342
// Show detailed error message
@@ -515,6 +526,7 @@ export function InstalledScriptsTab() {
515526
message: `Are you sure you want to ${action} container ${script.container_id} (${script.script_name})?`,
516527
onConfirm: () => {
517528
setControllingScriptId(script.id);
529+
setLoadingModal({ isOpen: true, action: `${action === 'start' ? 'Starting' : 'Stopping'} container ${script.container_id}...` });
518530
void controlContainerMutation.mutate({ id: script.id, action });
519531
setConfirmationModal(null);
520532
}
@@ -535,6 +547,7 @@ export function InstalledScriptsTab() {
535547
confirmText: script.container_id,
536548
onConfirm: () => {
537549
setControllingScriptId(script.id);
550+
setLoadingModal({ isOpen: true, action: `Destroying container ${script.container_id}...` });
538551
void destroyContainerMutation.mutate({ id: script.id });
539552
setConfirmationModal(null);
540553
}
@@ -1566,6 +1579,14 @@ export function InstalledScriptsTab() {
15661579
type={errorModal.type ?? 'error'}
15671580
/>
15681581
)}
1582+
1583+
{/* Loading Modal */}
1584+
{loadingModal && (
1585+
<LoadingModal
1586+
isOpen={loadingModal.isOpen}
1587+
action={loadingModal.action}
1588+
/>
1589+
)}
15691590
</div>
15701591
);
15711592
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use client';
2+
3+
import { Loader2 } from 'lucide-react';
4+
5+
interface LoadingModalProps {
6+
isOpen: boolean;
7+
action: string;
8+
}
9+
10+
export function LoadingModal({ isOpen, action }: LoadingModalProps) {
11+
if (!isOpen) return null;
12+
13+
return (
14+
<div className="fixed inset-0 backdrop-blur-sm bg-black/50 flex items-center justify-center z-50 p-4">
15+
<div className="bg-card rounded-lg shadow-xl max-w-md w-full border border-border p-8">
16+
<div className="flex flex-col items-center space-y-4">
17+
<div className="relative">
18+
<Loader2 className="h-12 w-12 animate-spin text-primary" />
19+
<div className="absolute inset-0 rounded-full border-2 border-primary/20 animate-pulse"></div>
20+
</div>
21+
<div className="text-center">
22+
<h3 className="text-lg font-semibold text-card-foreground mb-2">
23+
Processing
24+
</h3>
25+
<p className="text-sm text-muted-foreground">
26+
{action}
27+
</p>
28+
<p className="text-xs text-muted-foreground mt-2">
29+
Please wait...
30+
</p>
31+
</div>
32+
</div>
33+
</div>
34+
</div>
35+
);
36+
}
37+

0 commit comments

Comments
 (0)