Initial implementation: Logbuch Sternwarte Welzheim
Vollständige Next.js 16 Webanwendung als Logbuch für die Sternwarte Welzheim. 4 Kuppeln (West/Ost/Süd/Pluto), BEO-basierte Authentifizierung mit erzwungenem Passwort-Wechsel beim Erstlogin, MySQL-Backend, Docker-Deployment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
70
components/BeoSelector.tsx
Normal file
70
components/BeoSelector.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { BeoOption } from '@/types/logbuch';
|
||||
|
||||
interface Props {
|
||||
selected: BeoOption[];
|
||||
onChange: (beos: BeoOption[]) => void;
|
||||
}
|
||||
|
||||
export default function BeoSelector({ selected, onChange }: Props) {
|
||||
const [all, setAll] = useState<BeoOption[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/beos')
|
||||
.then((r) => { if (!r.ok) throw new Error('Fehler'); return r.json(); })
|
||||
.then(setAll)
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
const selectedIds = new Set(selected.map((b) => b.ID));
|
||||
const available = all.filter((b) => !selectedIds.has(b.ID));
|
||||
|
||||
function add(id: string) {
|
||||
if (!id) return;
|
||||
const beo = all.find((b) => b.ID === parseInt(id));
|
||||
if (beo) onChange([...selected, beo]);
|
||||
}
|
||||
|
||||
function remove(id: number) {
|
||||
onChange(selected.filter((b) => b.ID !== id));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selected.map((b) => (
|
||||
<span
|
||||
key={b.ID}
|
||||
className="inline-flex items-center gap-1 bg-blue-100 text-blue-800 text-sm px-2 py-1 rounded-full"
|
||||
>
|
||||
{b.Kuerzel} — {b.Name}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => remove(b.ID)}
|
||||
className="ml-1 text-blue-600 hover:text-red-600 font-bold leading-none"
|
||||
aria-label={`${b.Kuerzel} entfernen`}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
{available.length > 0 && (
|
||||
<select
|
||||
className="px-3 py-1.5 border-2 border-gray-400 rounded-lg bg-white text-sm focus:border-blue-500 focus:outline-none"
|
||||
value=""
|
||||
onChange={(e) => add(e.target.value)}
|
||||
>
|
||||
<option value="">+ BEO hinzufügen</option>
|
||||
{available.map((b) => (
|
||||
<option key={b.ID} value={b.ID}>
|
||||
{b.Kuerzel} — {b.Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user