feat: vollständiges Dashboard-Redesign mit HeroUI - monolithische App.tsx aufgelöst, 16 Seiten, Context-API, collapsible Sidebar, neues Dashboard-Layout
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
This commit is contained in:
93
frontend/src/pages/Statuspage.tsx
Normal file
93
frontend/src/pages/Statuspage.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator } from '@heroui/react';
|
||||
import { RadioTower, Save, Trash2, Plus, Activity as ActivityIcon } from 'lucide-react';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import { SectionCard } from '../components/shared/SectionCard';
|
||||
import type { StatusService } from '../types';
|
||||
|
||||
export function Statuspage() {
|
||||
const { statusDraft, setStatusDraft, saveStatuspage, statusServiceDraft, setStatusServiceDraft, addStatusService, deleteStatusService } = useApp();
|
||||
|
||||
const services = ((statusDraft?.services || []) as StatusService[]);
|
||||
|
||||
return (
|
||||
<SectionCard title="Statuspage" subtitle="Statusseite und Service-Liste verwalten">
|
||||
<div className="grid gap-5 xl:grid-cols-[420px_1fr]">
|
||||
<Card className="border border-default-100 bg-default-50/20">
|
||||
<CardHeader className="px-5 pt-5 pb-0">
|
||||
<h3 className="text-base font-semibold">Konfiguration</h3>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4 p-5">
|
||||
<Switch isSelected={statusDraft?.enabled !== false} onValueChange={(v) => setStatusDraft((s) => ({ ...(s || {}), enabled: v }))}>
|
||||
<div className="flex items-center gap-2"><RadioTower size={16} /> Statuspage aktiv</div>
|
||||
</Switch>
|
||||
|
||||
<Input
|
||||
label="Channel ID"
|
||||
placeholder="Channel f<>r Status-Updates"
|
||||
value={statusDraft?.channelId || ''}
|
||||
onValueChange={(v) => setStatusDraft((s) => ({ ...(s || {}), channelId: v }))}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Intervall (ms)"
|
||||
type="number"
|
||||
value={String(statusDraft?.intervalMs || 60000)}
|
||||
onValueChange={(v) => setStatusDraft((s) => ({ ...(s || {}), intervalMs: Number(v || 60000) }))}
|
||||
/>
|
||||
|
||||
<Button color="primary" startContent={<Save size={16} />} onPress={saveStatuspage}>
|
||||
Statuspage speichern
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border border-default-100 bg-default-50/20">
|
||||
<CardHeader className="px-5 pt-5 pb-0">
|
||||
<h3 className="text-base font-semibold">Services ({services.length})</h3>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-3 p-5">
|
||||
{services.length ? services.map((service) => (
|
||||
<div key={service.id} className="flex items-center justify-between rounded-xl border border-default-100 bg-default-50/30 px-4 py-3 text-small">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<div className="size-2 rounded-full shrink-0" />
|
||||
<div className="min-w-0">
|
||||
<span className="font-medium">{service.name || 'Service'}</span>
|
||||
<span className="ml-2 text-default-400 text-tiny truncate">{service.url || ''}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Chip size="sm" variant="flat" color={
|
||||
service.status === 'operational' ? 'success' :
|
||||
service.status === 'degraded' ? 'warning' :
|
||||
service.status === 'down' ? 'danger' : 'default'
|
||||
}>{service.status || 'unknown'}</Chip>
|
||||
<Button isIconOnly size="sm" variant="light" color="danger" onPress={() => deleteStatusService(service.id)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)) : (
|
||||
<div className="flex flex-col items-center gap-2 py-4 text-center text-tiny text-default-400">
|
||||
<ActivityIcon size={20} />
|
||||
Keine Services
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Separator />
|
||||
|
||||
<div>
|
||||
<h4 className="text-small font-semibold mb-2">Service hinzuf<EFBFBD>gen</h4>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input placeholder="Name" value={statusServiceDraft.name} onValueChange={(v) => setStatusServiceDraft((s) => ({ ...s, name: v }))} />
|
||||
<Input placeholder="URL (optional)" value={statusServiceDraft.url} onValueChange={(v) => setStatusServiceDraft((s) => ({ ...s, url: v }))} />
|
||||
<Button size="sm" color="primary" startContent={<Plus size={14} />} onPress={addStatusService}>
|
||||
Hinzuf<EFBFBD>gen
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</SectionCard>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user