Upgrade dependencies in package.json and update next, eslint, and related packages
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
import Link from "next/link";
|
||||
import { db, initDatabase } from "@/lib/db";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
type MotorcycleMaintenanceRow = {
|
||||
id: number;
|
||||
nickname: string;
|
||||
brand: string;
|
||||
model: string;
|
||||
year: number | null;
|
||||
plate: string | null;
|
||||
owner_name: string;
|
||||
tire_pressure_front_bar: number | null;
|
||||
tire_pressure_rear_bar: number | null;
|
||||
chain_tension_min_mm: number | null;
|
||||
chain_tension_max_mm: number | null;
|
||||
oil_type: string | null;
|
||||
oil_capacity_liters: number | null;
|
||||
oil_check_temp: "warm" | "cold" | null;
|
||||
oil_check_run_minutes: number | null;
|
||||
oil_check_wait_min_minutes: number | null;
|
||||
oil_check_wait_max_minutes: number | null;
|
||||
oil_check_method: "dipstick" | "sight_glass" | null;
|
||||
};
|
||||
|
||||
function formatBar(value: number | null) {
|
||||
if (value == null) return "—";
|
||||
return `${new Intl.NumberFormat("de-DE", {
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 1,
|
||||
}).format(value)} bar`;
|
||||
}
|
||||
|
||||
function oilTempLabel(value: MotorcycleMaintenanceRow["oil_check_temp"]) {
|
||||
if (value === "warm") return "Warm";
|
||||
if (value === "cold") return "Kalt";
|
||||
return "—";
|
||||
}
|
||||
|
||||
function oilMethodLabel(value: MotorcycleMaintenanceRow["oil_check_method"]) {
|
||||
if (value === "dipstick") return "Peilstab";
|
||||
if (value === "sight_glass") return "Schauglas";
|
||||
return "—";
|
||||
}
|
||||
|
||||
function oilInstruction(bike: MotorcycleMaintenanceRow) {
|
||||
const tempText =
|
||||
bike.oil_check_temp === "warm"
|
||||
? "bei warmem Motor"
|
||||
: bike.oil_check_temp === "cold"
|
||||
? "bei kaltem Motor"
|
||||
: null;
|
||||
const methodText =
|
||||
bike.oil_check_method === "dipstick"
|
||||
? "mit Peilstab"
|
||||
: bike.oil_check_method === "sight_glass"
|
||||
? "über Schauglas"
|
||||
: null;
|
||||
if (!tempText && !methodText) return "Keine Anleitung hinterlegt.";
|
||||
const runText =
|
||||
bike.oil_check_temp === "warm" && bike.oil_check_run_minutes != null
|
||||
? `Motor mindestens ${bike.oil_check_run_minutes} Min. laufen lassen, `
|
||||
: "";
|
||||
const waitText =
|
||||
bike.oil_check_temp === "warm" &&
|
||||
bike.oil_check_wait_min_minutes != null &&
|
||||
bike.oil_check_wait_max_minutes != null
|
||||
? `danach ${bike.oil_check_wait_min_minutes}-${bike.oil_check_wait_max_minutes} Min. warten, `
|
||||
: "";
|
||||
return `${runText}${waitText}Ölstand ${tempText ?? "im passenden Motorzustand"} messen ${methodText ?? "Methode offen"}.`.trim();
|
||||
}
|
||||
|
||||
function plateFromParams(value: string | string[] | undefined) {
|
||||
if (Array.isArray(value)) return String(value[0] ?? "").trim();
|
||||
return String(value ?? "").trim();
|
||||
}
|
||||
|
||||
function validatedPlate(value: string) {
|
||||
const trimmed = value.slice(0, 20).trim();
|
||||
if (!trimmed) return "";
|
||||
if (!/^[A-Za-z0-9ÄÖÜäöü \-]+$/.test(trimmed)) {
|
||||
throw new Error("Kennzeichen enthält ungültige Zeichen.");
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
export default async function KioskPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
||||
}) {
|
||||
initDatabase();
|
||||
const params = await searchParams;
|
||||
let plate = "";
|
||||
let plateError: string | null = null;
|
||||
try {
|
||||
plate = validatedPlate(plateFromParams(params.plate));
|
||||
} catch (error) {
|
||||
plateError = error instanceof Error ? error.message : "Ungültige Eingabe.";
|
||||
}
|
||||
|
||||
const matches = plate && !plateError
|
||||
? (db
|
||||
.prepare(
|
||||
`SELECT m.id, m.nickname, m.brand, m.model, m.year, m.plate, owner.name AS owner_name,
|
||||
m.tire_pressure_front_bar, m.tire_pressure_rear_bar, m.chain_tension_min_mm, m.chain_tension_max_mm,
|
||||
m.oil_type, m.oil_capacity_liters,
|
||||
m.oil_check_temp, m.oil_check_run_minutes, m.oil_check_wait_min_minutes, m.oil_check_wait_max_minutes, m.oil_check_method
|
||||
FROM motorcycles m
|
||||
JOIN users owner ON owner.id = m.created_by
|
||||
WHERE m.plate IS NOT NULL
|
||||
AND TRIM(m.plate) <> ''
|
||||
AND REPLACE(REPLACE(UPPER(m.plate), ' ', ''), '-', '') = REPLACE(REPLACE(UPPER(?), ' ', ''), '-', '')
|
||||
ORDER BY m.id ASC
|
||||
LIMIT 2`,
|
||||
)
|
||||
.all(plate) as MotorcycleMaintenanceRow[])
|
||||
: [];
|
||||
|
||||
const bike = matches.length === 1 ? matches[0] : null;
|
||||
const notFound = plate.length > 0 && matches.length === 0;
|
||||
const ambiguous = matches.length > 1;
|
||||
|
||||
return (
|
||||
<main className="page-shell">
|
||||
<div className="page-heading">
|
||||
<div>
|
||||
<p className="eyebrow">Öffentlicher Zugriff</p>
|
||||
<h1>Kiosk-Modus</h1>
|
||||
<p className="muted">Kennzeichen eingeben und Wartungsdaten direkt anzeigen.</p>
|
||||
</div>
|
||||
<Link className="button" href="/login">Zur Anmeldung</Link>
|
||||
</div>
|
||||
|
||||
<form className="panel form-grid" method="get" action="/kiosk">
|
||||
<label className="span-2">
|
||||
Kennzeichen
|
||||
<input
|
||||
name="plate"
|
||||
defaultValue={plate}
|
||||
autoFocus
|
||||
required
|
||||
maxLength={20}
|
||||
placeholder="z. B. HH AB 123"
|
||||
/>
|
||||
</label>
|
||||
<div className="form-actions span-2">
|
||||
<button className="button button-primary" type="submit">Wartungsdaten anzeigen</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{plateError && (
|
||||
<section className="section-block">
|
||||
<div className="flash flash-error">{plateError}</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{notFound && (
|
||||
<section className="section-block">
|
||||
<div className="flash flash-error">Kein Motorrad mit diesem Kennzeichen gefunden.</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{ambiguous && (
|
||||
<section className="section-block">
|
||||
<div className="flash flash-error">
|
||||
Dieses Kennzeichen ist nicht eindeutig. Bitte in der App ein eindeutiges Kennzeichen pflegen.
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{bike && (
|
||||
<>
|
||||
<section className="section-block">
|
||||
<div className="panel">
|
||||
<p className="eyebrow">{bike.brand} · {bike.model}{bike.year ? ` · ${bike.year}` : ""}</p>
|
||||
<h2>{bike.nickname}</h2>
|
||||
<p className="muted">Kennzeichen: {bike.plate ?? "—"} · Besitzer: {bike.owner_name}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="detail-grid">
|
||||
<div className="panel">
|
||||
<h3 className="panel-title">Reifen & Kette</h3>
|
||||
<dl className="data-list">
|
||||
<div><dt>Reifendruck vorne</dt><dd>{formatBar(bike.tire_pressure_front_bar)}</dd></div>
|
||||
<div><dt>Reifendruck hinten</dt><dd>{formatBar(bike.tire_pressure_rear_bar)}</dd></div>
|
||||
<div>
|
||||
<dt>Kettenspannung</dt>
|
||||
<dd>
|
||||
{bike.chain_tension_min_mm != null && bike.chain_tension_max_mm != null
|
||||
? `${bike.chain_tension_min_mm}-${bike.chain_tension_max_mm} mm`
|
||||
: "—"}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="panel">
|
||||
<h3 className="panel-title">Ölstand-Kontrolle</h3>
|
||||
<dl className="data-list">
|
||||
<div><dt>Benötigtes Öl</dt><dd>{bike.oil_type ?? "—"}</dd></div>
|
||||
<div>
|
||||
<dt>Ölmenge gesamt</dt>
|
||||
<dd>
|
||||
{bike.oil_capacity_liters != null
|
||||
? `${new Intl.NumberFormat("de-DE", { minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(bike.oil_capacity_liters)} l`
|
||||
: "—"}
|
||||
</dd>
|
||||
</div>
|
||||
<div><dt>Motorzustand</dt><dd>{oilTempLabel(bike.oil_check_temp)}</dd></div>
|
||||
<div>
|
||||
<dt>Laufzeit vor Messung</dt>
|
||||
<dd>{bike.oil_check_temp === "warm" && bike.oil_check_run_minutes != null ? `${bike.oil_check_run_minutes} Min.` : "—"}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Wartezeitfenster</dt>
|
||||
<dd>
|
||||
{bike.oil_check_temp === "warm" &&
|
||||
bike.oil_check_wait_min_minutes != null &&
|
||||
bike.oil_check_wait_max_minutes != null
|
||||
? `${bike.oil_check_wait_min_minutes}-${bike.oil_check_wait_max_minutes} Min.`
|
||||
: "—"}
|
||||
</dd>
|
||||
</div>
|
||||
<div><dt>Methode</dt><dd>{oilMethodLabel(bike.oil_check_method)}</dd></div>
|
||||
</dl>
|
||||
<p className="notes">{oilInstruction(bike)}</p>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user