since3-portal/modules/shared/schemas/info.ts
2026-09-29 15:20:50 +02:00

111 lines
3.2 KiB
TypeScript

import * as z from "zod";
export const PRODUCT_CATEGORIES = ["website", "hosting", "email", "seo", "other"] as const;
export type ProductCategory = (typeof PRODUCT_CATEGORIES)[number];
export const PRODUCT_STATUSES = ["online", "maintenance", "offline"] as const;
export type ProductStatus = (typeof PRODUCT_STATUSES)[number];
// Beim Seitenaufruf werden nur Produkte geprüft, deren letzte Prüfung älter ist.
export const AUTO_CHECK_STALE_AFTER_MS = 5 * 60 * 1000;
export const TASK_STATUSES =["in_progress", "planned", "done"] as const;
export type TaskStatus = (typeof TASK_STATUSES)[number];
export const CATEGORY_LABELS: Record<ProductCategory, string> = {
website: "Webseite",
hosting: "Hosting",
email: "E-Mail",
seo: "SEO",
other: "Sonstiges",
};
export const PRODUCT_STATUS_LABELS: Record<ProductStatus, string> = {
online: "Operativ",
maintenance: "Wartung",
offline: "Ausfall",
};
export const DEFAULT_STATUS_MESSAGES: Record<ProductStatus, string> = {
online: "Erreichbar, keine Störungen",
maintenance: "Wartung geplant",
offline: "Nicht erreichbar",
};
export const TASK_STATUS_LABELS: Record<TaskStatus, string> = {
in_progress: "In Arbeit",
planned: "Geplant",
done: "Erledigt",
};
export type Product = {
id: string;
name: string;
category: ProductCategory;
url: string | null;
status: ProductStatus;
statusMessage: string | null;
uptime30d: number | null;
updatedAt: string;
checkable: boolean;
checkDue: boolean;
measuredUptime30d: number | null;
checks30d: number;
lastCheck: {
checkedAt: string;
ok: boolean;
responseMs: number | null;
error: string | null;
} | null;
};
export type Task = {
id: string;
name: string;
description: string | null;
status: TaskStatus;
since: string;
};
export type InternalNote = {
id: string;
body: string;
updatedAt: string;
editorName: string | null;
};
const optionalText = z
.string()
.trim()
.transform((value) => (value === "" ? null : value));
export const ProductSchema = z.object({
name: z.string().trim().min(1, { error: "Name ist erforderlich." }),
category: z.enum(PRODUCT_CATEGORIES),
url: optionalText,
status: z.enum(PRODUCT_STATUSES),
statusMessage: optionalText,
uptime30d: z
.string()
.trim()
.transform((value, ctx) => {
if (value === "") return null;
const number = Number(value.replace(",", "."));
if (!Number.isFinite(number) || number < 0 || number > 100) {
ctx.addIssue({ code: "custom", message: "Uptime muss zwischen 0 und 100 liegen." });
return z.NEVER;
}
return Math.round(number * 100) / 100;
}),
});
export const TaskSchema = z.object({
name: z.string().trim().min(1, { error: "Vorgang ist erforderlich." }),
description: optionalText,
status: z.enum(TASK_STATUSES),
since: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, { error: "Bitte ein Datum wählen." }),
});
export const NoteSchema = z.object({
body: z.string().trim().min(1, { error: "Notiz darf nicht leer sein." }).max(5000),
});