diff --git a/src/actions/_items/commissions/tos/getTos.ts b/src/actions/_items/commissions/tos/getTos.ts deleted file mode 100644 index 9023ab7..0000000 --- a/src/actions/_items/commissions/tos/getTos.ts +++ /dev/null @@ -1,10 +0,0 @@ -'use server'; - -import prisma from "@/lib/prisma"; - -export async function getLatestTos(): Promise { - const tos = await prisma.termsOfService.findFirst({ - orderBy: { createdAt: 'desc' }, - }); - return tos?.markdown ?? null; -} diff --git a/src/actions/_items/commissions/tos/saveTosAction.ts b/src/actions/_items/commissions/tos/saveTosAction.ts deleted file mode 100644 index 0fb72b4..0000000 --- a/src/actions/_items/commissions/tos/saveTosAction.ts +++ /dev/null @@ -1,11 +0,0 @@ -'use server'; - -import prisma from "@/lib/prisma"; - -export async function saveTosAction(markdown: string) { - await prisma.termsOfService.create({ - data: { - markdown, - }, - }); -} \ No newline at end of file diff --git a/src/actions/_items/commissions/types/deleteType.ts b/src/actions/_items/commissions/types/deleteType.ts deleted file mode 100644 index 6577a6e..0000000 --- a/src/actions/_items/commissions/types/deleteType.ts +++ /dev/null @@ -1,19 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma" - -export async function deleteCommissionType(typeId: string) { - - await prisma.commissionTypeOption.deleteMany({ - where: { typeId }, - }) - - await prisma.commissionTypeExtra.deleteMany({ - where: { typeId }, - }) - - await prisma.commissionType.delete({ - where: { id: typeId }, - }) - -} \ No newline at end of file diff --git a/src/actions/_items/commissions/types/newType.ts b/src/actions/_items/commissions/types/newType.ts deleted file mode 100644 index 9b5b92d..0000000 --- a/src/actions/_items/commissions/types/newType.ts +++ /dev/null @@ -1,81 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma" -import { commissionTypeSchema } from "@/schemas/commissions/commissionType" - -export async function createCommissionOption(data: { name: string }) { - return await prisma.commissionOption.create({ - data: { - name: data.name, - description: "", - }, - }) -} - -export async function createCommissionExtra(data: { name: string }) { - return await prisma.commissionExtra.create({ - data: { - name: data.name, - description: "", - }, - }) -} - -export async function createCommissionCustomInput(data: { - name: string - fieldId: string -}) { - return await prisma.commissionCustomInput.create({ - data: { - name: data.name, - fieldId: data.fieldId, - }, - }) -} - -export async function createCommissionType(formData: commissionTypeSchema) { - const parsed = commissionTypeSchema.safeParse(formData) - - if (!parsed.success) { - console.error("Validation failed", parsed.error) - throw new Error("Invalid input") - } - - const data = parsed.data - - const created = await prisma.commissionType.create({ - data: { - name: data.name, - description: data.description, - options: { - create: data.options?.map((opt, index) => ({ - option: { connect: { id: opt.optionId } }, - price: opt.price, - pricePercent: opt.pricePercent, - priceRange: opt.priceRange, - sortIndex: index, - })) || [], - }, - extras: { - create: data.extras?.map((ext, index) => ({ - extra: { connect: { id: ext.extraId } }, - price: ext.price, - pricePercent: ext.pricePercent, - priceRange: ext.priceRange, - sortIndex: index, - })) || [], - }, - customInputs: { - create: data.customInputs?.map((c, index) => ({ - customInput: { connect: { id: c.customInputId } }, - label: c.label, - inputType: c.inputType, - required: c.required, - sortIndex: index, - })) || [], - }, - }, - }) - - return created -} \ No newline at end of file diff --git a/src/actions/_items/commissions/types/updateCommissionTypeSortOrder.ts b/src/actions/_items/commissions/types/updateCommissionTypeSortOrder.ts deleted file mode 100644 index 6ac70ad..0000000 --- a/src/actions/_items/commissions/types/updateCommissionTypeSortOrder.ts +++ /dev/null @@ -1,16 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma"; - -export async function updateCommissionTypeSortOrder( - ordered: { id: string; sortIndex: number }[] -) { - const updates = ordered.map(({ id, sortIndex }) => - prisma.commissionType.update({ - where: { id }, - data: { sortIndex }, - }) - ) - - await Promise.all(updates) -} \ No newline at end of file diff --git a/src/actions/_items/commissions/types/updateType.ts b/src/actions/_items/commissions/types/updateType.ts deleted file mode 100644 index 9348ab5..0000000 --- a/src/actions/_items/commissions/types/updateType.ts +++ /dev/null @@ -1,57 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma" -import { commissionTypeSchema } from "@/schemas/commissions/commissionType" -import * as z from "zod/v4" - -export async function updateCommissionType( - id: string, - rawData: z.infer -) { - const data = commissionTypeSchema.parse(rawData) - - const updated = await prisma.commissionType.update({ - where: { id }, - data: { - name: data.name, - description: data.description, - options: { - deleteMany: {}, - create: data.options?.map((opt, index) => ({ - option: { connect: { id: opt.optionId } }, - price: opt.price ?? null, - pricePercent: opt.pricePercent ?? null, - priceRange: opt.priceRange ?? null, - sortIndex: index, - })), - }, - extras: { - deleteMany: {}, - create: data.extras?.map((ext, index) => ({ - extra: { connect: { id: ext.extraId } }, - price: ext.price ?? null, - pricePercent: ext.pricePercent ?? null, - priceRange: ext.priceRange ?? null, - sortIndex: index, - })), - }, - customInputs: { - deleteMany: {}, - create: data.customInputs?.map((c, index) => ({ - customInput: { connect: { id: c.customInputId } }, - label: c.label, - inputType: c.inputType, - required: c.required, - sortIndex: index, - })) || [], - }, - }, - include: { - options: true, - extras: true, - customInputs: true, - }, - }) - - return updated -} diff --git a/src/actions/_portfolio/arttypes/createArtType.ts b/src/actions/_portfolio/arttypes/createArtType.ts deleted file mode 100644 index 91222b4..0000000 --- a/src/actions/_portfolio/arttypes/createArtType.ts +++ /dev/null @@ -1,25 +0,0 @@ -"use server" - -import prisma from '@/lib/prisma'; -import { artTypeSchema } from '@/schemas/artTypeSchema'; - -export async function createArtType(formData: artTypeSchema) { - const parsed = artTypeSchema.safeParse(formData) - - if (!parsed.success) { - console.error("Validation failed", parsed.error) - throw new Error("Invalid input") - } - - const data = parsed.data - - const created = await prisma.portfolioArtType.create({ - data: { - name: data.name, - slug: data.slug, - description: data.description - }, - }) - - return created -} \ No newline at end of file diff --git a/src/actions/_portfolio/arttypes/updateArtType.ts b/src/actions/_portfolio/arttypes/updateArtType.ts deleted file mode 100644 index a31a257..0000000 --- a/src/actions/_portfolio/arttypes/updateArtType.ts +++ /dev/null @@ -1,28 +0,0 @@ -"use server" - -import prisma from '@/lib/prisma'; -import { artTypeSchema } from '@/schemas/artTypeSchema'; -import { z } from 'zod/v4'; - -export async function updateArtType(id: string, - rawData: z.infer) { - const parsed = artTypeSchema.safeParse(rawData) - - if (!parsed.success) { - console.error("Validation failed", parsed.error) - throw new Error("Invalid input") - } - - const data = parsed.data - - const updated = await prisma.portfolioArtType.update({ - where: { id }, - data: { - name: data.name, - slug: data.slug, - description: data.description - }, - }) - - return updated -} \ No newline at end of file diff --git a/src/actions/_portfolio/arttypes/updateArtTypeSortOrder.ts b/src/actions/_portfolio/arttypes/updateArtTypeSortOrder.ts deleted file mode 100644 index 26ff02b..0000000 --- a/src/actions/_portfolio/arttypes/updateArtTypeSortOrder.ts +++ /dev/null @@ -1,15 +0,0 @@ -'use server'; - -import prisma from "@/lib/prisma"; -import { SortableItem } from "@/types/SortableItem"; - -export async function updateArtTypeSortOrder(items: SortableItem[]) { - await Promise.all( - items.map(item => - prisma.portfolioArtType.update({ - where: { id: item.id }, - data: { sortIndex: item.sortIndex }, - }) - ) - ); -} diff --git a/src/actions/_portfolio/edit/deleteImage.ts b/src/actions/_portfolio/edit/deleteImage.ts deleted file mode 100644 index 6512ca4..0000000 --- a/src/actions/_portfolio/edit/deleteImage.ts +++ /dev/null @@ -1,67 +0,0 @@ -"use server"; - -import prisma from "@/lib/prisma"; -import { s3 } from "@/lib/s3"; -import { DeleteObjectCommand } from "@aws-sdk/client-s3"; - -export async function deleteImage(imageId: string) { - const image = await prisma.portfolioImage.findUnique({ - where: { id: imageId }, - include: { - variants: true, - colors: true, - metadata: true, - tags: true, - categories: true, - }, - }); - - if (!image) throw new Error("Image not found"); - - // Delete S3 objects - for (const variant of image.variants) { - try { - await s3.send( - new DeleteObjectCommand({ - Bucket: "gaertan", - Key: variant.s3Key, - }) - ); - } catch (err) { - console.warn("Failed to delete S3 object: " + variant.s3Key + ". " + err); - } - } - - // Step 1: Delete join entries - await prisma.imageColor.deleteMany({ where: { imageId } }); - - // Colors - for (const color of image.colors) { - const count = await prisma.imageColor.count({ - where: { colorId: color.colorId }, - }); - if (count === 0) { - await prisma.color.delete({ where: { id: color.colorId } }); - } - } - - // Delete variants - await prisma.imageVariant.deleteMany({ where: { imageId } }); - - // Delete metadata - await prisma.imageMetadata.deleteMany({ where: { imageId } }); - - // Clean many-to-many tag/category joins - await prisma.portfolioImage.update({ - where: { id: imageId }, - data: { - tags: { set: [] }, - categories: { set: [] }, - }, - }); - - // Finally delete the image - await prisma.portfolioImage.delete({ where: { id: imageId } }); - - return { success: true }; -} \ No newline at end of file diff --git a/src/actions/_portfolio/edit/generateImageColors.ts b/src/actions/_portfolio/edit/generateImageColors.ts deleted file mode 100644 index edbe0e8..0000000 --- a/src/actions/_portfolio/edit/generateImageColors.ts +++ /dev/null @@ -1,66 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma"; -import { VibrantSwatch } from "@/types/VibrantSwatch"; -import { getImageBufferFromS3 } from "@/utils/getImageBufferFromS3"; -import { generateColorName, rgbToHex } from "@/utils/uploadHelper"; -import { Vibrant } from "node-vibrant/node"; - -export async function generateImageColors(imageId: string, fileKey: string, fileType?: string) { - const buffer = await getImageBufferFromS3(fileKey, fileType); - const palette = await Vibrant.from(buffer).getPalette(); - - const vibrantHexes = Object.entries(palette).map(([key, swatch]) => { - const castSwatch = swatch as VibrantSwatch | null; - const rgb = castSwatch?._rgb; - const hex = castSwatch?.hex || (rgb ? rgbToHex(rgb) : undefined); - return { type: key, hex }; - }); - - for (const { type, hex } of vibrantHexes) { - if (!hex) continue; - - const [r, g, b] = hex.match(/\w\w/g)!.map((h) => parseInt(h, 16)); - const name = generateColorName(hex); - - const color = await prisma.color.upsert({ - where: { name }, - create: { - name, - type, - hex, - red: r, - green: g, - blue: b, - }, - update: { - hex, - red: r, - green: g, - blue: b, - }, - }); - - await prisma.imageColor.upsert({ - where: { - imageId_type: { - imageId, - type, - }, - }, - create: { - imageId, - colorId: color.id, - type, - }, - update: { - colorId: color.id, - }, - }); - } - - return await prisma.imageColor.findMany({ - where: { imageId }, - include: { color: true }, - }); -} \ No newline at end of file diff --git a/src/actions/_portfolio/edit/updateImage.ts b/src/actions/_portfolio/edit/updateImage.ts deleted file mode 100644 index ee052f6..0000000 --- a/src/actions/_portfolio/edit/updateImage.ts +++ /dev/null @@ -1,74 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma"; -import { imageSchema } from "@/schemas/imageSchema"; -import * as z from "zod/v4"; - -export async function updateImage( - values: z.infer, - id: string -) { - const validated = imageSchema.safeParse(values); - if (!validated.success) { - throw new Error("Invalid image data"); - } - - const { - fileKey, - originalFile, - nsfw, - published, - altText, - description, - fileType, - name, - slug, - type, - fileSize, - creationDate, - tagIds, - categoryIds - } = validated.data; - - const updatedImage = await prisma.portfolioImage.update({ - where: { id: id }, - data: { - fileKey, - originalFile, - nsfw, - published, - altText, - description, - fileType, - name, - slug, - type, - fileSize, - creationDate, - } - }); - - if (tagIds) { - await prisma.portfolioImage.update({ - where: { id: id }, - data: { - tags: { - set: tagIds.map(id => ({ id })) - } - } - }); - } - - if (categoryIds) { - await prisma.portfolioImage.update({ - where: { id: id }, - data: { - categories: { - set: categoryIds.map(id => ({ id })) - } - } - }); - } - - return updatedImage -} \ No newline at end of file diff --git a/src/actions/_portfolio/updateImageSortOrder.ts b/src/actions/_portfolio/updateImageSortOrder.ts deleted file mode 100644 index eabf6d1..0000000 --- a/src/actions/_portfolio/updateImageSortOrder.ts +++ /dev/null @@ -1,15 +0,0 @@ -'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.portfolioImage.update({ - where: { id: item.id }, - data: { sortIndex: item.sortIndex }, - }) - ) - ); -} diff --git a/src/actions/_portfolio/upload/uploadImage.ts b/src/actions/_portfolio/upload/uploadImage.ts deleted file mode 100644 index 206cfcf..0000000 --- a/src/actions/_portfolio/upload/uploadImage.ts +++ /dev/null @@ -1,190 +0,0 @@ -"use server" - -import prisma from "@/lib/prisma"; -import { s3 } from "@/lib/s3"; -import { imageUploadSchema } from "@/schemas/imageSchema"; -import { PutObjectCommand } from "@aws-sdk/client-s3"; -import sharp from "sharp"; -import { v4 as uuidv4 } from 'uuid'; -import * as z from "zod/v4"; - -export async function uploadImage(values: z.infer) { - const imageFile = values.file[0]; - - if (!(imageFile instanceof File)) { - console.log("No image or invalid type"); - return null; - } - - const fileName = imageFile.name; - const fileType = imageFile.type; - const fileSize = imageFile.size; - const lastModified = new Date(imageFile.lastModified); - - const fileKey = uuidv4(); - - const arrayBuffer = await imageFile.arrayBuffer(); - const buffer = Buffer.from(arrayBuffer); - - const realFileType = fileType.split("/")[1]; - const originalKey = `original/${fileKey}.${realFileType}`; - const modifiedKey = `modified/${fileKey}.webp`; - const resizedKey = `resized/${fileKey}.webp`; - const thumbnailKey = `thumbnail/${fileKey}.webp`; - - const sharpData = sharp(buffer); - const metadata = await sharpData.metadata(); - - //--- Original file - await s3.send( - new PutObjectCommand({ - Bucket: "gaertan", - Key: originalKey, - Body: buffer, - ContentType: "image/" + metadata.format, - }) - ); - //--- Modified file - const modifiedBuffer = await sharp(buffer) - .toFormat('webp') - .toBuffer() - const modifiedMetadata = await sharp(modifiedBuffer).metadata(); - await s3.send( - new PutObjectCommand({ - Bucket: "gaertan", - Key: modifiedKey, - Body: modifiedBuffer, - ContentType: "image/" + modifiedMetadata.format, - }) - ); - //--- Resized file - const { width, height } = modifiedMetadata; - const targetSize = 400; - let resizeOptions; - if (width && height) { - if (height < width) { - resizeOptions = { height: targetSize }; - } else { - resizeOptions = { width: targetSize }; - } - } else { - resizeOptions = { height: targetSize }; - } - const resizedBuffer = await sharp(modifiedBuffer) - .resize({ ...resizeOptions, withoutEnlargement: true }) - .toFormat('webp') - .toBuffer(); - const resizedMetadata = await sharp(resizedBuffer).metadata(); - await s3.send( - new PutObjectCommand({ - Bucket: "gaertan", - Key: resizedKey, - Body: resizedBuffer, - ContentType: "image/" + resizedMetadata.format, - }) - ); - //--- Thumbnail file - // const thumbnailWidth = Math.min(watermarkedMetadata.width || 200, 200); - const thumbnailTargetSize = 160; - let thumbnailOptions; - if (width && height) { - if (height < width) { - thumbnailOptions = { height: thumbnailTargetSize }; - } else { - thumbnailOptions = { width: thumbnailTargetSize }; - } - } else { - thumbnailOptions = { height: thumbnailTargetSize }; - } - const thumbnailBuffer = await sharp(modifiedBuffer) - .resize({ ...thumbnailOptions, withoutEnlargement: true }) - .toFormat('webp') - .toBuffer(); - const thumbnailMetadata = await sharp(thumbnailBuffer).metadata(); - await s3.send( - new PutObjectCommand({ - Bucket: "gaertan", - Key: thumbnailKey, - Body: thumbnailBuffer, - ContentType: "image/" + thumbnailMetadata.format, - }) - ); - - const image = await prisma.portfolioImage.create({ - data: { - name: fileName, - fileKey, - originalFile: fileName, - creationDate: lastModified, - fileType: fileType, - fileSize: fileSize - }, - }); - - await prisma.imageMetadata.create({ - data: { - imageId: image.id, - format: metadata.format || "unknown", - width: metadata.width || 0, - height: metadata.height || 0, - space: metadata.space || "unknown", - channels: metadata.channels || 0, - depth: metadata.depth || "unknown", - density: metadata.density ?? undefined, - bitsPerSample: metadata.bitsPerSample ?? undefined, - isProgressive: metadata.isProgressive ?? undefined, - isPalette: metadata.isPalette ?? undefined, - hasProfile: metadata.hasProfile ?? undefined, - hasAlpha: metadata.hasAlpha ?? undefined, - autoOrientW: metadata.autoOrient?.width ?? undefined, - autoOrientH: metadata.autoOrient?.height ?? undefined, - }, - }); - - await prisma.imageVariant.createMany({ - data: [ - { - s3Key: originalKey, - type: "original", - height: metadata.height, - width: metadata.width, - fileExtension: metadata.format, - mimeType: "image/" + metadata.format, - sizeBytes: metadata.size, - imageId: image.id - }, - { - s3Key: modifiedKey, - type: "modified", - height: modifiedMetadata.height, - width: modifiedMetadata.width, - fileExtension: modifiedMetadata.format, - mimeType: "image/" + modifiedMetadata.format, - sizeBytes: modifiedMetadata.size, - imageId: image.id - }, - { - s3Key: resizedKey, - type: "resized", - height: resizedMetadata.height, - width: resizedMetadata.width, - fileExtension: resizedMetadata.format, - mimeType: "image/" + resizedMetadata.format, - sizeBytes: resizedMetadata.size, - imageId: image.id - }, - { - s3Key: thumbnailKey, - type: "thumbnail", - height: thumbnailMetadata.height, - width: thumbnailMetadata.width, - fileExtension: thumbnailMetadata.format, - mimeType: "image/" + thumbnailMetadata.format, - sizeBytes: thumbnailMetadata.size, - imageId: image.id - } - ], - }); - - return image -} \ No newline at end of file diff --git a/src/actions/portfolio/images/updateImage.ts b/src/actions/portfolio/images/updateImage.ts index 2eb32cc..af49ff9 100644 --- a/src/actions/portfolio/images/updateImage.ts +++ b/src/actions/portfolio/images/updateImage.ts @@ -16,14 +16,16 @@ export async function updateImage( const { fileKey, originalFile, + name, nsfw, published, setAsHeader, altText, description, fileType, - name, fileSize, + month, + year, creationDate, typeId, tagIds, @@ -43,14 +45,16 @@ export async function updateImage( data: { fileKey, originalFile, + name, nsfw, published, setAsHeader, altText, description, fileType, - name, fileSize, + month, + year, creationDate, typeId } diff --git a/src/app/portfolio/images/page.tsx b/src/app/portfolio/images/page.tsx index 5fdb9b9..9f7632e 100644 --- a/src/app/portfolio/images/page.tsx +++ b/src/app/portfolio/images/page.tsx @@ -1,5 +1,5 @@ -import { AdvancedMosaicGallery } from "@/components/portfolio/images/AdvancedMosaicGallery"; import FilterBar from "@/components/portfolio/images/FilterBar"; +import ImageGallery from "@/components/portfolio/images/ImageGallery"; import { Prisma } from "@/generated/prisma"; import prisma from "@/lib/prisma"; import { PlusCircleIcon } from "lucide-react"; @@ -79,7 +79,6 @@ export default async function PortfolioImagesPage({ Upload new image -
- {images && images.length > 0 ? 0 ? ({ ...img, width: 400, height: 300, }))} - /> :

No images found.

} + /> :

No images found.

} */} + {images && images.length > 0 ? + + : +

No images found.

+ }
- + ); } \ No newline at end of file diff --git a/src/app/portfolio/images/sort/page.tsx b/src/app/portfolio/images/sort/page.tsx new file mode 100644 index 0000000..6c8a563 --- /dev/null +++ b/src/app/portfolio/images/sort/page.tsx @@ -0,0 +1,73 @@ +import ImageSortGallery from "@/components/portfolio/images/ImageSortGallery"; +import { Prisma } from "@/generated/prisma"; +import prisma from "@/lib/prisma"; + +export default async function PortfolioImagesSortPage({ + searchParams +}: { + searchParams?: { + type?: string; + published?: string; + groupBy?: string; + year?: string; + album?: string; + } +}) { + const { + type = "all", + published = "all", + groupBy = "year", + year, + album, + } = await searchParams ?? {}; + + const groupMode = groupBy === "album" ? "album" : "year"; + const groupId = groupMode === "album" ? album ?? "all" : year ?? "all"; + + const where: Prisma.PortfolioImageWhereInput = {}; + + // Filter by type + if (type !== "all") { + where.typeId = type === "none" ? null : type; + } + + // Filter by published status + if (published === "published") { + where.published = true; + } 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, + orderBy: [{ sortIndex: 'asc' }], + } + ) + + return ( +
+
+ {/* {images && images.length > 0 ? ({ + ...img, + width: 400, + height: 300, + }))} + /> :

No images found.

} */} + {images && images.length > 0 ? + + : +

No images found.

+ } +
+
+ ); +} \ No newline at end of file diff --git a/src/components/portfolio/images/EditImageForm.tsx b/src/components/portfolio/images/EditImageForm.tsx index 6056eab..18b36f6 100644 --- a/src/components/portfolio/images/EditImageForm.tsx +++ b/src/components/portfolio/images/EditImageForm.tsx @@ -128,7 +128,14 @@ export default function EditImageForm({ image, categories, tags, types }: Creation Month - + + field.onChange(e.target.value === '' ? undefined : +e.target.value) + } + /> @@ -141,7 +148,14 @@ export default function EditImageForm({ image, categories, tags, types }: Creation Year - + + field.onChange(e.target.value === '' ? undefined : +e.target.value) + } + /> diff --git a/src/components/portfolio/images/FilterBar.tsx b/src/components/portfolio/images/FilterBar.tsx index b81375b..5500de0 100644 --- a/src/components/portfolio/images/FilterBar.tsx +++ b/src/components/portfolio/images/FilterBar.tsx @@ -1,6 +1,8 @@ "use client"; import { PortfolioAlbum, PortfolioType } from "@/generated/prisma"; +import { SortAscIcon } from "lucide-react"; +import Link from "next/link"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; type FilterBarProps = { @@ -25,10 +27,9 @@ export default function FilterBar({ const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); + const params = new URLSearchParams(searchParams); const setFilter = (key: string, value: string) => { - const params = new URLSearchParams(searchParams); - if (value !== "all") { params.set(key, value); } else { @@ -44,96 +45,108 @@ export default function FilterBar({ router.push(`${pathname}?${params.toString()}`); }; + const sortHref = `${pathname}/sort?${params.toString()}`; + return ( -
- {/* GroupBy Toggle */} -
- Group by: - setFilter("groupBy", "year")} - /> - setFilter("groupBy", "album")} - /> +
+
+
+ + Sort images + +
- - {/* Subnavigation for Year or Album */} -
- - {groupBy === "year" ? "Year:" : "Album:"} - - setFilter(groupBy, "all")} - /> - {groupBy === "year" && - years.map((year) => ( - setFilter("year", String(year))} - /> - ))} - {groupBy === "album" && - albums.map((album) => ( - setFilter("album", album.id)} - /> - ))} -
- - {/* Type Filter */} -
- Type: - setFilter("type", "all")} - /> - {types.map((type) => ( +
+ {/* GroupBy Toggle */} +
+ Group by: setFilter("type", type.id)} + active={groupBy === "year"} + label="Year" + onClick={() => setFilter("groupBy", "year")} /> - ))} - setFilter("type", "none")} - /> -
+ setFilter("groupBy", "album")} + /> +
+ {/* Type Filter */} +
+ Type: + setFilter("type", "all")} + /> + {types.map((type) => ( + setFilter("type", type.id)} + /> + ))} + setFilter("type", "none")} + /> +
- {/* Published Filter */} -
- Status: - setFilter("published", "all")} - /> - setFilter("published", "published")} - /> - setFilter("published", "unpublished")} - /> + {/* Published Filter */} +
+ Status: + setFilter("published", "all")} + /> + setFilter("published", "published")} + /> + setFilter("published", "unpublished")} + /> +
+
+
+ {/* Subnavigation for Year or Album */} +
+ + {groupBy === "year" ? "Year:" : "Album:"} + + setFilter(groupBy, "all")} + /> + {groupBy === "year" && + years.map((year) => ( + setFilter("year", String(year))} + /> + ))} + {groupBy === "album" && + albums.map((album) => ( + setFilter("album", album.id)} + /> + ))} +
+ ); } @@ -150,8 +163,8 @@ function FilterButton({ +
+ ) +} + +function DroplayoutGroup({ id, children }: { id: string; children: React.ReactNode }) { + const { setNodeRef, isOver } = useDroppable({ id }); + + return ( +
+ {React.Children.count(children) === 0 ? ( +

Drop images here

+ ) : ( + children + )} +
+ ); +} + +function DraggableImage({ id, fileKey }: { id: string; fileKey: string }) { + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id }) + + const style = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0.4 : 1, + } + + return ( +
+ +
+ ) +}