From 0a16e46372b712aac2315b8ca8604b85a3ef5f5e Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Wed, 10 Sep 2025 11:11:53 +0200 Subject: [PATCH] Remove UI for machine details --- webui/src/App.jsx | 2 - .../pages/MachineDetails/MachineDetails.jsx | 416 ------------------ webui/src/pages/MachineDetails/index.js | 2 - webui/src/pages/MachineDetails/styles.sass | 232 ---------- webui/src/pages/Machines/Machines.jsx | 20 +- 5 files changed, 2 insertions(+), 670 deletions(-) delete mode 100644 webui/src/pages/MachineDetails/MachineDetails.jsx delete mode 100644 webui/src/pages/MachineDetails/index.js delete mode 100644 webui/src/pages/MachineDetails/styles.sass diff --git a/webui/src/App.jsx b/webui/src/App.jsx index bc08b5e..bb56281 100644 --- a/webui/src/App.jsx +++ b/webui/src/App.jsx @@ -6,7 +6,6 @@ import Root from "@/common/layouts/Root.jsx"; import UserManagement from "@/pages/UserManagement"; import SystemSettings from "@/pages/SystemSettings"; import Machines from "@/pages/Machines"; -import MachineDetails from "@/pages/MachineDetails"; import "@fontsource/plus-jakarta-sans/300.css"; import "@fontsource/plus-jakarta-sans/400.css"; import "@fontsource/plus-jakarta-sans/600.css"; @@ -25,7 +24,6 @@ const App = () => { {path: "/", element: }, {path: "/dashboard", element: }, {path: "/machines", element: }, - {path: "/machines/:id", element: }, {path: "/servers", element: }, {path: "/settings", element: }, {path: "/admin/users", element: }, diff --git a/webui/src/pages/MachineDetails/MachineDetails.jsx b/webui/src/pages/MachineDetails/MachineDetails.jsx deleted file mode 100644 index 7a7b79c..0000000 --- a/webui/src/pages/MachineDetails/MachineDetails.jsx +++ /dev/null @@ -1,416 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { getRequest } from '@/common/utils/RequestUtil.js'; -import { useToast } from '@/common/contexts/ToastContext.jsx'; -import Card, { CardHeader, CardBody } from '@/common/components/Card'; -import Grid from '@/common/components/Grid'; -import LoadingSpinner from '@/common/components/LoadingSpinner'; -import EmptyState from '@/common/components/EmptyState'; -import PageHeader from '@/common/components/PageHeader'; -import DetailItem, { DetailList } from '@/common/components/DetailItem'; -import Badge from '@/common/components/Badge'; -import Button from '@/common/components/Button'; -import { - ArrowLeft, - Camera, - HardDrive, - Folder, - Calendar, - Hash, - Database, - Devices, - Eye, - ArrowCircleLeft -} from '@phosphor-icons/react'; -import './styles.sass'; - -export const MachineDetails = () => { - const { id } = useParams(); - const navigate = useNavigate(); - const toast = useToast(); - const [machine, setMachine] = useState(null); - const [snapshots, setSnapshots] = useState([]); - const [loading, setLoading] = useState(true); - const [selectedSnapshot, setSelectedSnapshot] = useState(null); - const [snapshotDetails, setSnapshotDetails] = useState(null); - const [loadingDetails, setLoadingDetails] = useState(false); - - useEffect(() => { - if (id) { - fetchMachineData(); - } - }, [id]); - - const fetchMachineData = async () => { - try { - setLoading(true); - - // Fetch machine info and snapshots in parallel - const [machineResponse, snapshotsResponse] = await Promise.all([ - getRequest(`machines/${id}`), - getRequest(`machines/${id}/snapshots`) - ]); - - setMachine(machineResponse); - setSnapshots(snapshotsResponse); - } catch (error) { - console.error('Failed to fetch machine data:', error); - toast.error('Failed to load machine details'); - } finally { - setLoading(false); - } - }; - - const fetchSnapshotDetails = async (snapshotId) => { - try { - setLoadingDetails(true); - const details = await getRequest(`machines/${id}/snapshots/${snapshotId}`); - setSnapshotDetails(details); - setSelectedSnapshot(snapshotId); - } catch (error) { - console.error('Failed to fetch snapshot details:', error); - toast.error('Failed to load snapshot details'); - } finally { - setLoadingDetails(false); - } - }; - - const backToSnapshots = () => { - setSelectedSnapshot(null); - setSnapshotDetails(null); - }; - - const formatBytes = (bytes) => { - if (!bytes) return '0 B'; - const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; - }; - - const formatDate = (dateString) => { - if (!dateString || dateString === 'Unknown') return 'Unknown'; - try { - // Handle both "2025-09-09 20:19:48" and "2025-09-09 20:19:48 UTC" formats - const cleanDate = dateString.replace(' UTC', ''); - const date = new Date(cleanDate); - if (isNaN(date.getTime())) { - return dateString; // Return original if parsing fails - } - return date.toLocaleString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }); - } catch { - return dateString; - } - }; - - const formatLBA = (lba) => { - if (!lba && lba !== 0) return '0'; - return lba.toLocaleString(); - }; - - const getFsTypeColor = (fsType) => { - switch (fsType?.toLowerCase()) { - case 'ext': - case 'ext4': - case 'ext3': - case 'ext2': - return 'success'; - case 'ntfs': - return 'info'; - case 'fat32': - case 'fat': - return 'warning'; - case 'xfs': - return 'info'; - case 'btrfs': - return 'success'; - default: - return 'secondary'; - } - }; - - const truncateHash = (hash, length = 16) => { - if (!hash) return 'Unknown'; - return hash.length > length ? `${hash.substring(0, length)}...` : hash; - }; - - if (loading) { - return ( -
- navigate('/machines')}> - - Back to Machines - - } - /> - -
- ); - } - - if (!machine) { - return ( -
- navigate('/machines')}> - - Back to Machines - - } - /> - } - title="Machine Not Found" - subtitle="This machine may have been deleted or you don't have access to it." - /> -
- ); - } - - return ( -
- - - Back to Snapshots - - ) : ( - - ) - } - /> - - - {/* Machine Information - Only show when not viewing snapshot details */} - {!selectedSnapshot && ( - - -

Machine Information

-
- - - - - - Active - } /> - - -
- )} - - {/* Snapshots List or Details */} - {!selectedSnapshot ? ( - /* Snapshots List */ - - -

Snapshots ({snapshots.length})

-
- - {snapshots.length === 0 ? ( - } - title="No Snapshots" - subtitle="This machine hasn't created any snapshots yet." - /> - ) : ( - - {snapshots.map((snapshot) => ( - - -
-
-
- -

Snapshot

-
- - - - {formatDate(snapshot.created_at)} -
- } - /> - - - {truncateHash(snapshot.id, 24)} -
- } - /> - - - {truncateHash(snapshot.snapshot_hash, 24)} -
- } - /> - - -
- -
- - - - ))} - - )} - - - ) : ( - /* Snapshot Details */ - - -

Snapshot {selectedSnapshot} Details

-
- - {loadingDetails ? ( - - ) : snapshotDetails ? ( -
- {/* Snapshot Metadata */} - - -

Metadata

-
- - - - - {formatDate(snapshotDetails.created_at)} -
- } - /> - - - {snapshotDetails.snapshot_hash} - - } - /> - - -
-
- - {/* Disks */} -
-

Disks ({snapshotDetails.disks.length})

- - {snapshotDetails.disks.map((disk, diskIndex) => ( - - -
Disk {diskIndex + 1}
-
- - - - - - - - {/* Partitions */} - {disk.partitions.length > 0 && ( -
-
Partitions
- - {disk.partitions.map((partition, partIndex) => ( - - -
- Partition {partIndex + 1} - - {partition.fs_type.toUpperCase()} - -
-
- - - - - - - - -
- ))} -
-
- )} -
-
- ))} -
-
- - ) : ( - } - title="No Details Available" - subtitle="Unable to load snapshot details." - /> - )} - - - )} - - - ); -}; - -export default MachineDetails; diff --git a/webui/src/pages/MachineDetails/index.js b/webui/src/pages/MachineDetails/index.js deleted file mode 100644 index e110807..0000000 --- a/webui/src/pages/MachineDetails/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './MachineDetails.jsx'; -export { MachineDetails } from './MachineDetails.jsx'; diff --git a/webui/src/pages/MachineDetails/styles.sass b/webui/src/pages/MachineDetails/styles.sass deleted file mode 100644 index 0bb1b3a..0000000 --- a/webui/src/pages/MachineDetails/styles.sass +++ /dev/null @@ -1,232 +0,0 @@ -// Machine Details Page Styles -.machine-details - // Snapshot Summary Cards (list view) - .snapshot-summary-card - transition: all 0.2s ease - cursor: pointer - - &:hover - border-color: var(--border-strong) - box-shadow: 0 4px 12px rgba(31, 36, 41, 0.1) - transform: translateY(-1px) - - .snapshot-summary - display: flex - justify-content: space-between - align-items: flex-start - gap: 1.5rem - - .snapshot-info - flex: 1 - - .snapshot-title - display: flex - align-items: center - gap: 0.75rem - margin-bottom: 1rem - - h4 - font-size: 1.125rem - font-weight: 600 - color: var(--text) - margin: 0 - - .snapshot-date - display: flex - align-items: center - gap: 0.5rem - font-size: 0.875rem - color: var(--text-dim) - - .snapshot-hash - display: flex - align-items: center - gap: 0.5rem - font-size: 0.875rem - - code - background: var(--bg-elev) - padding: 0.25rem 0.5rem - border-radius: var(--radius-sm) - font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', monospace - color: var(--text-dim) - font-size: 0.8rem - - .snapshot-actions - display: flex - flex-direction: column - gap: 0.5rem - - // Snapshot Detail View - .snapshot-details - .snapshot-metadata - margin-bottom: 2rem - background: linear-gradient(135deg, var(--bg-alt) 0%, var(--bg-elev) 100%) - border: 1px solid var(--border) - - .disks-section - h4 - font-size: 1.25rem - font-weight: 600 - color: var(--text) - margin-bottom: 1.5rem - display: flex - align-items: center - gap: 0.75rem - padding-bottom: 0.5rem - border-bottom: 2px solid var(--border) - - .disk-card - border: 1px solid var(--border) - background: linear-gradient(135deg, var(--bg-alt) 0%, var(--bg-elev) 100%) - transition: all 0.2s ease - position: relative - overflow: hidden - - &::before - content: '' - position: absolute - top: 0 - left: 0 - right: 0 - height: 3px - background: linear-gradient(90deg, var(--accent) 0%, var(--success) 100%) - opacity: 0 - transition: opacity 0.2s ease - - &:hover - border-color: var(--border-strong) - box-shadow: 0 6px 20px rgba(31, 36, 41, 0.15) - transform: translateY(-2px) - - &::before - opacity: 1 - - .partitions-section - margin-top: 2rem - - h6 - font-size: 1rem - font-weight: 600 - color: var(--text) - margin-bottom: 1rem - display: flex - align-items: center - gap: 0.5rem - padding: 0.5rem 0 - border-bottom: 1px solid var(--border) - - .partition-card - border: 1px solid var(--border) - background: var(--bg-elev) - transition: all 0.2s ease - position: relative - - &:hover - border-color: var(--border-strong) - box-shadow: 0 3px 10px rgba(31, 36, 41, 0.1) - transform: translateY(-1px) - - .partition-header - display: flex - justify-content: space-between - align-items: center - - span - font-size: 0.875rem - font-weight: 600 - color: var(--text) - - // Enhanced visual feedback - .snapshot-date, .snapshot-hash - transition: color 0.2s ease - - &:hover - color: var(--text) - - // Better spacing for detail items - .detail-list - .detail-item - padding: 0.75rem 0 - border-bottom: 1px solid var(--border) - - &:last-child - border-bottom: none - - .detail-label - font-weight: 500 - color: var(--text-dim) - font-size: 0.875rem - text-transform: uppercase - letter-spacing: 0.05em - - .detail-value - font-weight: 500 - color: var(--text) - - code - background: var(--bg-elev) - padding: 0.25rem 0.5rem - border-radius: var(--radius-sm) - font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', monospace - font-size: 0.8rem - border: 1px solid var(--border) - - // Loading and error states - .loading-section - text-align: center - padding: 3rem - - .spinner - border: 3px solid var(--border) - border-top: 3px solid var(--accent) - border-radius: 50% - width: 40px - height: 40px - animation: spin 1s linear infinite - margin: 0 auto 1rem - - @keyframes spin - 0% - transform: rotate(0deg) - 100% - transform: rotate(360deg) - - // Responsive design - @media (max-width: 768px) - .snapshot-summary - flex-direction: column - gap: 1rem - - .snapshot-actions - flex-direction: row - align-self: stretch - - .disk-card .partitions-section h6 - font-size: 0.875rem - - .disks-section h4 - font-size: 1.125rem - - // Visual hierarchy improvements - .card - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) - - &:hover - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) - - .badge - font-weight: 600 - letter-spacing: 0.025em - - &.variant-success - background: linear-gradient(135deg, var(--success) 0%, #22c55e 100%) - - &.variant-info - background: linear-gradient(135deg, var(--info) 0%, #3b82f6 100%) - - &.variant-warning - background: linear-gradient(135deg, var(--warning) 0%, #f59e0b 100%) - - &.variant-secondary - background: linear-gradient(135deg, var(--text-dim) 0%, #6b7280 100%) diff --git a/webui/src/pages/Machines/Machines.jsx b/webui/src/pages/Machines/Machines.jsx index 6e0fe1e..c05366a 100644 --- a/webui/src/pages/Machines/Machines.jsx +++ b/webui/src/pages/Machines/Machines.jsx @@ -1,5 +1,4 @@ import React, {useState, useEffect, useContext} from 'react'; -import {useNavigate} from 'react-router-dom'; import {UserContext} from '@/common/contexts/UserContext.jsx'; import {useToast} from '@/common/contexts/ToastContext.jsx'; import {getRequest, postRequest, deleteRequest} from '@/common/utils/RequestUtil.js'; @@ -29,7 +28,6 @@ import './styles.sass'; export const Machines = () => { const {user: currentUser} = useContext(UserContext); const toast = useToast(); - const navigate = useNavigate(); const [machines, setMachines] = useState([]); const [loading, setLoading] = useState(true); const [showCreateModal, setShowCreateModal] = useState(false); @@ -181,14 +179,6 @@ export const Machines = () => { } }; - const handleMachineClick = (machineId) => { - navigate(`/machines/${machineId}`); - }; - - const handleActionClick = (e) => { - e.stopPropagation(); // Prevent navigation when clicking action buttons - }; - const handleInputChange = (e) => { const {name, value} = e.target; setFormData(prev => ({ @@ -230,13 +220,7 @@ export const Machines = () => { {machines.map(machine => ( - handleMachineClick(machine.id)} - style={{ cursor: 'pointer' }} - > +
@@ -249,7 +233,7 @@ export const Machines = () => { {formatUuid(machine.uuid)}
-
+