136 lines
4.9 KiB
TypeScript
136 lines
4.9 KiB
TypeScript
import { getTranslations } from "next-intl/server";
|
|
import { Wrench } from "lucide-react";
|
|
import { saveMaintenance } from "@/actions/admin-maintenance";
|
|
import { StatusCard } from "@/components/admin/dashboard";
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const KEYS = ["maintenance_enabled", "maintenance_message", "min_maintenance_login_rank"] as const;
|
|
|
|
export default async function AdminMaintenance() {
|
|
const t = await getTranslations("pages.admin.maintenance");
|
|
|
|
let rows: { key: string; value: string }[] = [];
|
|
try {
|
|
rows = await prisma.websiteSetting.findMany({
|
|
where: { key: { in: [...KEYS] } },
|
|
select: { key: true, value: true },
|
|
});
|
|
} catch {
|
|
rows = [];
|
|
}
|
|
|
|
const byKey = new Map(rows.map((r) => [r.key, r.value]));
|
|
|
|
const enabledRaw = byKey.get("maintenance_enabled") ?? "0";
|
|
const enabled = enabledRaw === "1" || enabledRaw.toLowerCase() === "true";
|
|
const message = byKey.get("maintenance_message") ?? "";
|
|
const minRankRaw = byKey.get("min_maintenance_login_rank") ?? "5";
|
|
const minRank = Number.parseInt(minRankRaw, 10);
|
|
const minRankValue = Number.isFinite(minRank) ? minRank : 5;
|
|
|
|
return (
|
|
<main>
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
|
|
<Wrench size={20} className="text-[var(--admin-accent)]" />
|
|
</div>
|
|
<div>
|
|
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
|
|
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
|
{t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
|
|
<StatusCard
|
|
label={t("maintenance")}
|
|
value={enabled ? t("enabled") : t("disabled")}
|
|
state={enabled ? "danger" : "ok"}
|
|
hint={enabled ? t("hotelLockedDown") : t("hotelOpen")}
|
|
icon={enabled ? "🚧" : "🟢"}
|
|
/>
|
|
<StatusCard label={t("minLoginRank")} value={minRankValue} hint={t("duringMaintenance")} icon="🛡️" />
|
|
<StatusCard
|
|
label={t("splashMessage")}
|
|
value={message ? t("set") : t("empty")}
|
|
state={message ? "ok" : "neutral"}
|
|
icon="💬"
|
|
/>
|
|
</div>
|
|
|
|
<section className="mt-6">
|
|
<h2 className="admin-section-title">{t("currentState")}</h2>
|
|
<div className="admin-card flex items-center gap-3">
|
|
<span
|
|
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${enabled ? "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]"}`}
|
|
>
|
|
{enabled ? t("enabled") : t("disabled")}
|
|
</span>
|
|
<div>
|
|
<strong>{t("maintenanceCurrently", { status: enabled ? t("enabled") : t("disabled") })}</strong>
|
|
<p className="text-xs theme-text-muted dark:theme-text-muted mt-1 m-0">
|
|
{enabled ? t("onlyRankCanLogin", { rank: minRankValue }) : t("hotelOpenToEveryone")}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="mt-6">
|
|
<h2 className="admin-section-title">{t("settings")}</h2>
|
|
<form action={saveMaintenance} className="admin-card">
|
|
<label htmlFor="mt-enabled" className="flex items-center gap-2 mb-4">
|
|
<input
|
|
id="mt-enabled"
|
|
type="checkbox"
|
|
name="enabled"
|
|
value="1"
|
|
defaultChecked={enabled}
|
|
className="w-auto"
|
|
/>
|
|
<span>{t("enableMaintenance")}</span>
|
|
</label>
|
|
|
|
<div className="mb-4 max-w-[220px]">
|
|
<label
|
|
htmlFor="mt-min-rank"
|
|
className="text-sm theme-text-muted dark:theme-text-muted block mb-1"
|
|
>
|
|
{t("minLoginRankLabel")}
|
|
</label>
|
|
<input
|
|
id="mt-min-rank"
|
|
type="number"
|
|
name="min_rank"
|
|
min={0}
|
|
step={1}
|
|
defaultValue={minRankValue}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div className="mb-4">
|
|
<label htmlFor="mt-message" className="text-sm theme-text-muted dark:theme-text-muted block mb-1">
|
|
{t("maintenanceMessageLabel")}
|
|
</label>
|
|
<textarea
|
|
id="mt-message"
|
|
name="message"
|
|
rows={8}
|
|
defaultValue={message}
|
|
placeholder={t("maintenanceMessagePlaceholder")}
|
|
className="w-full resize-y"
|
|
/>
|
|
</div>
|
|
|
|
<button type="submit" className="btn btn-primary">
|
|
{t("saveChanges")}
|
|
</button>
|
|
</form>
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|