Enhance tags
This commit is contained in:
@ -1,23 +1,23 @@
|
||||
"use server";
|
||||
// "use server";
|
||||
|
||||
import { prisma } from "@/lib/prisma";
|
||||
// import { prisma } from "@/lib/prisma";
|
||||
|
||||
export async function deleteItems(itemId: string, type: string) {
|
||||
// export async function deleteItems(itemId: string, type: string) {
|
||||
|
||||
switch (type) {
|
||||
case "categories":
|
||||
await prisma.artCategory.delete({ where: { id: itemId } });
|
||||
break;
|
||||
case "tags":
|
||||
await prisma.artTag.delete({ where: { id: itemId } });
|
||||
break;
|
||||
// case "types":
|
||||
// await prisma.portfolioType.delete({ where: { id: itemId } });
|
||||
// break;
|
||||
// case "albums":
|
||||
// await prisma.portfolioAlbum.delete({ where: { id: itemId } });
|
||||
// break;
|
||||
}
|
||||
// switch (type) {
|
||||
// case "categories":
|
||||
// await prisma.artCategory.delete({ where: { id: itemId } });
|
||||
// break;
|
||||
// case "tags":
|
||||
// await prisma.artTag.delete({ where: { id: itemId } });
|
||||
// break;
|
||||
// // case "types":
|
||||
// // await prisma.portfolioType.delete({ where: { id: itemId } });
|
||||
// // break;
|
||||
// // case "albums":
|
||||
// // await prisma.portfolioAlbum.delete({ where: { id: itemId } });
|
||||
// // break;
|
||||
// }
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
// return { success: true };
|
||||
// }
|
||||
@ -1,9 +1,9 @@
|
||||
"use server"
|
||||
|
||||
import { prisma } from "@/lib/prisma"
|
||||
import { tagSchema } from "@/schemas/artworks/tagSchema"
|
||||
import { TagFormInput, tagSchema } from "@/schemas/artworks/tagSchema"
|
||||
|
||||
export async function createTag(formData: tagSchema) {
|
||||
export async function createTag(formData: TagFormInput) {
|
||||
const parsed = tagSchema.safeParse(formData)
|
||||
|
||||
if (!parsed.success) {
|
||||
@ -12,25 +12,43 @@ export async function createTag(formData: tagSchema) {
|
||||
}
|
||||
|
||||
const data = parsed.data
|
||||
|
||||
const parentId = data.parentId ?? null;
|
||||
const tagSlug = data.name.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
const created = await prisma.artTag.create({
|
||||
data: {
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
description: data.description
|
||||
},
|
||||
})
|
||||
|
||||
if (data.categoryIds) {
|
||||
await prisma.artTag.update({
|
||||
where: { id: created.id },
|
||||
const created = await prisma.$transaction(async (tx) => {
|
||||
const tag = await tx.artTag.create({
|
||||
data: {
|
||||
categories: {
|
||||
set: data.categoryIds.map(id => ({ id }))
|
||||
}
|
||||
}
|
||||
name: data.name,
|
||||
slug: tagSlug,
|
||||
description: data.description,
|
||||
parentId
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (data.categoryIds) {
|
||||
await tx.artTag.update({
|
||||
where: { id: tag.id },
|
||||
data: {
|
||||
categories: {
|
||||
set: data.categoryIds.map(id => ({ id }))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (data.aliases && data.aliases.length > 0) {
|
||||
await tx.artTagAlias.createMany({
|
||||
data: data.aliases.map((alias) => ({
|
||||
tagId: tag.id,
|
||||
alias,
|
||||
})),
|
||||
skipDuplicates: true,
|
||||
});
|
||||
}
|
||||
|
||||
return tag;
|
||||
});
|
||||
|
||||
return created
|
||||
}
|
||||
40
src/actions/tags/deleteTag.ts
Normal file
40
src/actions/tags/deleteTag.ts
Normal file
@ -0,0 +1,40 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
export async function deleteTag(tagId: string) {
|
||||
const tag = await prisma.artTag.findUnique({
|
||||
where: { id: tagId },
|
||||
select: {
|
||||
id: true,
|
||||
_count: {
|
||||
select: {
|
||||
artworks: true,
|
||||
children: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!tag) {
|
||||
throw new Error("Tag not found.");
|
||||
}
|
||||
|
||||
if (tag._count.artworks > 0) {
|
||||
throw new Error("Cannot delete tag: it is used by artworks.");
|
||||
}
|
||||
|
||||
if (tag._count.children > 0) {
|
||||
throw new Error("Cannot delete tag: it has child tags.");
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.artTagAlias.deleteMany({ where: { tagId } });
|
||||
await tx.artTag.delete({ where: { id: tagId } });
|
||||
});
|
||||
|
||||
revalidatePath("/tags");
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
17
src/actions/tags/isDescendant.ts
Normal file
17
src/actions/tags/isDescendant.ts
Normal file
@ -0,0 +1,17 @@
|
||||
"use server"
|
||||
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export async function isDescendant(tagId: string, possibleAncestorId: string) {
|
||||
// Walk upwards from possibleAncestorId; if we hit tagId, it's a cycle.
|
||||
let current: string | null = possibleAncestorId;
|
||||
while (current) {
|
||||
if (current === tagId) return true;
|
||||
const t = await prisma.artTag.findUnique({
|
||||
where: { id: current },
|
||||
select: { parentId: true },
|
||||
});
|
||||
current = t?.parentId ?? null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
"use server"
|
||||
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { tagSchema } from '@/schemas/artworks/tagSchema';
|
||||
import { z } from 'zod/v4';
|
||||
import { TagFormInput, tagSchema } from '@/schemas/artworks/tagSchema';
|
||||
import { isDescendant } from './isDescendant';
|
||||
|
||||
export async function updateTag(id: string, rawData: z.infer<typeof tagSchema>) {
|
||||
export async function updateTag(id: string, rawData: TagFormInput) {
|
||||
const parsed = tagSchema.safeParse(rawData)
|
||||
|
||||
if (!parsed.success) {
|
||||
@ -14,25 +14,60 @@ export async function updateTag(id: string, rawData: z.infer<typeof tagSchema>)
|
||||
|
||||
const data = parsed.data
|
||||
|
||||
const updated = await prisma.artTag.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
description: data.description
|
||||
},
|
||||
})
|
||||
const parentId = data.parentId ?? null;
|
||||
const tagSlug = data.name.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
if (data.categoryIds) {
|
||||
await prisma.artTag.update({
|
||||
where: { id: id },
|
||||
data: {
|
||||
categories: {
|
||||
set: data.categoryIds.map(id => ({ id }))
|
||||
}
|
||||
}
|
||||
});
|
||||
if (parentId === id) {
|
||||
throw new Error("A tag cannot be its own parent.");
|
||||
}
|
||||
|
||||
if (parentId) {
|
||||
const cycle = await isDescendant(id, parentId);
|
||||
if (cycle) throw new Error("Invalid parent tag (would create a cycle).");
|
||||
}
|
||||
|
||||
const updated = await prisma.$transaction(async (tx) => {
|
||||
const tag = await tx.artTag.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: data.name,
|
||||
slug: tagSlug,
|
||||
description: data.description,
|
||||
parentId,
|
||||
categories: data.categoryIds
|
||||
? { set: data.categoryIds.map((cid) => ({ id: cid })) }
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const existing = await tx.artTagAlias.findMany({
|
||||
where: { tagId: id },
|
||||
select: { id: true, alias: true },
|
||||
});
|
||||
|
||||
const desired = new Set((data.aliases ?? []).map((a) => a));
|
||||
const existingSet = new Set(existing.map((a) => a.alias));
|
||||
|
||||
const toCreate = Array.from(desired).filter((a) => !existingSet.has(a));
|
||||
const toDeleteIds = existing
|
||||
.filter((a) => !desired.has(a.alias))
|
||||
.map((a) => a.id);
|
||||
|
||||
if (toDeleteIds.length > 0) {
|
||||
await tx.artTagAlias.deleteMany({
|
||||
where: { id: { in: toDeleteIds } },
|
||||
});
|
||||
}
|
||||
|
||||
if (toCreate.length > 0) {
|
||||
await tx.artTagAlias.createMany({
|
||||
data: toCreate.map((alias) => ({ tagId: id, alias })),
|
||||
skipDuplicates: true,
|
||||
});
|
||||
}
|
||||
|
||||
return tag;
|
||||
});
|
||||
|
||||
return updated
|
||||
}
|
||||
Reference in New Issue
Block a user