feat: implement light/dark mode theme system (#182)
* feat: implement light/dark mode theme system - Add semantic color CSS variables (success, warning, info, error) for both themes - Create ThemeProvider with React context and localStorage persistence - Add ThemeToggle component with sun/moon icons for header region - Add theme switcher in General Settings modal - Replace 200+ hardcoded Tailwind colors with CSS variables across 30+ components - Update layout.tsx to remove forced dark mode - Keep terminal colors unchanged as requested - Default to dark mode, with seamless light/dark switching Components updated: - High-priority: InstalledScriptsTab, ScriptInstallationCard, LXCSettingsModal, ScriptsGrid - All remaining component files with hardcoded colors - UI components: button, toggle, badge variants - Modal components: ErrorModal, ConfirmationModal, AuthModal, SetupModal - Form components: ServerForm, FilterBar, CategorySidebar - Display components: ScriptCard, ScriptCardList, DiffViewer, TextViewer Theme switchers: - Header: Small nuanced toggle in top-right - Settings: Detailed Light/Dark selection in General Settings * fix: resolve ESLint warnings - Fix missing dependencies in useCallback and useEffect hooks - Prefix unused parameter with underscore to satisfy ESLint rules - Build now completes without warnings * fix: improve toggle component styling for better visibility - Use explicit gray colors instead of CSS variables for toggle background - Ensure proper contrast in both light and dark modes - Toggle switches now display correctly with proper visual states * fix: improve toggle visual states for better UX - Use explicit conditional styling instead of peer classes - Active toggles now clearly show primary color background - Inactive toggles show gray background for clear distinction - Much easier to tell which toggles are on/off at a glance * fix: improve toggle contrast in dark mode - Change inactive toggle background from gray-700 to gray-600 for better visibility - Add darker border color (gray-500) for toggle handle in dark mode - Toggles now have proper contrast against dark backgrounds - Both light and dark modes now have clear visual distinction * fix: resolve dependency loop and improve dropdown styling - Fix circular dependency in InstalledScriptsTab status check - Remove fetchContainerStatuses function and inline logic in useEffect - Make all dropdown menu items grey with consistent hover effects - Update both ScriptInstallationCard and InstalledScriptsTab dropdowns - Remove unused useCallback import - Build now completes without warnings or errors * fix: restore proper button colors and eliminate dependency loop - Restore red color for Stop/Destroy buttons and green for Start buttons - Fix circular dependency by using ref for containerStatusMutation - Update both InstalledScriptsTab and ScriptInstallationCard dropdowns - Maintain grey color for other menu items (Update, Shell, Open UI, etc.) - Build now completes without warnings or dependency loops * feat: add missing hover utility classes for semantic colors - Add hover states for success, warning, info, error colors - Add hover:bg-success/20, hover:bg-error/20, etc. classes - Add hover:text-success-foreground, hover:text-error-foreground classes - Start/Stop and Destroy buttons now have proper hover effects - All dropdown menu items now have consistent hover behavior * feat: improve status cards with useful LXC container information - Replace useless 'Successful/Failed/In Progress' cards with meaningful data - Show 'Running LXC' count in green (actual running containers) - Show 'Stopped LXC' count in red (actual stopped containers) - Keep 'Total Installations' for overall count - Change layout from 4 columns to 3 columns for better spacing - Status cards now show real-time container states instead of installation status * style: center content in status cards - Add text-center class to each individual status card - Numbers and labels now centered within each card - Improves visual balance and readability - All three cards (Total, Running LXC, Stopped LXC) now have centered content
This commit is contained in:
committed by
GitHub
parent
d0312165bd
commit
c89638021c
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import { useState, useEffect, useRef, useMemo } from 'react';
|
||||
import { api } from '~/trpc/react';
|
||||
import { Terminal } from './Terminal';
|
||||
import { StatusBadge } from './Badge';
|
||||
@@ -204,6 +204,9 @@ export function InstalledScriptsTab() {
|
||||
}
|
||||
});
|
||||
|
||||
// Ref for container status mutation to avoid dependency loops
|
||||
const containerStatusMutationRef = useRef(containerStatusMutation);
|
||||
|
||||
// Cleanup orphaned scripts mutation
|
||||
const cleanupMutation = api.installedScripts.cleanupOrphanedScripts.useMutation({
|
||||
onSuccess: (data) => {
|
||||
@@ -289,7 +292,8 @@ export function InstalledScriptsTab() {
|
||||
});
|
||||
|
||||
// Re-fetch status for all containers using bulk method (in background)
|
||||
fetchContainerStatuses();
|
||||
// Trigger status check by updating scripts length dependency
|
||||
// This will be handled by the useEffect that watches scripts.length
|
||||
} else {
|
||||
// Show error message from backend
|
||||
const errorMessage = data.error ?? 'Unknown error occurred';
|
||||
@@ -362,41 +366,15 @@ export function InstalledScriptsTab() {
|
||||
const scripts: InstalledScript[] = useMemo(() => (scriptsData?.scripts as InstalledScript[]) ?? [], [scriptsData?.scripts]);
|
||||
const stats = statsData?.stats;
|
||||
|
||||
// Update ref when scripts change
|
||||
// Update refs when data changes
|
||||
useEffect(() => {
|
||||
scriptsRef.current = scripts;
|
||||
}, [scripts]);
|
||||
|
||||
useEffect(() => {
|
||||
containerStatusMutationRef.current = containerStatusMutation;
|
||||
}, [containerStatusMutation]);
|
||||
|
||||
// Function to fetch container statuses - simplified to just check all servers
|
||||
const fetchContainerStatuses = useCallback(() => {
|
||||
console.log('fetchContainerStatuses called, isPending:', containerStatusMutation.isPending);
|
||||
|
||||
// Prevent multiple simultaneous status checks
|
||||
if (containerStatusMutation.isPending) {
|
||||
console.log('Status check already pending, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any existing timeout
|
||||
if (statusCheckTimeoutRef.current) {
|
||||
clearTimeout(statusCheckTimeoutRef.current);
|
||||
}
|
||||
|
||||
// Debounce status checks by 500ms
|
||||
statusCheckTimeoutRef.current = setTimeout(() => {
|
||||
const currentScripts = scriptsRef.current;
|
||||
|
||||
// Get unique server IDs from scripts
|
||||
const serverIds = [...new Set(currentScripts
|
||||
.filter(script => script.server_id)
|
||||
.map(script => script.server_id!))];
|
||||
|
||||
console.log('Executing status check for server IDs:', serverIds);
|
||||
if (serverIds.length > 0) {
|
||||
containerStatusMutation.mutate({ serverIds });
|
||||
}
|
||||
}, 500);
|
||||
}, []);
|
||||
|
||||
// Run cleanup when component mounts and scripts are loaded (only once)
|
||||
useEffect(() => {
|
||||
@@ -410,7 +388,32 @@ export function InstalledScriptsTab() {
|
||||
useEffect(() => {
|
||||
if (scripts.length > 0) {
|
||||
console.log('Status check triggered - scripts length:', scripts.length);
|
||||
fetchContainerStatuses();
|
||||
|
||||
// Clear any existing timeout
|
||||
if (statusCheckTimeoutRef.current) {
|
||||
clearTimeout(statusCheckTimeoutRef.current);
|
||||
}
|
||||
|
||||
// Debounce status checks by 500ms
|
||||
statusCheckTimeoutRef.current = setTimeout(() => {
|
||||
// Prevent multiple simultaneous status checks
|
||||
if (containerStatusMutationRef.current.isPending) {
|
||||
console.log('Status check already pending, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentScripts = scriptsRef.current;
|
||||
|
||||
// Get unique server IDs from scripts
|
||||
const serverIds = [...new Set(currentScripts
|
||||
.filter(script => script.server_id)
|
||||
.map(script => script.server_id!))];
|
||||
|
||||
console.log('Executing status check for server IDs:', serverIds);
|
||||
if (serverIds.length > 0) {
|
||||
containerStatusMutationRef.current.mutate({ serverIds });
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}, [scripts.length]);
|
||||
|
||||
@@ -896,22 +899,22 @@ export function InstalledScriptsTab() {
|
||||
<h2 className="text-2xl font-bold text-foreground mb-4">Installed Scripts</h2>
|
||||
|
||||
{stats && (
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-blue-500/10 border border-blue-500/20 p-4 rounded-lg">
|
||||
<div className="text-2xl font-bold text-blue-400">{stats.total}</div>
|
||||
<div className="text-sm text-blue-300">Total Installations</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||
<div className="bg-info/10 border border-info/20 p-4 rounded-lg text-center">
|
||||
<div className="text-2xl font-bold text-info">{stats.total}</div>
|
||||
<div className="text-sm text-info/80">Total Installations</div>
|
||||
</div>
|
||||
<div className="bg-green-500/10 border border-green-500/20 p-4 rounded-lg">
|
||||
<div className="text-2xl font-bold text-green-400">{stats.byStatus.success}</div>
|
||||
<div className="text-sm text-green-300">Successful</div>
|
||||
<div className="bg-success/10 border border-success/20 p-4 rounded-lg text-center">
|
||||
<div className="text-2xl font-bold text-success">
|
||||
{scriptsWithStatus.filter(script => script.container_status === 'running').length}
|
||||
</div>
|
||||
<div className="text-sm text-success/80">Running LXC</div>
|
||||
</div>
|
||||
<div className="bg-red-500/10 border border-red-500/20 p-4 rounded-lg">
|
||||
<div className="text-2xl font-bold text-red-400">{stats.byStatus.failed}</div>
|
||||
<div className="text-sm text-red-300">Failed</div>
|
||||
</div>
|
||||
<div className="bg-yellow-500/10 border border-yellow-500/20 p-4 rounded-lg">
|
||||
<div className="text-2xl font-bold text-yellow-400">{stats.byStatus.in_progress}</div>
|
||||
<div className="text-sm text-yellow-300">In Progress</div>
|
||||
<div className="bg-error/10 border border-error/20 p-4 rounded-lg text-center">
|
||||
<div className="text-2xl font-bold text-error">
|
||||
{scriptsWithStatus.filter(script => script.container_status === 'stopped').length}
|
||||
</div>
|
||||
<div className="text-sm text-error/80">Stopped LXC</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -933,7 +936,15 @@ export function InstalledScriptsTab() {
|
||||
{showAutoDetectForm ? 'Cancel Auto-Detect' : '🔍 Auto-Detect LXC Containers (Must contain a tag with "community-script")'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={fetchContainerStatuses}
|
||||
onClick={() => {
|
||||
// Trigger status check by calling the mutation directly
|
||||
const serverIds = [...new Set(scripts
|
||||
.filter(script => script.server_id)
|
||||
.map(script => script.server_id!))];
|
||||
if (serverIds.length > 0) {
|
||||
containerStatusMutation.mutate({ serverIds });
|
||||
}
|
||||
}}
|
||||
disabled={containerStatusMutation.isPending ?? scripts.length === 0}
|
||||
variant="outline"
|
||||
size="default"
|
||||
@@ -1018,17 +1029,17 @@ export function InstalledScriptsTab() {
|
||||
{autoDetectStatus.type && (
|
||||
<div className={`p-4 rounded-lg border ${
|
||||
autoDetectStatus.type === 'success'
|
||||
? 'bg-green-50 dark:bg-green-950/20 border-green-200 dark:border-green-800'
|
||||
: 'bg-red-50 dark:bg-red-950/20 border-red-200 dark:border-red-800'
|
||||
? 'bg-success/10 border-success/20'
|
||||
: 'bg-error/10 border-error/20'
|
||||
}`}>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
{autoDetectStatus.type === 'success' ? (
|
||||
<svg className="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<svg className="h-5 w-5 text-success" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<svg className="h-5 w-5 text-error" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)}
|
||||
@@ -1036,8 +1047,8 @@ export function InstalledScriptsTab() {
|
||||
<div className="ml-3">
|
||||
<p className={`text-sm font-medium ${
|
||||
autoDetectStatus.type === 'success'
|
||||
? 'text-green-800 dark:text-green-200'
|
||||
: 'text-red-800 dark:text-red-200'
|
||||
? 'text-success-foreground'
|
||||
: 'text-error-foreground'
|
||||
}`}>
|
||||
{autoDetectStatus.message}
|
||||
</p>
|
||||
@@ -1050,17 +1061,17 @@ export function InstalledScriptsTab() {
|
||||
{cleanupStatus.type && (
|
||||
<div className={`p-4 rounded-lg border ${
|
||||
cleanupStatus.type === 'success'
|
||||
? 'bg-slate-50 dark:bg-slate-900/50 border-slate-200 dark:border-slate-700'
|
||||
: 'bg-red-50 dark:bg-red-950/20 border-red-200 dark:border-red-800'
|
||||
? 'bg-muted/50 border-muted'
|
||||
: 'bg-error/10 border-error/20'
|
||||
}`}>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
{cleanupStatus.type === 'success' ? (
|
||||
<svg className="h-5 w-5 text-slate-500 dark:text-slate-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<svg className="h-5 w-5 text-muted-foreground" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<svg className="h-5 w-5 text-error" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)}
|
||||
@@ -1068,8 +1079,8 @@ export function InstalledScriptsTab() {
|
||||
<div className="ml-3">
|
||||
<p className={`text-sm font-medium ${
|
||||
cleanupStatus.type === 'success'
|
||||
? 'text-slate-700 dark:text-slate-300'
|
||||
: 'text-red-800 dark:text-red-200'
|
||||
? 'text-foreground'
|
||||
: 'text-error-foreground'
|
||||
}`}>
|
||||
{cleanupStatus.message}
|
||||
</p>
|
||||
@@ -1085,18 +1096,18 @@ export function InstalledScriptsTab() {
|
||||
<div className="mb-6 p-4 sm:p-6 bg-card rounded-lg border border-border shadow-sm">
|
||||
<h3 className="text-lg font-semibold text-foreground mb-4 sm:mb-6">Auto-Detect LXC Containers (Must contain a tag with "community-script")</h3>
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
<div className="bg-slate-50 dark:bg-slate-900/30 border border-slate-200 dark:border-slate-700 rounded-lg p-4">
|
||||
<div className="bg-muted/30 border border-muted rounded-lg p-4">
|
||||
<div className="flex items-start">
|
||||
<div className="flex-shrink-0">
|
||||
<svg className="h-5 w-5 text-slate-500 dark:text-slate-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<svg className="h-5 w-5 text-muted-foreground" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h4 className="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
<h4 className="text-sm font-medium text-foreground">
|
||||
How it works
|
||||
</h4>
|
||||
<div className="mt-2 text-sm text-slate-600 dark:text-slate-400">
|
||||
<div className="mt-2 text-sm text-muted-foreground">
|
||||
<p>This feature will:</p>
|
||||
<ul className="list-disc list-inside mt-1 space-y-1">
|
||||
<li>Connect to the selected server via SSH</li>
|
||||
@@ -1348,14 +1359,14 @@ export function InstalledScriptsTab() {
|
||||
{script.container_status && (
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className={`w-2 h-2 rounded-full ${
|
||||
script.container_status === 'running' ? 'bg-green-500' :
|
||||
script.container_status === 'stopped' ? 'bg-red-500' :
|
||||
'bg-gray-400'
|
||||
script.container_status === 'running' ? 'bg-success' :
|
||||
script.container_status === 'stopped' ? 'bg-error' :
|
||||
'bg-muted-foreground'
|
||||
}`}></div>
|
||||
<span className={`text-xs font-medium ${
|
||||
script.container_status === 'running' ? 'text-green-700 dark:text-green-300' :
|
||||
script.container_status === 'stopped' ? 'text-red-700 dark:text-red-300' :
|
||||
'text-gray-500 dark:text-gray-400'
|
||||
script.container_status === 'running' ? 'text-success' :
|
||||
script.container_status === 'stopped' ? 'text-error' :
|
||||
'text-muted-foreground'
|
||||
}`}>
|
||||
{script.container_status === 'running' ? 'Running' :
|
||||
script.container_status === 'stopped' ? 'Stopped' :
|
||||
@@ -1397,7 +1408,7 @@ export function InstalledScriptsTab() {
|
||||
{containerStatuses.get(script.id) === 'running' && (
|
||||
<button
|
||||
onClick={() => handleOpenWebUI(script)}
|
||||
className="text-xs px-2 py-1 bg-blue-900/20 hover:bg-blue-900/30 border border-blue-700/50 text-blue-300 hover:text-blue-200 hover:border-blue-600/60 transition-all duration-200 hover:scale-105 hover:shadow-md rounded disabled:opacity-50 flex-shrink-0"
|
||||
className="text-xs px-2 py-1 bg-info/20 hover:bg-info/30 border border-info/50 text-info hover:text-info-foreground hover:border-info/60 transition-all duration-200 hover:scale-105 hover:shadow-md rounded disabled:opacity-50 flex-shrink-0"
|
||||
title="Open Web UI"
|
||||
>
|
||||
Open UI
|
||||
@@ -1411,7 +1422,7 @@ export function InstalledScriptsTab() {
|
||||
<button
|
||||
onClick={() => handleAutoDetectWebUI(script)}
|
||||
disabled={autoDetectWebUIMutation.isPending}
|
||||
className="text-xs px-2 py-1 bg-blue-900 hover:bg-blue-800 text-blue-300 border border-blue-700 rounded disabled:opacity-50 transition-colors"
|
||||
className="text-xs px-2 py-1 bg-info hover:bg-info/90 text-info-foreground border border-info rounded disabled:opacity-50 transition-colors"
|
||||
title="Re-detect IP and port"
|
||||
>
|
||||
{autoDetectWebUIMutation.isPending ? '...' : 'Re-detect'}
|
||||
@@ -1475,17 +1486,17 @@ export function InstalledScriptsTab() {
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="bg-gray-800/20 hover:bg-gray-800/30 border border-gray-600/50 text-gray-300 hover:text-gray-200 hover:border-gray-500/60 transition-all duration-200 hover:scale-105 hover:shadow-md"
|
||||
className="bg-muted/20 hover:bg-muted/30 border border-muted text-muted-foreground hover:text-foreground hover:border-muted-foreground transition-all duration-200 hover:scale-105 hover:shadow-md"
|
||||
>
|
||||
Actions
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-48 bg-gray-900 border-gray-700">
|
||||
<DropdownMenuContent className="w-48 bg-card border-border">
|
||||
{script.container_id && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleUpdateScript(script)}
|
||||
disabled={containerStatuses.get(script.id) === 'stopped'}
|
||||
className="text-cyan-300 hover:text-cyan-200 hover:bg-cyan-900/20 focus:bg-cyan-900/20"
|
||||
className="text-muted-foreground hover:text-foreground hover:bg-muted/20 focus:bg-muted/20"
|
||||
>
|
||||
Update
|
||||
</DropdownMenuItem>
|
||||
@@ -1494,7 +1505,7 @@ export function InstalledScriptsTab() {
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleOpenShell(script)}
|
||||
disabled={containerStatuses.get(script.id) === 'stopped'}
|
||||
className="text-gray-300 hover:text-gray-200 hover:bg-gray-800/20 focus:bg-gray-800/20"
|
||||
className="text-muted-foreground hover:text-foreground hover:bg-muted/20 focus:bg-muted/20"
|
||||
>
|
||||
Shell
|
||||
</DropdownMenuItem>
|
||||
@@ -1503,7 +1514,7 @@ export function InstalledScriptsTab() {
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleOpenWebUI(script)}
|
||||
disabled={containerStatuses.get(script.id) === 'stopped'}
|
||||
className="text-blue-300 hover:text-blue-200 hover:bg-blue-900/20 focus:bg-blue-900/20"
|
||||
className="text-muted-foreground hover:text-foreground hover:bg-muted/20 focus:bg-muted/20"
|
||||
>
|
||||
Open UI
|
||||
</DropdownMenuItem>
|
||||
@@ -1512,28 +1523,28 @@ export function InstalledScriptsTab() {
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleAutoDetectWebUI(script)}
|
||||
disabled={autoDetectWebUIMutation.isPending ?? containerStatuses.get(script.id) === 'stopped'}
|
||||
className="text-blue-300 hover:text-blue-200 hover:bg-blue-900/20 focus:bg-blue-900/20"
|
||||
className="text-muted-foreground hover:text-foreground hover:bg-muted/20 focus:bg-muted/20"
|
||||
>
|
||||
{autoDetectWebUIMutation.isPending ? 'Re-detect...' : 'Re-detect IP/Port'}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{script.container_id && script.execution_mode === 'ssh' && (
|
||||
<>
|
||||
<DropdownMenuSeparator className="bg-gray-700" />
|
||||
<DropdownMenuSeparator className="bg-border" />
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleLXCSettings(script)}
|
||||
className="text-purple-300 hover:text-purple-200 hover:bg-purple-900/20 focus:bg-purple-900/20"
|
||||
className="text-muted-foreground hover:text-foreground hover:bg-muted/20 focus:bg-muted/20"
|
||||
>
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
LXC Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="bg-gray-700" />
|
||||
<DropdownMenuSeparator className="bg-border" />
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleStartStop(script, (containerStatuses.get(script.id) ?? 'unknown') === 'running' ? 'stop' : 'start')}
|
||||
disabled={controllingScriptId === script.id || (containerStatuses.get(script.id) ?? 'unknown') === 'unknown'}
|
||||
className={(containerStatuses.get(script.id) ?? 'unknown') === 'running'
|
||||
? "text-red-300 hover:text-red-200 hover:bg-red-900/20 focus:bg-red-900/20"
|
||||
: "text-green-300 hover:text-green-200 hover:bg-green-900/20 focus:bg-green-900/20"
|
||||
? "text-error hover:text-error-foreground hover:bg-error/20 focus:bg-error/20"
|
||||
: "text-success hover:text-success-foreground hover:bg-success/20 focus:bg-success/20"
|
||||
}
|
||||
>
|
||||
{controllingScriptId === script.id ? 'Working...' : (containerStatuses.get(script.id) ?? 'unknown') === 'running' ? 'Stop' : 'Start'}
|
||||
@@ -1541,7 +1552,7 @@ export function InstalledScriptsTab() {
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleDestroy(script)}
|
||||
disabled={controllingScriptId === script.id}
|
||||
className="text-red-300 hover:text-red-200 hover:bg-red-900/20 focus:bg-red-900/20"
|
||||
className="text-error hover:text-error-foreground hover:bg-error/20 focus:bg-error/20"
|
||||
>
|
||||
{controllingScriptId === script.id ? 'Working...' : 'Destroy'}
|
||||
</DropdownMenuItem>
|
||||
@@ -1549,11 +1560,11 @@ export function InstalledScriptsTab() {
|
||||
)}
|
||||
{(!script.container_id || script.execution_mode !== 'ssh') && (
|
||||
<>
|
||||
<DropdownMenuSeparator className="bg-gray-700" />
|
||||
<DropdownMenuSeparator className="bg-border" />
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleDeleteScript(Number(script.id))}
|
||||
disabled={deleteScriptMutation.isPending}
|
||||
className="text-red-300 hover:text-red-200 hover:bg-red-900/20 focus:bg-red-900/20"
|
||||
className="text-muted-foreground hover:text-foreground hover:bg-muted/20 focus:bg-muted/20"
|
||||
>
|
||||
{deleteScriptMutation.isPending ? 'Deleting...' : 'Delete'}
|
||||
</DropdownMenuItem>
|
||||
|
||||
Reference in New Issue
Block a user