Refactor portfolio

This commit is contained in:
2025-07-21 23:45:39 +02:00
parent 312b2c2f94
commit a8d5dbaa09
31 changed files with 1111 additions and 30 deletions

View File

@ -47,10 +47,10 @@ export default function EditImageForm({ image, categories, tags, types }:
defaultValues: {
fileKey: image.fileKey,
originalFile: image.originalFile,
nsfw: image.nsfw ?? false,
published: image.nsfw ?? false,
setAsHeader: image.setAsHeader ?? false,
name: image.name,
nsfw: image.nsfw ?? false,
published: image.published ?? false,
setAsHeader: image.setAsHeader ?? false,
altText: image.altText || "",
description: image.description || "",
@ -72,7 +72,7 @@ export default function EditImageForm({ image, categories, tags, types }:
const updatedImage = await updateImage(values, image.id)
if (updatedImage) {
toast.success("Image updated")
router.push(`/portfolio`)
router.push(`/portfolio/images`)
}
}

View File

@ -0,0 +1,97 @@
"use client"
import { PortfolioType } from "@/generated/prisma";
import { usePathname, useRouter } from "next/navigation";
export default function FilterBar({
types,
currentType,
currentPublished,
}: {
types: PortfolioType[];
currentType: string;
currentPublished: string;
}) {
const router = useRouter();
const pathname = usePathname();
const searchParams = new URLSearchParams();
const setFilter = (key: string, value: string) => {
if (value !== "all") {
searchParams.set(key, value);
} else {
searchParams.delete(key);
}
router.push(`${pathname}?${searchParams.toString()}`);
};
return (
<div className="flex flex-wrap gap-4 border-b pb-4">
{/* Type Filter */}
<div className="flex gap-2 items-center flex-wrap">
<span className="text-sm font-medium text-muted-foreground">Type:</span>
<FilterButton
active={currentType === "all"}
label="All"
onClick={() => setFilter("type", "all")}
/>
{types.map((type) => (
<FilterButton
key={type.id}
active={currentType === type.id}
label={type.name}
onClick={() => setFilter("type", type.id)}
/>
))}
<FilterButton
active={currentType === "none"}
label="No Type"
onClick={() => setFilter("type", "none")}
/>
</div>
{/* Published Filter */}
<div className="flex gap-2 items-center flex-wrap">
<span className="text-sm font-medium text-muted-foreground">Status:</span>
<FilterButton
active={currentPublished === "all"}
label="All"
onClick={() => setFilter("published", "all")}
/>
<FilterButton
active={currentPublished === "published"}
label="Published"
onClick={() => setFilter("published", "published")}
/>
<FilterButton
active={currentPublished === "unpublished"}
label="Unpublished"
onClick={() => setFilter("published", "unpublished")}
/>
</div>
</div>
);
}
function FilterButton({
active,
label,
onClick,
}: {
active: boolean;
label: string;
onClick: () => void;
}) {
return (
<button
onClick={onClick}
className={`px-3 py-1 rounded text-sm border ${active
? "bg-primary text-white border-primary"
: "bg-muted text-muted-foreground hover:bg-muted/70 border-muted"
}`}
>
{label}
</button>
);
}

View File

@ -1,5 +1,6 @@
"use client";
import { deleteImage } from "@/actions/_portfolio/edit/deleteImage";
import { sortImages } from "@/actions/portfolio/images/sortImages";
import { SortableItem } from "@/components/sort/items/SortableItem";
import SortableList from "@/components/sort/lists/SortableList";
@ -24,6 +25,10 @@ export default function ImageList({ images }: { images: PortfolioImage[] }) {
await sortImages(items);
};
const handleDelete = (id: string) => {
deleteImage(id);
};
if (!isMounted) return null;
return (
@ -48,6 +53,7 @@ export default function ImageList({ images }: { images: PortfolioImage[] }) {
type: 'image'
}
}
onDelete={() => handleDelete(image.id)}
/>
);
}}