Enhance RustyCMS: Update .gitignore to include demo assets, improve admin UI dependency management, and add new translations for asset management. Implement asset date filtering and enhance content forms with asset previews. Introduce caching mechanisms for improved performance and add support for draft status in content entries.

This commit is contained in:
Peter Meier
2026-03-12 16:03:26 +01:00
parent 7795a238e1
commit 22b4367c47
24 changed files with 900 additions and 131 deletions

View File

@@ -9,7 +9,8 @@ import { useTranslations } from "next-intl";
import { fetchCollections, fetchContentList } from "@/lib/api";
import { LocaleSwitcher } from "./LocaleSwitcher";
const navLinkClass = "inline-flex items-center gap-2 rounded-lg px-3 py-2.5 text-sm font-medium min-h-[44px] md:min-h-0 md:py-2";
const navLinkClass =
"inline-flex items-center gap-2 rounded-lg px-3 py-2.5 text-sm font-medium min-h-[44px] md:min-h-0 md:py-2";
type SidebarProps = {
locale: string;
@@ -35,7 +36,7 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
c.name.toLowerCase().includes(q) ||
(c.category?.toLowerCase().includes(q) ?? false) ||
(c.tags?.some((t) => t.toLowerCase().includes(q)) ?? false) ||
(c.description?.toLowerCase().includes(q) ?? false)
(c.description?.toLowerCase().includes(q) ?? false),
);
}, [data?.collections, search]);
@@ -54,15 +55,19 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
const isDrawer = typeof onClose === "function";
const asideClass =
"flex h-screen flex-col overflow-hidden border-r border-accent-200/50 bg-gradient-to-b from-violet-50/95 via-accent-50/90 to-amber-50/85 shadow-[2px_0_16px_-2px_rgba(225,29,72,0.06)] " +
"relative flex h-screen flex-col overflow-hidden border-r border-accent-200/50 bg-gradient-to-b from-violet-50/95 via-accent-50/90 to-amber-50/85 shadow-[2px_0_16px_-2px_rgba(225,29,72,0.06)] " +
(isDrawer
? "fixed left-0 top-0 z-40 w-72 max-w-[85vw] transition-transform duration-200 ease-out md:relative md:z-auto md:w-56 md:shrink-0 md:translate-x-0 " +
? "fixed left-0 top-0 z-40 w-72 max-w-[85vw] transition-transform duration-200 ease-out md:relative md:left-auto md:top-auto md:z-auto md:h-full md:w-full md:max-w-none md:translate-x-0 " +
(mobileOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0")
: "w-56 shrink-0");
: "w-full");
return (
<aside className={asideClass}>
<nav className="flex flex-1 min-h-0 flex-col p-4">
<div
className="pointer-events-none absolute inset-0 bg-repeat opacity-10"
aria-hidden
/>
<nav className="relative flex flex-1 min-h-0 flex-col p-4">
{/* Brand / logo row */}
<div className="flex shrink-0 items-center gap-2 pb-3">
<Link
@@ -73,7 +78,9 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
<span className="flex size-9 shrink-0 items-center justify-center rounded-md bg-accent-200/80 text-accent-800">
<Icon icon="mdi:cog-outline" className="size-5" aria-hidden />
</span>
<span className="truncate text-lg font-bold tracking-tight">RustyCMS</span>
<span className="truncate text-lg font-bold tracking-tight">
RustyCMS
</span>
</Link>
{isDrawer && (
<button
@@ -92,7 +99,11 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
onClick={onClose}
className={`${navLinkClass} font-bold ${pathname === "/" ? "bg-accent-200/70 text-gray-900" : "text-gray-700 hover:bg-accent-100/80 hover:text-gray-900"}`}
>
<Icon icon="mdi:view-dashboard-outline" className="size-5 shrink-0" aria-hidden />
<Icon
icon="mdi:view-dashboard-outline"
className="size-5 shrink-0"
aria-hidden
/>
{t("dashboard")}
</Link>
<Link
@@ -100,7 +111,11 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
onClick={onClose}
className={`${navLinkClass} ${pathname?.startsWith("/admin/types") ? "bg-accent-200/70 text-gray-900" : "text-gray-700 hover:bg-accent-100/80 hover:text-gray-900"}`}
>
<Icon icon="mdi:shape-outline" className="size-5 shrink-0" aria-hidden />
<Icon
icon="mdi:shape-outline"
className="size-5 shrink-0"
aria-hidden
/>
{t("types")}
</Link>
<Link
@@ -108,7 +123,11 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
onClick={onClose}
className={`${navLinkClass} ${pathname?.startsWith("/assets") ? "bg-accent-200/70 text-gray-900" : "text-gray-700 hover:bg-accent-100/80 hover:text-gray-900"}`}
>
<Icon icon="mdi:image-multiple-outline" className="size-5 shrink-0" aria-hidden />
<Icon
icon="mdi:image-multiple-outline"
className="size-5 shrink-0"
aria-hidden
/>
{t("assets")}
</Link>
</div>
@@ -125,18 +144,23 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
</div>
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
{isLoading && (
<div className="px-3 py-2 text-sm text-gray-400">{t("loading")}</div>
<div className="px-3 py-2 text-sm text-gray-400">
{t("loading")}
</div>
)}
{error && (
<div className="px-3 py-2 text-sm text-red-600">
{t("errorLoading")}
</div>
)}
{!isLoading && !error && search.trim() && filteredCollections.length === 0 && (
<div className="px-3 py-2 text-sm text-gray-500">
{t("noResults", { query: search.trim() })}
</div>
)}
{!isLoading &&
!error &&
search.trim() &&
filteredCollections.length === 0 && (
<div className="px-3 py-2 text-sm text-gray-500">
{t("noResults", { query: search.trim() })}
</div>
)}
{filteredCollections.map((c, i) => {
const href = `/content/${c.name}`;
const active = pathname === href || pathname.startsWith(href + "/");
@@ -163,11 +187,11 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
</span>
{hasMeta && (
<span className="mt-0.5 block text-xs font-normal text-gray-500">
{c.category && (
<span className="rounded bg-accent-200/50 px-1">
{c.category}
</span>
)}
{c.category && (
<span className="rounded bg-accent-200/50 px-1">
{c.category}
</span>
)}
{c.tags?.length ? (
<span className="ml-1">
{c.tags.slice(0, 2).join(", ")}