Visual fixes: base href, dashboard pair names, edit button
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 147 KiB |
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<base href="/">
|
||||||
<title>SyncServer</title>
|
<title>SyncServer</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Server, Activity, HardDrive, Clock, Plus } from 'lucide-react';
|
import { Server, Activity, HardDrive, Clock, Plus } from 'lucide-react';
|
||||||
import { api, Machine, Job } from '../api/client';
|
import { api, Machine, Job, SyncPair } from '../api/client';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { Card, CardBody } from '@/components/ui/Card';
|
import { Card, CardBody } from '@/components/ui/Card';
|
||||||
@@ -15,21 +15,26 @@ import { formatRelativeTime } from '@/lib/utils';
|
|||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const [machines, setMachines] = useState<Machine[]>([]);
|
const [machines, setMachines] = useState<Machine[]>([]);
|
||||||
const [jobs, setJobs] = useState<Job[]>([]);
|
const [jobs, setJobs] = useState<Job[]>([]);
|
||||||
|
const [pairs, setPairs] = useState<SyncPair[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
api<Machine[]>('/api/machines'),
|
api<Machine[]>('/api/machines'),
|
||||||
api<Job[]>('/api/jobs?limit=5'),
|
api<Job[]>('/api/jobs?limit=5'),
|
||||||
|
api<SyncPair[]>('/api/sync-pairs'),
|
||||||
])
|
])
|
||||||
.then(([m, j]) => {
|
.then(([m, j, p]) => {
|
||||||
setMachines(m);
|
setMachines(m);
|
||||||
setJobs(j);
|
setJobs(j);
|
||||||
|
setPairs(p);
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const pairName = (id: number) => pairs.find(p => p.id === id)?.name ?? `Pair ${id}`;
|
||||||
|
|
||||||
const online = machines.filter(m => m.status.startsWith('online')).length;
|
const online = machines.filter(m => m.status.startsWith('online')).length;
|
||||||
const todayJobs = jobs.filter(j => {
|
const todayJobs = jobs.filter(j => {
|
||||||
if (!j.started_at) return false;
|
if (!j.started_at) return false;
|
||||||
@@ -152,7 +157,7 @@ export default function Dashboard() {
|
|||||||
</Link>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-fg-muted">
|
<TableCell className="text-fg-muted">
|
||||||
Pair {j.sync_pair_id}
|
{pairName(j.sync_pair_id)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Badge variant={statusVariant(j.status)} label={statusLabel(j.status)} />
|
<Badge variant={statusVariant(j.status)} label={statusLabel(j.status)} />
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
import { EmptyState } from '@/components/ui/EmptyState';
|
import { EmptyState } from '@/components/ui/EmptyState';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import { Card } from '@/components/ui/Card';
|
import { Card } from '@/components/ui/Card';
|
||||||
import { Play, Trash2, Plus, GitCompare, ArrowRight, ArrowLeft } from 'lucide-react';
|
import { Play, Trash2, Plus, GitCompare, ArrowRight, ArrowLeft, Pencil } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
@@ -242,6 +242,15 @@ export default function SyncPairs() {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
onClick={() => openEdit(p)}
|
||||||
|
className="text-fg-muted hover:text-fg hover:bg-surface-raised"
|
||||||
|
title="Edit"
|
||||||
|
>
|
||||||
|
<Pencil className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
|
|||||||