From a87ac03c00a91e2bba27cee815e3591dcdf3d5fa Mon Sep 17 00:00:00 2001 From: Citali Date: Sat, 12 Jul 2025 13:17:57 +0200 Subject: [PATCH] Add new booelan and stuff --- .../migration.sql | 2 + prisma/schema.prisma | 1 + src/actions/images/updateImage.ts | 4 +- src/actions/images/updateImageSortOrder.ts | 15 +++ src/app/images/page.tsx | 13 ++- src/components/images/edit/EditImageForm.tsx | 17 +++ src/components/images/list/ListImages.tsx | 87 ++++++++++---- src/components/sort/SortableImage.tsx | 108 ++++++++++++++++++ src/schemas/images/imageSchema.ts | 1 + 9 files changed, 224 insertions(+), 24 deletions(-) create mode 100644 prisma/migrations/20250711232415_image_done_check/migration.sql create mode 100644 src/actions/images/updateImageSortOrder.ts create mode 100644 src/components/sort/SortableImage.tsx diff --git a/prisma/migrations/20250711232415_image_done_check/migration.sql b/prisma/migrations/20250711232415_image_done_check/migration.sql new file mode 100644 index 0000000..0d082c1 --- /dev/null +++ b/prisma/migrations/20250711232415_image_done_check/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Image" ADD COLUMN "isDone" BOOLEAN DEFAULT false; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7f4e654..b6fe0c2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -133,6 +133,7 @@ model Image { creationYear Int? fileSize Int? creationDate DateTime? + isDone Boolean? @default(false) albumId String? artistId String? diff --git a/src/actions/images/updateImage.ts b/src/actions/images/updateImage.ts index a3057b2..021f73b 100644 --- a/src/actions/images/updateImage.ts +++ b/src/actions/images/updateImage.ts @@ -29,7 +29,8 @@ export async function updateImage( albumId, artistId, tagIds, - categoryIds + categoryIds, + isDone } = validated.data; const updatedImage = await prisma.image.update({ @@ -49,6 +50,7 @@ export async function updateImage( creationDate, albumId, artistId, + isDone } }); diff --git a/src/actions/images/updateImageSortOrder.ts b/src/actions/images/updateImageSortOrder.ts new file mode 100644 index 0000000..a389926 --- /dev/null +++ b/src/actions/images/updateImageSortOrder.ts @@ -0,0 +1,15 @@ +'use server'; + +import prisma from "@/lib/prisma"; +import { SortableItem } from "@/types/SortableItem"; + +export async function updateImageSortOrder(items: SortableItem[]) { + await Promise.all( + items.map(item => + prisma.image.update({ + where: { id: item.id }, + data: { sortIndex: item.sortIndex }, + }) + ) + ); +} diff --git a/src/app/images/page.tsx b/src/app/images/page.tsx index c081641..faa5010 100644 --- a/src/app/images/page.tsx +++ b/src/app/images/page.tsx @@ -4,7 +4,18 @@ import { PlusCircleIcon } from "lucide-react"; import Link from "next/link"; export default async function ImagesPage() { - const images = await prisma.image.findMany({ orderBy: { imageName: "asc" } }); + const images = await prisma.image.findMany( + { + orderBy: [ + { album: { sortIndex: 'asc' } }, + { sortIndex: 'asc' }, + { creationDate: 'desc' }, + { imageName: 'asc' }], + include: { + album: { include: { gallery: true } } + } + } + ); return (
diff --git a/src/components/images/edit/EditImageForm.tsx b/src/components/images/edit/EditImageForm.tsx index d6bcf00..f9f7e76 100644 --- a/src/components/images/edit/EditImageForm.tsx +++ b/src/components/images/edit/EditImageForm.tsx @@ -80,6 +80,7 @@ export default function EditImageForm({ image, albums, artists, categories, tags creationYear: image.creationYear || undefined, fileSize: image.fileSize || undefined, creationDate: image.creationDate ? new Date(image.creationDate) : undefined, + isDone: image.isDone || false, artistId: image.artist?.id || undefined, albumId: image.album?.id || undefined, @@ -477,6 +478,22 @@ export default function EditImageForm({ image, albums, artists, categories, tags }} /> + ( + +
+ Image Done + Is all image data correct and image is ready to be published? +
+ + + +
+ )} + /> +
diff --git a/src/components/images/list/ListImages.tsx b/src/components/images/list/ListImages.tsx index d3f3fa8..73aaab9 100644 --- a/src/components/images/list/ListImages.tsx +++ b/src/components/images/list/ListImages.tsx @@ -1,27 +1,70 @@ -// "use client" +"use client" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Image } from "@/generated/prisma"; -import NetImage from "next/image"; -import Link from "next/link"; +import { updateImageSortOrder } from "@/actions/images/updateImageSortOrder"; +import { SortableImage } from "@/components/sort/SortableImage"; +import { SortableList } from "@/components/sort/SortableList"; +import { Album, Gallery, Image } from "@/generated/prisma"; +import { SortableItem } from "@/types/SortableItem"; +import { useEffect, useState } from "react"; + +type ImagesWithItems = Image & { + album: Album & { + gallery: Gallery | null + } | null +}; + +export default function ListImages({ images }: { images: ImagesWithItems[] }) { + const [isMounted, setIsMounted] = useState(false); + + useEffect(() => { + setIsMounted(true); + }, []); + + const sortableItems: SortableItem[] = images.map(image => ({ + id: image.id, + sortIndex: image.sortIndex, + label: image.imageName, + })); + + const handleSortDefault = async () => { + const sorted = [...sortableItems] + .sort((a, b) => a.label.localeCompare(b.label)) + .map((item, index) => ({ ...item, sortIndex: index * 10 })); + await updateImageSortOrder(sorted); + }; + + const handleReorder = async (items: SortableItem[]) => { + await updateImageSortOrder(items); + }; + + if (!isMounted) return null; -export default function ListImages({ images }: { images: Image[] }) { return ( -
- {images.map((image) => ( -
- - - - {image.imageName} - - - - - - -
- ))} -
+ { + const image = images.find(g => g.id === item.id)!; + return ( + + ); + }} + /> ); } \ No newline at end of file diff --git a/src/components/sort/SortableImage.tsx b/src/components/sort/SortableImage.tsx new file mode 100644 index 0000000..623be35 --- /dev/null +++ b/src/components/sort/SortableImage.tsx @@ -0,0 +1,108 @@ +'use client'; + +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import clsx from 'clsx'; +import { CheckCircle, Circle, GripVertical } from 'lucide-react'; +import NextImage from 'next/image'; +import Link from 'next/link'; + +type SortableCardItemProps = { + id: string; + item: { + id: string; + name: string; + fileKey: string; + altText: string; + album?: string; + gallery?: string; + isDone?: boolean; + creationDate?: Date | string; + creationMonth?: number; + creationYear?: number; + }; +}; + +export function SortableImage({ id, item }: SortableCardItemProps) { + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + const href = `/images/edit/${item.id}`; + + let dateDisplay = null; + if (item.creationDate instanceof Date) { + dateDisplay = item.creationDate.toLocaleDateString('de-DE'); + } else if (typeof item.creationDate === 'string') { + const parsed = new Date(item.creationDate); + if (!isNaN(parsed.getTime())) { + dateDisplay = parsed.toLocaleDateString('de-DE'); + } + } else if ( + typeof item.creationMonth === 'number' && + typeof item.creationYear === 'number' + ) { + const date = new Date(item.creationYear, item.creationMonth); + dateDisplay = date.toLocaleDateString('en-US', { + month: 'long', + year: 'numeric', + }); + } + + return ( +
+
+ +
+ + +
+
+ +
+ + {/* Content */} +
+

{item.name}

+ + {item.album && ( +

{item.album}

+ )} + {item.gallery && ( +

{item.gallery}

+ )} + {dateDisplay && ( +

{dateDisplay}

+ )} +
+ + {/* Status icon in bottom-left corner */} +
+ {item.isDone ? ( + + ) : ( + + )} +
+
+ +
+ ); +} \ No newline at end of file diff --git a/src/schemas/images/imageSchema.ts b/src/schemas/images/imageSchema.ts index 07d62d0..a1d0f53 100644 --- a/src/schemas/images/imageSchema.ts +++ b/src/schemas/images/imageSchema.ts @@ -25,6 +25,7 @@ export const imageSchema = z.object({ creationYear: z.number().min(1900).max(2100).optional(), fileSize: z.number().optional(), creationDate: z.date().optional(), + isDone: z.boolean().optional(), albumId: z.string().optional(), artistId: z.string().optional(),