diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index d0006e0..a8bdca2 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -1,5 +1,5 @@ import { NavLink, useParams } from 'react-router'; -import { Server, Play, Container, AppWindow, Settings, CloudLightning, Sun, Moon, Monitor, ChevronDown, Globe, Usb } from 'lucide-react'; +import { Server, Play, Container, AppWindow, Settings, CloudLightning, Sun, Moon, Monitor, ChevronDown, Globe, Usb, Download, CheckCircle } from 'lucide-react'; import { cn } from '../lib/utils'; import { Sidebar, @@ -71,7 +71,23 @@ export function AppSidebar() {
- +
+
+ +
+ + {({ isActive }) => ( + + + + )} + +
@@ -100,29 +116,6 @@ export function AppSidebar() { - - - {({ isActive }) => ( - -
- -
- Cloud -
- )} -
-
- @@ -177,17 +170,6 @@ export function AppSidebar() { */} - - - - -
- -
- ISO / USB -
-
-
@@ -225,21 +207,58 @@ export function AppSidebar() { + + + + +
+ +
+ ISO / USB +
+
+
- - - -
+ + + + -
- Apps -
-
-
+ Apps + + + + + + + + +
+ +
+ Available +
+
+
+ + + + +
+ +
+ Installed +
+
+
+
+
+ + diff --git a/src/components/AppsComponent.tsx b/src/components/AppsComponent.tsx index 1863b59..b63efcb 100644 --- a/src/components/AppsComponent.tsx +++ b/src/components/AppsComponent.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useLocation } from 'react-router'; import { Card } from './ui/card'; import { Button } from './ui/button'; import { Badge } from './ui/badge'; @@ -37,6 +38,7 @@ interface MergedApp extends App { type TabView = 'available' | 'installed'; export function AppsComponent() { + const location = useLocation(); const { currentInstance } = useInstanceContext(); const { data: availableAppsData, isLoading: loadingAvailable, error: availableError } = useAvailableApps(); const { @@ -51,7 +53,8 @@ export function AppsComponent() { isDeleting } = useDeployedApps(currentInstance); - const [activeTab, setActiveTab] = useState('available'); + // Determine active tab from URL path + const activeTab: TabView = location.pathname.endsWith('/installed') ? 'installed' : 'available'; const [searchTerm, setSearchTerm] = useState(''); const [selectedCategory, setSelectedCategory] = useState('all'); const [configDialogOpen, setConfigDialogOpen] = useState(false); @@ -323,22 +326,6 @@ export function AppsComponent() { - {/* Tab Navigation */} -
- - -
-
diff --git a/src/router/routes.tsx b/src/router/routes.tsx index f9bd39c..c71a5cc 100644 --- a/src/router/routes.tsx +++ b/src/router/routes.tsx @@ -101,7 +101,20 @@ export const routes: RouteObject[] = [ }, { path: 'apps', - element: , + children: [ + { + index: true, + element: , + }, + { + path: 'available', + element: , + }, + { + path: 'installed', + element: , + }, + ], }, { path: 'advanced', diff --git a/wild-web-app/src/components/apps/appUtils.tsx b/wild-web-app/src/components/apps/appUtils.tsx new file mode 100644 index 0000000..c2d7125 --- /dev/null +++ b/wild-web-app/src/components/apps/appUtils.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react'; +import { + AppWindow, + Database, + Globe, + Shield, + BarChart3, + MessageSquare, + CheckCircle, + AlertCircle, + Download, + Loader2, + Settings, +} from 'lucide-react'; +import { Badge } from '../ui/badge'; +import type { App } from '../../services/api'; + +export interface MergedApp extends App { + deploymentStatus?: 'added' | 'deployed'; + url?: string; +} + +export function getStatusIcon(status?: string) { + switch (status) { + case 'running': + return ; + case 'error': + return ; + case 'deploying': + return ; + case 'stopped': + return ; + case 'added': + return ; + case 'deployed': + return ; + case 'available': + return ; + default: + return null; + } +} + +export function getStatusBadge(app: MergedApp) { + // Determine status: runtime status > deployment status > available + const status = app.status?.status || app.deploymentStatus || 'available'; + + const variants: Record = { + available: 'secondary', + added: 'outline', + deploying: 'default', + running: 'success', + error: 'destructive', + stopped: 'warning', + deployed: 'outline', + }; + + const labels: Record = { + available: 'Available', + added: 'Added', + deploying: 'Deploying', + running: 'Running', + error: 'Error', + stopped: 'Stopped', + deployed: 'Deployed', + }; + + return ( + + {labels[status] || status} + + ); +} + +export function getCategoryIcon(category?: string) { + switch (category) { + case 'database': + return ; + case 'web': + return ; + case 'security': + return ; + case 'monitoring': + return ; + case 'communication': + return ; + case 'storage': + return ; + default: + return ; + } +} + +export function AppIcon({ app }: { app: MergedApp }) { + const [imageError, setImageError] = useState(false); + + return ( +
+ {app.icon && !imageError ? ( + {app.name} setImageError(true)} + /> + ) : ( + getCategoryIcon(app.category) + )} +
+ ); +}