Changelog-Seite, option zum verstecken von Einträgen auf dieser

This commit is contained in:
PxlLoewe
2026-01-06 12:19:10 +01:00
parent dd39331c1a
commit c5c3bc0775
8 changed files with 226 additions and 97 deletions

View File

@@ -23,6 +23,7 @@ export const ChangelogForm = ({ changelog }: { changelog?: Changelog }) => {
title: changelog?.title || "", title: changelog?.title || "",
text: changelog?.text || "", text: changelog?.text || "",
previewImage: changelog?.previewImage || "", // Changed to accept a URL as a string previewImage: changelog?.previewImage || "", // Changed to accept a URL as a string
showOnChangelogPage: changelog?.showOnChangelogPage || true,
}, },
}); });
const [skipUserUpdate, setSkipUserUpdate] = useState(false); const [skipUserUpdate, setSkipUserUpdate] = useState(false);
@@ -84,6 +85,7 @@ export const ChangelogForm = ({ changelog }: { changelog?: Changelog }) => {
placeholder="Titel (vX.X.X)" placeholder="Titel (vX.X.X)"
className="input-sm" className="input-sm"
/> />
<Input <Input
form={form} form={form}
label="Bild-URL" label="Bild-URL"
@@ -146,6 +148,16 @@ export const ChangelogForm = ({ changelog }: { changelog?: Changelog }) => {
</span> </span>
</label> </label>
)} )}
<label className="label mx-6 mt-6 w-full cursor-pointer">
<input
type="checkbox"
className={cn("toggle")}
{...form.register("showOnChangelogPage", {})}
/>
<span className={cn("label-text w-full text-left")}>
Auf der Changelog-Seite anzeigen
</span>
</label>
<div className="card-body"> <div className="card-body">
<div className="flex w-full gap-4"> <div className="flex w-full gap-4">
<Button <Button

View File

@@ -1,23 +1,23 @@
"use client"; "use client";
import { DatabaseBackupIcon } from "lucide-react"; import { Check, Cross, DatabaseBackupIcon } from "lucide-react";
import { PaginatedTable } from "../../../_components/PaginatedTable"; import { PaginatedTable } from "../../../_components/PaginatedTable";
import Link from "next/link"; import Link from "next/link";
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
import { Keyword, Prisma } from "@repo/db"; import { Changelog, Keyword, Prisma } from "@repo/db";
export default () => { export default () => {
return ( return (
<> <>
<PaginatedTable <PaginatedTable
stickyHeaders stickyHeaders
initialOrderBy={[{ id: "title", desc: true }]} initialOrderBy={[{ id: "createdAt", desc: true }]}
prismaModel="changelog" prismaModel="changelog"
showSearch showSearch
getFilter={(search) => getFilter={(search) =>
({ ({
OR: [ OR: [
{ title: { contains: search, mode: "insensitive" } }, { title: { contains: search, mode: "insensitive" } },
{ description: { contains: search, mode: "insensitive" } }, { text: { contains: search, mode: "insensitive" } },
], ],
}) as Prisma.ChangelogWhereInput }) as Prisma.ChangelogWhereInput
} }
@@ -27,6 +27,16 @@ export default () => {
header: "Title", header: "Title",
accessorKey: "title", accessorKey: "title",
}, },
{
header: "Auf Changelog Seite anzeigen",
accessorKey: "showOnChangelogPage",
cell: ({ row }) => (row.original.showOnChangelogPage ? <Check /> : <Cross />),
},
{
header: "Erstellt am",
accessorKey: "createdAt",
cell: ({ row }) => new Date(row.original.createdAt).toLocaleDateString(),
},
{ {
header: "Aktionen", header: "Aktionen",
cell: ({ row }) => ( cell: ({ row }) => (
@@ -37,7 +47,7 @@ export default () => {
</div> </div>
), ),
}, },
] as ColumnDef<Keyword>[] ] as ColumnDef<Changelog>[]
} }
leftOfSearch={ leftOfSearch={
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">

View File

@@ -0,0 +1,68 @@
"use client";
import MDEditor from "@uiw/react-md-editor";
import Image from "next/image";
export type TimelineEntry = {
id: number;
title: string;
text: string;
previewImage?: string | null;
createdAt: string;
};
const formatReleaseDate = (value: string) =>
new Intl.DateTimeFormat("en-GB", {
day: "2-digit",
month: "short",
year: "numeric",
}).format(new Date(value));
export const ChangelogTimeline = ({ entries }: { entries: TimelineEntry[] }) => {
if (!entries.length)
return <p className="text-base-content/70">Es sind noch keine Changelog-Einträge vorhanden.</p>;
return (
<div className="relative mt-6 pl-6">
<div className="bg-base-300 absolute bottom-0 left-2 top-0 w-px" aria-hidden />
<div className="space-y-8">
{entries.map((entry, idx) => (
<article key={entry.id ?? `${entry.title}-${idx}`} className="relative pl-4">
<div className="bg-primary ring-base-100 absolute -left-[9px] top-3 h-4 w-4 rounded-full ring-4" />
<div className="bg-base-200/80 rounded-xl p-5 shadow">
<div className="flex flex-col gap-1 text-left md:flex-row md:justify-between">
<div>
<h3 className="text-lg font-semibold leading-tight">{entry.title}</h3>
<p className="text-base-content/60 text-sm">
Release Date: {formatReleaseDate(entry.createdAt)}
</p>
</div>
{entry.previewImage && (
<div className="absolute right-5 top-5 md:pl-4">
<Image
src={entry.previewImage}
width={300}
height={300}
alt={`${entry.title} preview`}
className="mt-3 max-w-[300px] rounded-lg object-cover md:mt-0"
/>
</div>
)}
</div>
<div className="text-base-content/80 text-left" data-color-mode="dark">
<MDEditor.Markdown
source={entry.text}
style={{
backgroundColor: "transparent",
fontSize: "0.95rem",
}}
/>
</div>
</div>
</article>
))}
</div>
</div>
);
};

View File

@@ -0,0 +1,26 @@
import { prisma } from "@repo/db";
import { ChangelogTimeline } from "./_components/Timeline";
import { ActivityLogIcon } from "@radix-ui/react-icons";
export default async function Page() {
const changelog = await prisma.changelog.findMany({
where: { showOnChangelogPage: true },
orderBy: { createdAt: "desc" },
});
const entries = changelog.map((entry) => ({
...entry,
createdAt: entry.createdAt.toISOString(),
}));
return (
<>
<div className="w-full px-4">
<p className="flex items-center gap-2 text-left text-2xl font-semibold">
<ActivityLogIcon className="h-5 w-5" /> Changelog
</p>
</div>
<ChangelogTimeline entries={entries} />
</>
);
}

View File

@@ -6,13 +6,15 @@ import {
RocketIcon, RocketIcon,
ReaderIcon, ReaderIcon,
DownloadIcon, DownloadIcon,
UpdateIcon,
ActivityLogIcon,
} from "@radix-ui/react-icons"; } from "@radix-ui/react-icons";
import Link from "next/link"; import Link from "next/link";
import { WarningAlert } from "./ui/PageAlert"; import { WarningAlert } from "./ui/PageAlert";
import { getServerSession } from "api/auth/[...nextauth]/auth"; import { getServerSession } from "api/auth/[...nextauth]/auth";
import { Error } from "./Error"; import { Error } from "./Error";
import Image from "next/image"; import Image from "next/image";
import { Plane, Radar, Workflow } from "lucide-react"; import { Loader, Plane, Radar, Workflow } from "lucide-react";
import { BookingButton } from "./BookingButton"; import { BookingButton } from "./BookingButton";
export const VerticalNav = async () => { export const VerticalNav = async () => {
@@ -22,7 +24,8 @@ export const VerticalNav = async () => {
return p.startsWith("ADMIN"); return p.startsWith("ADMIN");
}); });
return ( return (
<ul className="menu bg-base-300 w-64 flex-nowrap rounded-lg p-3 font-semibold shadow-md"> <ul className="menu bg-base-300 flex w-64 flex-nowrap justify-between rounded-lg p-3 font-semibold shadow-md">
<div className="border-none">
<li> <li>
<Link href="/"> <Link href="/">
<HomeIcon /> Dashboard <HomeIcon /> Dashboard
@@ -109,6 +112,13 @@ export const VerticalNav = async () => {
</details> </details>
</li> </li>
)} )}
</div>
<li>
<Link href="/changelog">
<ActivityLogIcon />
Changelog
</Link>
</li>
</ul> </ul>
); );
}; };

View File

@@ -3,7 +3,7 @@
/* const nextConfig = removeImports({}); */ /* const nextConfig = removeImports({}); */
const nextConfig = { const nextConfig = {
images: { images: {
domains: ["cdn.discordapp.com"], domains: ["cdn.discordapp.com", "nextcloud.virtualairrescue.com"],
}, },
}; };

View File

@@ -4,4 +4,5 @@ model Changelog {
previewImage String? previewImage String?
text String text String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
showOnChangelogPage Boolean @default(true)
} }

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Changelog" ADD COLUMN "showOnChangelogPage" BOOLEAN NOT NULL DEFAULT true;