Refactor Study Desk application structure

This commit is contained in:
Elijah 2026-08-07 19:31:23 -07:00
parent faaccf8a7e
commit 089439ed90
145 changed files with 8087 additions and 3412 deletions

View file

@ -1,7 +1,7 @@
import { prisma } from "@/lib/db";
export async function getShareLink(token: string) {
return prisma.shareLink.findUnique({
const link = await prisma.shareLink.findUnique({
where: { id: token },
include: {
deck: {
@ -47,6 +47,30 @@ export async function getShareLink(token: string) {
}
},
});
if (!link) return null;
const targets = [link.deck, link.quizSet, link.group].filter(Boolean);
const agrees =
(link.targetType === "DECK" && Boolean(link.deck)) ||
(link.targetType === "QUIZ" && Boolean(link.quizSet)) ||
(link.targetType === "GROUP" && Boolean(link.group));
if (targets.length !== 1 || !agrees) return null;
if (!link.group) return link;
return {
...link,
group: {
...link.group,
decks: link.group.decks.filter(
(deck) =>
link.group?.type === "DECK" &&
deck.class.slug === link.group.class.slug
),
quizSets: link.group.quizSets.filter(
(quiz) =>
link.group?.type === "QUIZ" &&
quiz.class.slug === link.group.class.slug
),
},
};
}
/**
@ -54,9 +78,10 @@ export async function getShareLink(token: string) {
* metadata without loading every card, question and option.
*/
export async function getShareLinkMeta(token: string) {
return prisma.shareLink.findUnique({
const link = await prisma.shareLink.findUnique({
where: { id: token },
select: {
targetType: true,
deck: {
select: {
name: true,
@ -79,17 +104,53 @@ export async function getShareLinkMeta(token: string) {
type: true,
class: { select: { slug: true, name: true } },
decks: {
select: { id: true, name: true, description: true, _count: { select: { cards: true } } },
select: {
id: true,
name: true,
description: true,
class: { select: { slug: true, name: true } },
_count: { select: { cards: true } },
},
orderBy: { sortOrder: "asc" },
},
quizSets: {
select: { id: true, name: true, description: true, _count: { select: { questions: true } } },
select: {
id: true,
name: true,
description: true,
class: { select: { slug: true, name: true } },
_count: { select: { questions: true } },
},
orderBy: { sortOrder: "asc" },
},
},
},
},
});
if (!link) return null;
const targets = [link.deck, link.quizSet, link.group].filter(Boolean);
const agrees =
(link.targetType === "DECK" && Boolean(link.deck)) ||
(link.targetType === "QUIZ" && Boolean(link.quizSet)) ||
(link.targetType === "GROUP" && Boolean(link.group));
if (targets.length !== 1 || !agrees) return null;
if (!link.group) return link;
return {
...link,
group: {
...link.group,
decks: link.group.decks.filter(
(deck) =>
link.group?.type === "DECK" &&
deck.class.slug === link.group.class.slug
),
quizSets: link.group.quizSets.filter(
(quiz) =>
link.group?.type === "QUIZ" &&
quiz.class.slug === link.group.class.slug
),
},
};
}
export async function getShareLinkForContent(targetType: "DECK" | "QUIZ" | "GROUP", contentId: string) {
@ -103,25 +164,47 @@ export async function getShareLinkForContent(targetType: "DECK" | "QUIZ" | "GROU
}
export async function toggleShareLink(targetType: "DECK" | "QUIZ" | "GROUP", contentId: string) {
const existing = await getShareLinkForContent(targetType, contentId);
if (existing) {
// Disable sharing
await prisma.shareLink.delete({ where: { id: existing.id } });
return null;
}
// Enable sharing
return prisma.shareLink.create({
data: {
targetType,
deckId: targetType === "DECK" ? contentId : null,
quizSetId: targetType === "QUIZ" ? contentId : null,
groupId: targetType === "GROUP" ? contentId : null,
},
return prisma.$transaction(async (transaction) => {
const target =
targetType === "DECK"
? await transaction.deck.findUnique({
where: { id: contentId },
select: { id: true },
})
: targetType === "QUIZ"
? await transaction.quizSet.findUnique({
where: { id: contentId },
select: { id: true },
})
: await transaction.materialGroup.findUnique({
where: { id: contentId },
select: { id: true },
});
if (!target) throw new ShareTargetNotFoundError("Share target not found");
const existing =
targetType === "DECK"
? await transaction.shareLink.findUnique({ where: { deckId: contentId } })
: targetType === "QUIZ"
? await transaction.shareLink.findUnique({ where: { quizSetId: contentId } })
: await transaction.shareLink.findUnique({ where: { groupId: contentId } });
if (existing) {
await transaction.shareLink.delete({ where: { id: existing.id } });
return null;
}
return transaction.shareLink.create({
data: {
targetType,
deckId: targetType === "DECK" ? contentId : null,
quizSetId: targetType === "QUIZ" ? contentId : null,
groupId: targetType === "GROUP" ? contentId : null,
},
});
});
}
export class ShareTargetNotFoundError extends Error {}
export async function isContentSharedViaGroup(targetType: "DECK" | "QUIZ", contentId: string) {
let groupId: string | null = null;