diff --git a/app/MainClient.tsx b/app/MainClient.tsx index 4248dd7..f17f038 100644 --- a/app/MainClient.tsx +++ b/app/MainClient.tsx @@ -43,17 +43,22 @@ export default function MainClient({ kuerzel, beoId, beoName }: Props) { } return ( -
-
-
-

Logbuch — Sternwarte Welzheim

-
- +
+
+ + {/* Header */} +
+

+ Logbuch — Sternwarte Welzheim +

+
+ {kuerzel} — {beoName} + {kuerzel} @@ -61,24 +66,25 @@ export default function MainClient({ kuerzel, beoId, beoName }: Props) {
{/* Kuppel-Tabs */} -
+
{KUPPELN.map((k) => ( ))}
{/* Eingabe/Liste-Tabs */} -
+
{(['eingabe', 'liste'] as const).map((tab) => (
diff --git a/app/api/logbuch/route.ts b/app/api/logbuch/route.ts index 6817f61..af25270 100644 --- a/app/api/logbuch/route.ts +++ b/app/api/logbuch/route.ts @@ -1,34 +1,33 @@ import { NextRequest, NextResponse } from 'next/server'; -import { query } from '@/lib/db'; +import { query, getPool } from '@/lib/db'; import { getSession } from '@/lib/session'; import type { SelectedObjekt } from '@/types/logbuch'; +const LIST_SQL = + 'SELECT' + + ' l.ID, l.Kuppel, l.ArtFuehrung,' + + " DATE_FORMAT(l.Beginn, '%Y-%m-%dT%H:%i') AS Beginn," + + " DATE_FORMAT(l.Ende, '%Y-%m-%dT%H:%i') AS Ende," + + ' l.Besucher, l.Bemerkungen,' + + ' l.WetterTemp, l.WetterFeuchte, l.WetterDruck,' + + ' l.created_by, l.created_at,' + + " GROUP_CONCAT(DISTINCT bk.kuerzel ORDER BY bk.kuerzel SEPARATOR ', ') AS BEOs," + + " GROUP_CONCAT(DISTINCT lo.ObjektName ORDER BY lo.ObjektName SEPARATOR ', ') AS Objekte" + + ' FROM logbuch l' + + ' LEFT JOIN logbuch_beos lb ON lb.LogbuchID = l.ID' + + ' LEFT JOIN (SELECT id, `kürzel` AS kuerzel FROM beos) bk ON bk.id = lb.BeoID' + + ' LEFT JOIN logbuch_objekte lo ON lo.LogbuchID = l.ID' + + ' WHERE l.Kuppel = ?' + + ' GROUP BY l.ID' + + ' ORDER BY l.Beginn DESC'; + export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const kuppel = searchParams.get('kuppel') || 'West'; const limit = Math.min(parseInt(searchParams.get('limit') || '20'), 100); try { - const rows = await query( - `SELECT - l.ID, l.Kuppel, l.ArtFuehrung, - DATE_FORMAT(l.Beginn, '%Y-%m-%dT%H:%i') AS Beginn, - DATE_FORMAT(l.Ende, '%Y-%m-%dT%H:%i') AS Ende, - l.Besucher, l.Bemerkungen, - l.WetterTemp, l.WetterFeuchte, l.WetterDruck, - l.created_by, l.created_at, - GROUP_CONCAT(DISTINCT b.Kuerzel ORDER BY b.Kuerzel SEPARATOR ', ') AS BEOs, - GROUP_CONCAT(DISTINCT lo.ObjektName ORDER BY lo.ObjektName SEPARATOR ', ') AS Objekte - FROM logbuch l - LEFT JOIN logbuch_beos lb ON lb.LogbuchID = l.ID - LEFT JOIN beos b ON b.ID = lb.BeoID - LEFT JOIN logbuch_objekte lo ON lo.LogbuchID = l.ID - WHERE l.Kuppel = ? - GROUP BY l.ID - ORDER BY l.Beginn DESC - LIMIT ?`, - [kuppel, limit] - ); + const rows = await query(LIST_SQL + ` LIMIT ${limit}`, [kuppel]); return NextResponse.json(rows); } catch (error) { console.error('GET /api/logbuch:', error); @@ -44,9 +43,10 @@ export async function POST(request: NextRequest) { const body = await request.json(); const { Kuppel, ArtFuehrung, Beginn, Ende, Besucher, beoIds, objekte, Bemerkungen, Wetter } = body; - const [result] = await (await import('@/lib/db')).getPool().execute( - `INSERT INTO logbuch (Kuppel, ArtFuehrung, Beginn, Ende, Besucher, Bemerkungen, WetterTemp, WetterFeuchte, WetterDruck, created_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + const pool = getPool(); + const [result] = await pool.execute( + 'INSERT INTO logbuch (Kuppel, ArtFuehrung, Beginn, Ende, Besucher, Bemerkungen, WetterTemp, WetterFeuchte, WetterDruck, created_by)' + + ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [ Kuppel, ArtFuehrung, Beginn, Ende, Besucher ?? 0, @@ -71,9 +71,7 @@ export async function POST(request: NextRequest) { if (existing[0]) { objektId = existing[0].ID; } else { - const [ins] = await (await import('@/lib/db')).getPool().execute( - 'INSERT INTO objekte (Name) VALUES (?)', [obj.Name] - ) as [{ insertId: number }, unknown]; + const [ins] = await pool.execute('INSERT INTO objekte (Name) VALUES (?)', [obj.Name]) as [{ insertId: number }, unknown]; objektId = ins.insertId; } } diff --git a/claude/Anpassung_1.md b/claude/Anpassung_1.md new file mode 100644 index 0000000..907ca9a --- /dev/null +++ b/claude/Anpassung_1.md @@ -0,0 +1,10 @@ +### Anpassung für die einzelnen Führungen +Bei den unterschiedlichen Führungen solle einige Einträge wegfallen: +* bei Sonnenführung immer die Sonne als Objekt vorgeben, keine Auswahl von anderen Objekten zulassen +* bei BEO-Sitzung und bei Technischer Dienst kein Objekte-Feld vorsehen (in der DB bleibt das Feld leer) + +### Responsives Design +Passe das Design des Eingabefensters so an, dass es auf eimem Smartphone-Bildschirm gut zu bedienen ist + +### Kuppel +Kein Feld für die Kuppel in der Eingabe diff --git a/components/LogbuchForm.tsx b/components/LogbuchForm.tsx index c2ca6f4..2197f36 100644 --- a/components/LogbuchForm.tsx +++ b/components/LogbuchForm.tsx @@ -24,6 +24,9 @@ function nowLocalDatetime(): string { return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`; } +const NO_OBJEKTE_ARTEN: ArtFuehrung[] = ['BEO-Sitzung', 'Technischer Dienst']; +const SONNE_ART: ArtFuehrung = 'Sonnenführung'; + export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved }: Props) { const [artFuehrung, setArtFuehrung] = useState('Reguläre Führung'); const [beginn, setBeginn] = useState(nowLocalDatetime()); @@ -37,6 +40,9 @@ export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved const [error, setError] = useState(''); const [success, setSuccess] = useState(false); + const showObjekte = !NO_OBJEKTE_ARTEN.includes(artFuehrung); + const isSonne = artFuehrung === SONNE_ART; + useEffect(() => { fetch('/api/wetter') .then((r) => { if (!r.ok) throw new Error(); return r.json(); }) @@ -94,6 +100,15 @@ export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved } }, [editEntry]); + // Objekte-Vorauswahl je nach Art der Führung + useEffect(() => { + if (artFuehrung === SONNE_ART) { + setObjekte([{ ID: null, Name: 'Sonne' }]); + } else if (NO_OBJEKTE_ARTEN.includes(artFuehrung)) { + setObjekte([]); + } + }, [artFuehrung]); + async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setSaving(true); @@ -107,7 +122,7 @@ export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved Ende: ende, Besucher: besucher, beoIds: beos.map((b) => b.ID), - objekte, + objekte: showObjekte ? objekte : [], Bemerkungen: bemerkungen, Wetter: wetter, }; @@ -139,80 +154,89 @@ export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved } } - return ( -
-
-
- - -
+ const inputCls = 'w-full px-3 py-3 border-2 border-gray-400 rounded-lg bg-white text-base focus:border-blue-500 focus:outline-none'; + const labelCls = 'block text-sm font-medium text-gray-700 mb-1'; -
- - -
+ return ( + + + {/* Art der Führung — volle Breite */} +
+ +
-
+ {/* Beginn / Ende — nebeneinander auf Desktop, untereinander auf Mobile */} +
- + setBeginn(e.target.value)} required - className="w-full px-3 py-2 border-2 border-gray-400 rounded-lg bg-white text-sm focus:border-blue-500 focus:outline-none" + className={inputCls} />
- + setEnde(e.target.value)} required - className="w-full px-3 py-2 border-2 border-gray-400 rounded-lg bg-white text-sm focus:border-blue-500 focus:outline-none" + className={inputCls} />
+ {/* Besucher */}
- + setBesucher(parseInt(e.target.value) || 0)} min={0} max={9999} - className="w-32 px-3 py-2 border-2 border-gray-400 rounded-lg bg-white text-sm focus:border-blue-500 focus:outline-none" + className="w-32 px-3 py-3 border-2 border-gray-400 rounded-lg bg-white text-base focus:border-blue-500 focus:outline-none" />
+ {/* BEOs */}
- +
-
- - -
+ {/* Objekte — abhängig von der Art der Führung */} + {showObjekte && ( +
+ + {isSonne ? ( +
+ + Sonne + + (bei Sonnenführung fest vorgegeben) +
+ ) : ( + + )} +
+ )} + {/* Bemerkungen */}
-
+ {/* Wetter */} {wetter && (
- -
+ +
🌡 {wetter.temp} °C 💧 {wetter.feuchte} % 🌬 {wetter.druck} hPa @@ -247,11 +272,12 @@ export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved
)} -
+ {/* Buttons — volle Breite auf Mobile */} +
@@ -259,7 +285,7 @@ export default function LogbuchForm({ kuppel, currentUserBeo, editEntry, onSaved