Enhance Service Messages by Config & activeUntil #9

This commit is contained in:
nocnico
2025-07-03 19:26:17 +02:00
parent e137b0c75e
commit ffe0d45ab6
9 changed files with 159 additions and 78 deletions

View File

@@ -2,25 +2,24 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Notam } from "@repo/db"; import { Notam } from "@repo/db";
import { import { NotamOptionalDefaults, NotamOptionalDefaultsSchema } from "@repo/db/zod";
NotamOptionalDefaults,
NotamOptionalDefaultsSchema,
} from "@repo/db/zod";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { addMessage, disableMessage } from "../action"; import { addMessage, disableMessage } from "../action";
import { useState } from "react"; import { useState } from "react";
import { DateInput } from "_components/ui/DateInput";
import "react-datepicker/dist/react-datepicker.css"; // <-- Add this line at the top if using react-datepicker
export const MessageForm = ({ message }: { message?: Notam }) => { export const MessageForm = ({ message }: { message?: Notam }) => {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const getDefaultShowUntilDate = () => { const getDefaultShowUntilDate = () => {
const date = new Date(); const date = new Date();
date.setDate(date.getDate() + 5);
return date; return date;
}; };
const disableMessageClient = async () => { const disableMessageClient = async () => {
disableMessage(); await disableMessage();
window.location.reload();
}; };
const form = useForm<NotamOptionalDefaults>({ const form = useForm<NotamOptionalDefaults>({
@@ -28,8 +27,9 @@ export const MessageForm = ({ message }: { message?: Notam }) => {
defaultValues: { defaultValues: {
message: message?.message, message: message?.message,
color: message?.color, color: message?.color,
isMainMsg: true,
active: true, active: true,
wartungsmodus: message?.wartungsmodus,
disableHPG: message?.disableHPG,
showUntil: getDefaultShowUntilDate(), showUntil: getDefaultShowUntilDate(),
}, },
}); });
@@ -39,7 +39,8 @@ export const MessageForm = ({ message }: { message?: Notam }) => {
onSubmit={form.handleSubmit(async (values) => { onSubmit={form.handleSubmit(async (values) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
const msg = await addMessage(values); await addMessage(values);
window.location.reload();
} catch (error) { } catch (error) {
setIsSubmitting(false); setIsSubmitting(false);
console.error("Failed to add message", error); console.error("Failed to add message", error);
@@ -57,51 +58,76 @@ export const MessageForm = ({ message }: { message?: Notam }) => {
/> />
</label> </label>
<div className="gap-2 flex justify-between col-span-6"> <div className="gap-2 flex justify-between col-span-6">
<div className="content-center"> <div className="flex flex-col gap-4">
<input <div className="content-center">
type="radio" <input
value="INFO" type="radio"
{...form.register("color")} value="INFO"
className="radio radio-info ml-2 mr-2" {...form.register("color")}
/> className="radio radio-info ml-2 mr-2"
<span>Info</span> />
<input <span>Info</span>
type="radio" <input
value="SUCCESS" type="radio"
{...form.register("color")} value="SUCCESS"
className="radio radio-success ml-2 mr-2" {...form.register("color")}
/> className="radio radio-success ml-2 mr-2"
<span>Success</span> />
<input <span>Success</span>
type="radio" <input
value="WARNING" type="radio"
{...form.register("color")} value="WARNING"
className="radio radio-warning ml-2 mr-2" {...form.register("color")}
/> className="radio radio-warning ml-2 mr-2"
<span>Warning</span> />
<input <span>Warning</span>
type="radio" <input
value="ERROR" type="radio"
{...form.register("color")} value="ERROR"
className="radio radio-error ml-2 mr-2" {...form.register("color")}
/> className="radio radio-error ml-2 mr-2"
<span>Error</span> />
<span>Error</span>
</div>
<div className="flex flex-col gap-2 ml-2">
<label className="label">
<input
type="checkbox"
className="checkbox checkbox-primary"
{...form.register("wartungsmodus")}
/>
Wartungsmodus einschalten
</label>
<label className="label">
<input
type="checkbox"
className="checkbox checkbox-primary"
{...form.register("disableHPG")}
/>
HPG Alarmierung deaktivieren
</label>
</div>
<div className="flex flex-col gap-2 ml-2">
<label className="label">Nachricht & Effekte bis (optional)</label>
<DateInput
control={form.control}
name="showUntil"
showTimeInput
timeCaption="Uhrzeit"
showTimeCaption
className="input input-md"
/>
</div>
</div> </div>
<div> <div className="flex flex-col justify-end">
<button <div className="flex justify-center gap-2">
type="submit" <button type="submit" className="btn btn-soft" onClick={disableMessageClient}>
className="btn btn-soft mr-2" Aktuelle Nachricht deaktivieren
onClick={disableMessageClient} </button>
> <button type="submit" className="btn btn-primary" disabled={isSubmitting}>
Aktuelle Nachricht deaktivieren Speichern
</button> </button>
<button </div>
type="submit"
className="btn btn-primary"
disabled={isSubmitting}
>
Speichern
</button>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -3,19 +3,23 @@ import { prisma, Prisma } from "@repo/db";
export const addMessage = async (message: Prisma.NotamCreateInput) => { export const addMessage = async (message: Prisma.NotamCreateInput) => {
try { try {
// Set all current messages with isMainMsg=true to active=false
await prisma.notam.updateMany({ await prisma.notam.updateMany({
where: { isMainMsg: true }, where: { active: true },
data: { active: false }, data: { active: false },
}); });
const showUntil = new Date(message.showUntil);
const showUntilActive = showUntil > new Date();
await prisma.notam.create({ await prisma.notam.create({
data: { data: {
message: message.message, message: message.message,
color: message.color, color: message.color,
isMainMsg: true,
active: true, active: true,
showUntil: new Date().toISOString(), wartungsmodus: message.wartungsmodus,
disableHPG: message.disableHPG,
showUntilActive,
showUntil,
}, },
}); });
} catch (error) { } catch (error) {
@@ -25,9 +29,8 @@ export const addMessage = async (message: Prisma.NotamCreateInput) => {
export const disableMessage = async () => { export const disableMessage = async () => {
try { try {
// Set all current messages with isMainMsg=true to active=false
await prisma.notam.updateMany({ await prisma.notam.updateMany({
where: { isMainMsg: true }, where: { active: true },
data: { active: false }, data: { active: false },
}); });
} catch (error) { } catch (error) {

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { MessageSquareWarning } from "lucide-react"; import { Check, MessageSquareWarning } from "lucide-react";
import { MessageForm } from "./_components/messageForm"; import { MessageForm } from "./_components/messageForm";
import { PaginatedTable } from "_components/PaginatedTable"; import { PaginatedTable } from "_components/PaginatedTable";
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
@@ -23,6 +23,7 @@ export default function MessagePage() {
</div> </div>
<PaginatedTable <PaginatedTable
prismaModel="notam" prismaModel="notam"
initialOrderBy={[{ id: "createdAt", desc: true }]}
columns={ columns={
[ [
{ {
@@ -31,19 +32,58 @@ export default function MessagePage() {
}, },
{ {
accessorKey: "color", accessorKey: "color",
header: "Status", header: "Typ",
cell: ({ row }) => { cell: ({ row }) => {
const color = row.getValue("color"); const color = row.getValue("color");
return color; return color;
}, },
}, },
{
accessorKey: "wartungsmodus",
header: "Wartungsmodus",
cell: ({ row }) => {
const wartungsmodus = row.getValue("wartungsmodus");
return wartungsmodus ? <Check /> : "";
},
},
{
accessorKey: "disableHPG",
header: "HPG deaktiviert",
cell: ({ row }) => {
const disableHPG = row.getValue("disableHPG");
return disableHPG ? <Check /> : "";
},
},
{
accessorKey: "showUntil",
header: "Zeitlimit",
cell: ({ row, cell }) => {
const showUntil = new Date(cell.getValue() as string);
const createdAt = new Date(row.getValue("createdAt") as string);
if (showUntil > createdAt) {
return showUntil.toLocaleDateString("de-DE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}
return "";
},
},
{ {
accessorKey: "createdAt", accessorKey: "createdAt",
header: "Erstellt am", header: "Erstellt am",
sortDescFirst: false,
cell: ({ cell }) => { cell: ({ cell }) => {
const date = new Date(cell.getValue() as string); const date = new Date(cell.getValue() as string);
return date.toLocaleDateString(); return date.toLocaleDateString("de-DE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}, },
}, },
] as ColumnDef<Notam>[] ] as ColumnDef<Notam>[]

View File

@@ -23,9 +23,7 @@ import { useRouter } from "next/navigation";
import { handleParticipantEnrolled } from "../../../../helper/events"; import { handleParticipantEnrolled } from "../../../../helper/events";
import { eventCompleted } from "@repo/shared-components"; import { eventCompleted } from "@repo/shared-components";
import MDEditor from "@uiw/react-md-editor"; import MDEditor from "@uiw/react-md-editor";
import { DayPicker } from "react-day-picker";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { se } from "date-fns/locale";
interface ModalBtnProps { interface ModalBtnProps {
title: string; title: string;

View File

@@ -4,7 +4,6 @@ const fetchMainMessage = async () => {
return await prisma.notam.findFirst({ return await prisma.notam.findFirst({
where: { where: {
active: true, active: true,
isMainMsg: true,
}, },
}); });
}; };
@@ -15,19 +14,19 @@ export const WarningAlert = async () => {
let msgColor; let msgColor;
switch (mainMessage?.color) { switch (mainMessage?.color) {
case "WARNING": case "WARNING":
msgColor = "alert alert-soft alert-warning ml-3 py-2"; msgColor = "alert alert-soft alert-warning ml-3 py-2 flex items-center gap-2";
break; break;
case "INFO": case "INFO":
msgColor = "alert alert-soft alert-info ml-3 py-2"; msgColor = "alert alert-soft alert-info ml-3 py-2 flex items-center gap-2";
break; break;
case "SUCCESS": case "SUCCESS":
msgColor = "alert alert-soft alert-success ml-3 py-2"; msgColor = "alert alert-soft alert-success ml-3 py-2 flex items-center gap-2";
break; break;
case "ERROR": case "ERROR":
msgColor = "alert alert-error ml-3 py-2"; msgColor = "alert alert-error ml-3 py-2 flex items-center gap-2";
break; break;
default: default:
msgColor = "alert alert-soft ml-3 py-2"; msgColor = "alert alert-soft ml-3 py-2 flex items-center gap-2";
} }
if (mainMessage?.message == "" || !mainMessage) { if (mainMessage?.message == "" || !mainMessage) {

View File

@@ -0,0 +1,8 @@
/*
Warnings:
- You are about to drop the column `isMainMsg` on the `Notam` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Notam" DROP COLUMN "isMainMsg";

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Notam" ADD COLUMN "disableHPG" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "wartungsmodus" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Notam" ADD COLUMN "showUntilActive" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -8,12 +8,14 @@ enum GlobalColor {
} }
model Notam { model Notam {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
color GlobalColor color GlobalColor
message String message String
showUntil DateTime showUntil DateTime
isMainMsg Boolean showUntilActive Boolean @default(false)
active Boolean wartungsmodus Boolean @default(false)
createdAt DateTime @default(now()) disableHPG Boolean @default(false)
updatedAt DateTime @updatedAt active Boolean
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
} }