import { useMemo, useState } from "react"; import { Alert, Box, Button, Card, CardContent, Chip, Dialog, DialogContent, DialogTitle, FormControl, FormControlLabel, FormHelperText, Grid, InputLabel, MenuItem, Select, Stack, Switch, TextField, Typography, } from "@mui/material"; import { useNavigate } from "react-router-dom"; import { useActivity, useDashboardShortcuts, useDeleteDashboardShortcut, useMonitoringOverview, useSaveDashboardShortcut, } from "../hooks/useDashboard"; import { useMonitoringSettings } from "../hooks/useSettings"; import type { DashboardShortcut, DashboardShortcutInput } from "../types"; import { NowPlaying } from "../components/NowPlaying"; import { MonitoringOverviewTable } from "../components/MonitoringOverviewTable"; import { SectionCard } from "../components/SectionCard"; import { DialogFooter } from "../components/DialogFooter"; import BackupDashboardWidget from "../components/BackupDashboardWidget"; function emptyShortcut(): DashboardShortcutInput { return { id: null, label: "", shortcut_type: "website", enabled: true, icon: "", url: "", task_id: "", machine_id: "", user_id: "", notes: "", }; } function normalizeWebsiteUrl(url: string): string { const trimmed = url.trim(); if (!trimmed) return ""; if (/^https?:\/\//i.test(trimmed)) return trimmed; return `https://${trimmed}`; } function shortcutHref(shortcut: DashboardShortcut): string { if (shortcut.shortcut_type === "website") { return normalizeWebsiteUrl(shortcut.url); } if (shortcut.shortcut_type === "action") { if (!shortcut.task_id) return ""; const params = new URLSearchParams({ task: shortcut.task_id }); if (shortcut.machine_id) params.set("machine_id", shortcut.machine_id); return `/actions?${params.toString()}`; } if (!shortcut.user_id) return ""; return `/users?user=${encodeURIComponent(shortcut.user_id)}`; } function ShortcutDialog({ open, draft, onChange, onClose, onSave, }: { open: boolean; draft: DashboardShortcutInput; onChange: (shortcut: DashboardShortcutInput) => void; onClose: () => void; onSave: () => void; }) { return ( {draft.id ? "Edit shortcut" : "New shortcut"} onChange({ ...draft, label: e.target.value })} /> onChange({ ...draft, icon: e.target.value })} helperText="Emoji or glyph" /> Type Website opens a URL. Saved actions jump to a task. Users deep-link. {draft.shortcut_type === "website" ? ( onChange({ ...draft, url: e.target.value })} helperText="https:// is added if missing." /> ) : draft.shortcut_type === "action" ? ( onChange({ ...draft, task_id: e.target.value }) } helperText="Saved action ID." /> onChange({ ...draft, machine_id: e.target.value }) } helperText="Optional machine target." /> ) : ( onChange({ ...draft, user_id: e.target.value })} helperText="Jellyfin user ID." /> )} onChange({ ...draft, notes: e.target.value })} /> onChange({ ...draft, enabled: e.target.checked }) } /> } label="Enabled" /> ); } function ShortcutCard({ shortcut, onOpen, onEdit, onDelete, }: { shortcut: DashboardShortcut; onOpen: () => void; onEdit: () => void; onDelete: () => void; }) { const href = shortcutHref(shortcut); const subtitle = shortcut.shortcut_type === "website" ? shortcut.url || "No URL configured" : shortcut.shortcut_type === "action" ? [ shortcut.task_id || "task pending", shortcut.machine_id ? `machine ${shortcut.machine_id}` : "any machine", ].join(" ยท ") : shortcut.user_id || "No user configured"; return ( {shortcut.label} {subtitle} {shortcut.icon ? ( {shortcut.icon} ) : null} {shortcut.notes ? ( {shortcut.notes} ) : null} ); } export function Dashboard() { const navigate = useNavigate(); const { data: machines = [] } = useMonitoringSettings(); const jellyfinMachines = useMemo( () => machines.filter( (machine) => machine.enabled && machine.services.includes("jellyfin"), ), [machines], ); const [activeJellyfinMachineId, setActiveJellyfinMachineId] = useState(""); const selectedJellyfinId = activeJellyfinMachineId || jellyfinMachines[0]?.id || ""; const { data: activity } = useActivity(selectedJellyfinId || undefined); const { data: monitoringOverview } = useMonitoringOverview(); const { data: shortcuts = [] } = useDashboardShortcuts(); const saveShortcut = useSaveDashboardShortcut(); const deleteShortcut = useDeleteDashboardShortcut(); const [shortcutDialogOpen, setShortcutDialogOpen] = useState(false); const [shortcutDraft, setShortcutDraft] = useState( emptyShortcut(), ); const [deleteShortcutId, setDeleteShortcutId] = useState(null); const openCreateShortcut = () => { setShortcutDraft(emptyShortcut()); setShortcutDialogOpen(true); }; const openEditShortcut = (shortcut: DashboardShortcut) => { setShortcutDraft({ id: shortcut.id, label: shortcut.label, shortcut_type: shortcut.shortcut_type, enabled: shortcut.enabled, icon: shortcut.icon, url: shortcut.url, task_id: shortcut.task_id, machine_id: shortcut.machine_id, user_id: shortcut.user_id, notes: shortcut.notes, }); setShortcutDialogOpen(true); }; const saveShortcutDraft = async () => { await saveShortcut.mutateAsync(shortcutDraft); setShortcutDialogOpen(false); setShortcutDraft(emptyShortcut()); }; return ( Add shortcut } > {shortcuts.length ? ( {shortcuts.map((shortcut) => ( { const href = shortcutHref(shortcut); if (shortcut.shortcut_type === "website") { window.open(href, "_blank", "noopener,noreferrer"); } else if (href) { navigate(href); } }} onEdit={() => openEditShortcut(shortcut)} onDelete={() => setDeleteShortcutId(shortcut.id)} /> ))} ) : ( No shortcuts yet. Add a website now, then add action or user shortcuts later. )} 1 ? ( ) : jellyfinMachines.length === 1 ? ( ) : null } > {activity ? ( navigate(`/users?user=${encodeURIComponent(session.user)}`) } /> ) : null} setShortcutDialogOpen(false)} onSave={saveShortcutDraft} /> setDeleteShortcutId(null)} fullWidth maxWidth="xs" > Delete shortcut? This cannot be undone. The shortcut will be removed from the dashboard. setDeleteShortcutId(null)} onConfirm={() => { if (deleteShortcutId) { deleteShortcut.mutate(deleteShortcutId); } setDeleteShortcutId(null); }} confirmLabel="Delete" confirmColor="error" /> ); }