Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/app/_components/InstalledScriptsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Button } from './ui/button';
import { ScriptInstallationCard } from './ScriptInstallationCard';
import { ConfirmationModal } from './ConfirmationModal';
import { ErrorModal } from './ErrorModal';
import { LoadingModal } from './LoadingModal';
import { getContrastColor } from '../../lib/colorUtils';
import {
DropdownMenu,
Expand Down Expand Up @@ -84,6 +85,12 @@ export function InstalledScriptsTab() {
type?: 'error' | 'success';
} | null>(null);

// Loading modal state
const [loadingModal, setLoadingModal] = useState<{
isOpen: boolean;
action: string;
} | null>(null);

// Fetch installed scripts
const { data: scriptsData, refetch: refetchScripts, isLoading } = api.installedScripts.getAllInstalledScripts.useQuery();
const { data: statsData } = api.installedScripts.getInstallationStats.useQuery();
Expand Down Expand Up @@ -247,6 +254,7 @@ export function InstalledScriptsTab() {

const controlContainerMutation = api.installedScripts.controlContainer.useMutation({
onSuccess: (data, variables) => {
setLoadingModal(null);
setControllingScriptId(null);

if (data.success) {
Expand Down Expand Up @@ -287,6 +295,7 @@ export function InstalledScriptsTab() {
},
onError: (error) => {
console.error('Container control error:', error);
setLoadingModal(null);
setControllingScriptId(null);

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

const destroyContainerMutation = api.installedScripts.destroyContainer.useMutation({
onSuccess: (data) => {
setLoadingModal(null);
setControllingScriptId(null);

if (data.success) {
Expand All @@ -326,6 +336,7 @@ export function InstalledScriptsTab() {
},
onError: (error) => {
console.error('Container destroy error:', error);
setLoadingModal(null);
setControllingScriptId(null);

// Show detailed error message
Expand Down Expand Up @@ -515,6 +526,7 @@ export function InstalledScriptsTab() {
message: `Are you sure you want to ${action} container ${script.container_id} (${script.script_name})?`,
onConfirm: () => {
setControllingScriptId(script.id);
setLoadingModal({ isOpen: true, action: `${action === 'start' ? 'Starting' : 'Stopping'} container ${script.container_id}...` });
void controlContainerMutation.mutate({ id: script.id, action });
setConfirmationModal(null);
}
Expand All @@ -535,6 +547,7 @@ export function InstalledScriptsTab() {
confirmText: script.container_id,
onConfirm: () => {
setControllingScriptId(script.id);
setLoadingModal({ isOpen: true, action: `Destroying container ${script.container_id}...` });
void destroyContainerMutation.mutate({ id: script.id });
setConfirmationModal(null);
}
Expand Down Expand Up @@ -1566,6 +1579,14 @@ export function InstalledScriptsTab() {
type={errorModal.type ?? 'error'}
/>
)}

{/* Loading Modal */}
{loadingModal && (
<LoadingModal
isOpen={loadingModal.isOpen}
action={loadingModal.action}
/>
)}
</div>
);
}
37 changes: 37 additions & 0 deletions src/app/_components/LoadingModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import { Loader2 } from 'lucide-react';

interface LoadingModalProps {
isOpen: boolean;
action: string;
}

export function LoadingModal({ isOpen, action }: LoadingModalProps) {
if (!isOpen) return null;

return (
<div className="fixed inset-0 backdrop-blur-sm bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-card rounded-lg shadow-xl max-w-md w-full border border-border p-8">
<div className="flex flex-col items-center space-y-4">
<div className="relative">
<Loader2 className="h-12 w-12 animate-spin text-primary" />
<div className="absolute inset-0 rounded-full border-2 border-primary/20 animate-pulse"></div>
</div>
<div className="text-center">
<h3 className="text-lg font-semibold text-card-foreground mb-2">
Processing
</h3>
<p className="text-sm text-muted-foreground">
{action}
</p>
<p className="text-xs text-muted-foreground mt-2">
Please wait...
</p>
</div>
</div>
</div>
</div>
);
}