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'; import Button from '@/common/components/Button'; import Input from '@/common/components/Input'; import Modal, {ModalActions} from '@/common/components/Modal'; import Card, {CardHeader, CardBody} from '@/common/components/Card'; import Badge from '@/common/components/Badge'; 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 { PlusIcon, TrashIcon, ComputerTowerIcon, CalendarIcon, IdentificationCardIcon, UserIcon, QrCodeIcon, CopyIcon, ClockIcon } from '@phosphor-icons/react'; 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); const [showProvisioningModal, setShowProvisioningModal] = useState(false); const [selectedMachine, setSelectedMachine] = useState(null); const [provisioningCode, setProvisioningCode] = useState(null); const [formData, setFormData] = useState({name: ''}); const [formErrors, setFormErrors] = useState({}); const [submitting, setSubmitting] = useState(false); useEffect(() => { fetchMachines(); }, []); const fetchMachines = async () => { try { setLoading(true); const response = await getRequest('machines'); setMachines(response); } catch (error) { console.error('Failed to fetch machines:', error); toast.error('Failed to load machines. Please try again.'); } finally { setLoading(false); } }; const formatDate = (dateString) => { return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; const formatUuid = (uuid) => { // Show first 8 characters of UUID for display return uuid.substring(0, 8).toUpperCase(); }; const openCreateModal = () => { setFormData({name: ''}); setFormErrors({}); setShowCreateModal(true); }; const closeCreateModal = () => { setShowCreateModal(false); setFormData({name: ''}); setFormErrors({}); }; const openProvisioningModal = (machine) => { setSelectedMachine(machine); setProvisioningCode(null); setShowProvisioningModal(true); }; const closeProvisioningModal = () => { setShowProvisioningModal(false); setSelectedMachine(null); setProvisioningCode(null); }; const validateForm = () => { const errors = {}; if (!formData.name.trim()) { errors.name = 'Machine name is required'; } else if (formData.name.length < 3) { errors.name = 'Machine name must be at least 3 characters'; } setFormErrors(errors); return Object.keys(errors).length === 0; }; const handleCreateSubmit = async (e) => { e.preventDefault(); if (!validateForm()) { return; } setSubmitting(true); try { await postRequest('machines/register', { name: formData.name.trim() }); toast.success(`Machine "${formData.name}" registered successfully`); closeCreateModal(); fetchMachines(); } catch (error) { console.error('Failed to register machine:', error); const errorMessage = error.error || 'Failed to register machine. Please try again.'; toast.error(errorMessage); } finally { setSubmitting(false); } }; const handleGenerateProvisioningCode = async () => { if (!selectedMachine) return; setSubmitting(true); try { const response = await postRequest('machines/provisioning-code', { machine_id: selectedMachine.id }); setProvisioningCode(response); toast.success('Provisioning code generated successfully'); } catch (error) { console.error('Failed to generate provisioning code:', error); const errorMessage = error.error || 'Failed to generate provisioning code. Please try again.'; toast.error(errorMessage); } finally { setSubmitting(false); } }; const handleCopyCode = async (code) => { try { await navigator.clipboard.writeText(code); toast.success('Provisioning code copied to clipboard'); } catch (error) { console.error('Failed to copy to clipboard:', error); toast.error('Failed to copy to clipboard'); } }; const handleDelete = async (machineId, machineName) => { if (!window.confirm(`Are you sure you want to delete machine "${machineName}"? This action cannot be undone.`)) { return; } try { await deleteRequest(`machines/${machineId}`); toast.success(`Machine "${machineName}" deleted successfully`); fetchMachines(); } catch (error) { console.error('Failed to delete machine:', error); toast.error('Failed to delete machine. Please try again.'); } }; 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 => ({ ...prev, [name]: value })); if (formErrors[name]) { setFormErrors(prev => ({ ...prev, [name]: '' })); } }; if (loading) { return (
Generate a provisioning code for {selectedMachine?.name}. This code can be used to register a client machine with the backup system.
{!provisioningCode ? (