diff --git a/prisma/migrations/20250721221415_add_mosaic_sorting/migration.sql b/prisma/migrations/20250721221415_add_mosaic_sorting/migration.sql new file mode 100644 index 0000000..27bd729 --- /dev/null +++ b/prisma/migrations/20250721221415_add_mosaic_sorting/migration.sql @@ -0,0 +1,32 @@ +-- AlterTable +ALTER TABLE "PortfolioImage" ADD COLUMN "albumId" TEXT, +ADD COLUMN "needsWork" BOOLEAN NOT NULL DEFAULT false, +ALTER COLUMN "published" SET DEFAULT false; + +-- CreateTable +CREATE TABLE "PortfolioAlbum" ( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "sortIndex" INTEGER NOT NULL DEFAULT 0, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "PortfolioAlbum_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "PortfolioAlbum_name_key" ON "PortfolioAlbum"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "PortfolioAlbum_slug_key" ON "PortfolioAlbum"("slug"); + +-- CreateIndex +CREATE INDEX "PortfolioImage_typeId_year_layoutGroup_layoutOrder_idx" ON "PortfolioImage"("typeId", "year", "layoutGroup", "layoutOrder"); + +-- CreateIndex +CREATE INDEX "PortfolioImage_albumId_layoutGroup_layoutOrder_idx" ON "PortfolioImage"("albumId", "layoutGroup", "layoutOrder"); + +-- AddForeignKey +ALTER TABLE "PortfolioImage" ADD CONSTRAINT "PortfolioImage_albumId_fkey" FOREIGN KEY ("albumId") REFERENCES "PortfolioAlbum"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 315cfee..3cbb6dc 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -25,8 +25,9 @@ model PortfolioImage { originalFile String @unique name String nsfw Boolean @default(false) - published Boolean @default(true) + published Boolean @default(false) setAsHeader Boolean @default(false) + needsWork Boolean @default(false) altText String? description String? @@ -43,8 +44,10 @@ model PortfolioImage { // slug String? // fileSize Int? - typeId String? - type PortfolioType? @relation(fields: [typeId], references: [id]) + albumId String? + typeId String? + album PortfolioAlbum? @relation(fields: [albumId], references: [id]) + type PortfolioType? @relation(fields: [typeId], references: [id]) metadata ImageMetadata? @@ -52,6 +55,23 @@ model PortfolioImage { colors ImageColor[] tags PortfolioTag[] variants ImageVariant[] + + @@index([typeId, year, layoutGroup, layoutOrder]) + @@index([albumId, layoutGroup, layoutOrder]) +} + +model PortfolioAlbum { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sortIndex Int @default(0) + + name String @unique + slug String @unique + + description String? + + images PortfolioImage[] } model PortfolioType { diff --git a/src/actions/portfolio/images/updateImage.ts b/src/actions/portfolio/images/updateImage.ts index 1af6dca..2eb32cc 100644 --- a/src/actions/portfolio/images/updateImage.ts +++ b/src/actions/portfolio/images/updateImage.ts @@ -18,6 +18,7 @@ export async function updateImage( originalFile, nsfw, published, + setAsHeader, altText, description, fileType, @@ -29,6 +30,14 @@ export async function updateImage( categoryIds } = validated.data; + if(setAsHeader) { + await prisma.portfolioImage.updateMany({ + where: { setAsHeader: true }, + data: { setAsHeader: false }, + }) + } + + const updatedImage = await prisma.portfolioImage.update({ where: { id: id }, data: { @@ -36,6 +45,7 @@ export async function updateImage( originalFile, nsfw, published, + setAsHeader, altText, description, fileType, diff --git a/src/app/portfolio/images/page.tsx b/src/app/portfolio/images/page.tsx index 70aec72..2183e11 100644 --- a/src/app/portfolio/images/page.tsx +++ b/src/app/portfolio/images/page.tsx @@ -5,31 +5,50 @@ import prisma from "@/lib/prisma"; import { PlusCircleIcon } from "lucide-react"; import Link from "next/link"; -export default async function PortfolioImagesPage( - { searchParams }: - { searchParams: { type: string, published: string } } +export default async function PortfolioImagesPage({ + searchParams +}: { + searchParams?: { + type?: string; + published?: string; + groupBy?: string; + year?: string; + album?: string; + } +} ) { - const { type, published } = await searchParams; + const { + type = "all", + published = "all", + groupBy = "year", + year, + album, + } = searchParams ?? {}; - const types = await prisma.portfolioType.findMany({ - orderBy: { sortIndex: "asc" }, - }); - - const typeFilter = type ?? "all"; - const publishedFilter = published ?? "all"; + const groupMode = groupBy === "album" ? "album" : "year"; + const groupId = groupMode === "album" ? album ?? "all" : year ?? "all"; const where: Prisma.PortfolioImageWhereInput = {}; - if (typeFilter !== "all") { - where.typeId = typeFilter === "none" ? null : typeFilter; + // Filter by type + if (type !== "all") { + where.typeId = type === "none" ? null : type; } - if (publishedFilter === "published") { + // Filter by published status + if (published === "published") { where.published = true; - } else if (publishedFilter === "unpublished") { + } else if (published === "unpublished") { where.published = false; } + // Filter by group (year or album) + if (groupMode === "year" && groupId !== "all") { + where.year = parseInt(groupId); + } else if (groupMode === "album" && groupId !== "all") { + where.albumId = groupId; + } + const images = await prisma.portfolioImage.findMany( { where, @@ -37,6 +56,21 @@ export default async function PortfolioImagesPage( } ) + const [types, albums, yearsRaw] = await Promise.all([ + prisma.portfolioType.findMany({ orderBy: { sortIndex: "asc" } }), + prisma.portfolioAlbum.findMany({ orderBy: { sortIndex: "asc" } }), + prisma.portfolioImage.findMany({ + where: {}, + distinct: ['year'], + select: { year: true }, + orderBy: { year: 'desc' }, + }), + ]); + + const years = yearsRaw + .map((y) => y.year) + .filter((y): y is number => y !== null && y !== undefined); + return (
There are no images yet. Consider adding some!
}