Add sub-categories to affirmation categories

Add self-referential parent/child relation on AffirmationCategory for
   one-level hierarchy. Update AffirmationForm with two-level category and
   sub-category dropdowns, server actions with find-or-create hierarchy
   logic, AffirmationList with Parent
This commit is contained in:
NeilClack
2026-02-23 10:53:15 -06:00
parent a230a1e36d
commit 3c201f9b66
14 changed files with 573 additions and 64 deletions
@@ -10,20 +10,49 @@ export const load: PageServerLoad = async ({ locals }) => {
const affirmations = await prisma.affirmation.findMany({
include: {
solfeggioFrequency: true,
category: true
category: { include: { parent: true } }
}
});
const affirmationCategories = await prisma.affirmationCategory.findMany();
const affirmationCategories = await prisma.affirmationCategory.findMany({
include: { children: true }
});
const solfeggioFrequencies = await prisma.solfeggioFrequency.findMany();
return { user, affirmationCategories, affirmations, solfeggioFrequencies };
};
async function resolveCategory(categoryName: string, subCategoryName: string | null) {
let parent = await prisma.affirmationCategory.findFirst({
where: { name: categoryName, parentId: null }
});
if (!parent) {
parent = await prisma.affirmationCategory.create({
data: { name: categoryName }
});
}
if (!subCategoryName) return parent;
let child = await prisma.affirmationCategory.findFirst({
where: { name: subCategoryName, parentId: parent.id }
});
if (!child) {
child = await prisma.affirmationCategory.create({
data: { name: subCategoryName, parentId: parent.id }
});
}
return child;
}
export const actions: Actions = {
create: async ({ request }: RequestEvent) => {
const form = await request.formData();
const frequencyId = Number(form.get('solfeggioFrequencyId')?.toString()) || 0;
const price = Number(form.get('price')) || 0.0;
const categoryName = form.get('categoryName') as string;
const subCategoryName = (form.get('subCategoryName') as string) || null;
if (frequencyId === 0) {
return fail(400, {
@@ -39,15 +68,7 @@ export const actions: Actions = {
});
}
let category = await prisma.affirmationCategory.findFirst({ where: { name: categoryName } });
if (!category) {
category = await prisma.affirmationCategory.create({
data: {
name: categoryName
}
});
}
const category = await resolveCategory(categoryName, subCategoryName);
try {
const newAffirmation = await prisma.affirmation.create({
@@ -5,35 +5,59 @@ import { error, fail, type Actions, type RequestEvent, type ServerLoadEvent } fr
export const load: PageServerLoad = async ({ params }: ServerLoadEvent) => {
const affirmation = await prisma.affirmation.findUnique({
where: { id: params.id },
include: { solfeggioFrequency: true, category: true }
include: { solfeggioFrequency: true, category: { include: { parent: true } } }
});
const solfeggioFrequencies = await prisma.solfeggioFrequency.findMany();
const affirmationCategories = await prisma.affirmationCategory.findMany();
const affirmationCategories = await prisma.affirmationCategory.findMany({
include: { children: true }
});
if (!affirmation) {
throw error(404, 'not found');
}
console.log(affirmation);
return { affirmation, solfeggioFrequencies, affirmationCategories };
};
async function resolveCategory(categoryName: string, subCategoryName: string | null) {
let parent = await prisma.affirmationCategory.findFirst({
where: { name: categoryName, parentId: null }
});
if (!parent) {
parent = await prisma.affirmationCategory.create({
data: { name: categoryName }
});
}
if (!subCategoryName) return parent;
let child = await prisma.affirmationCategory.findFirst({
where: { name: subCategoryName, parentId: parent.id }
});
if (!child) {
child = await prisma.affirmationCategory.create({
data: { name: subCategoryName, parentId: parent.id }
});
}
return child;
}
export const actions: Actions = {
update: async ({ request }: RequestEvent) => {
const form = await request.formData();
const frequencyId = Number(form.get('solfeggioFrequencyId')?.toString()) || 0;
const price = Number(form.get('price')) || 0.0;
const categoryName = form.get('categoryName') as string;
const subCategoryName = (form.get('subCategoryName') as string) || null;
let category = await prisma.affirmationCategory.findFirst({ where: { name: categoryName } });
if (!category) {
category = await prisma.affirmationCategory.create({
data: {
name: categoryName
}
if (!categoryName) {
return fail(400, {
error: 'Category is required!',
...Object.fromEntries(form)
});
}
@@ -44,6 +68,8 @@ export const actions: Actions = {
});
}
const category = await resolveCategory(categoryName, subCategoryName);
try {
const updatedAffirmation = await prisma.affirmation.update({
where: {