diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7784da7..c377a26 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -11,6 +11,17 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install dependencies + run: npm ci + + - name: Run migrations + run: | + set -a + source /mnt/volume_nyc3_01/Portal/.env + set +a + export DO_CA_CERTIFICATE=/etc/ssl/certs/do-ca-certificate.crt + npx prisma migrate deploy + - name: Build image run: podman build -t portal:latest . diff --git a/prisma/migrations/20260223163626_add_category_parent/migration.sql b/prisma/migrations/20260223163626_add_category_parent/migration.sql new file mode 100644 index 0000000..000d5e9 --- /dev/null +++ b/prisma/migrations/20260223163626_add_category_parent/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "AffirmationCategory" ADD COLUMN "parentId" INTEGER; + +-- AddForeignKey +ALTER TABLE "AffirmationCategory" ADD CONSTRAINT "AffirmationCategory_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "AffirmationCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e3eecfc..afb4ae2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -128,8 +128,11 @@ model SolfeggioFrequency { } model AffirmationCategory { - id Int @id @default(autoincrement()) - name String @unique + id Int @id @default(autoincrement()) + name String @unique + parentId Int? + parent AffirmationCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id]) + children AffirmationCategory[] @relation("CategoryHierarchy") affirmations Affirmation[] } diff --git a/src/lib/components/admin/AdminTable.svelte b/src/lib/components/admin/AdminTable.svelte index 2950893..e2a8b73 100644 --- a/src/lib/components/admin/AdminTable.svelte +++ b/src/lib/components/admin/AdminTable.svelte @@ -110,7 +110,7 @@ class="flex items-center gap-1 hover:text-gray-900" onclick={() => toggleSort(field)} > - {fieldNameToLabel(field.includes('.') ? (field.split('.').pop() ?? field) : field)} + {fieldNameToLabel(field.includes('.') ? field.split('.')[0] : field)} {getSortIndicator(field)} diff --git a/src/lib/components/forms/affirmation/AffirmationForm.svelte b/src/lib/components/forms/affirmation/AffirmationForm.svelte index 7e0c52a..9a53799 100644 --- a/src/lib/components/forms/affirmation/AffirmationForm.svelte +++ b/src/lib/components/forms/affirmation/AffirmationForm.svelte @@ -4,6 +4,8 @@ type Category = { id: string; name: string; + parentId?: number | null; + children?: Category[]; }; type Frequency = { @@ -18,6 +20,7 @@ price?: number; categoryId?: string; solfeggioFrequencyId?: string; + category?: { parentId?: number | null; parent?: { name: string } }; }; type FormData = { @@ -27,6 +30,7 @@ price?: number; categoryId?: string; categoryName?: string; + subCategoryName?: string; solfeggioFrequencyId?: string; }; @@ -48,11 +52,74 @@ submitLabel = 'Save Affirmation' }: Props = $props(); - const currentCategory = $derived( - categories.find((c : Category) => c.id === (form?.categoryId ?? affirmation.categoryId)) - ); + const topLevelCategories = $derived(categories.filter((c) => !c.parentId)); - let categoryInput = $derived(form?.categoryName ?? currentCategory?.name ?? ''); + // Determine initial parent category name from form data or existing affirmation + function getInitialCategoryName(): string { + if (form?.categoryName) return form.categoryName; + if (affirmation.category?.parent) return affirmation.category.parent.name; + if (affirmation.categoryId) { + const cat = categories.find((c) => c.id === affirmation.categoryId); + if (cat?.parentId) { + const parent = categories.find((c) => String(c.id) === String(cat.parentId)); + return parent?.name ?? ''; + } + return cat?.name ?? ''; + } + return ''; + } + + function getInitialSubCategoryName(): string { + if (form?.subCategoryName) return form.subCategoryName; + if (affirmation.categoryId) { + const cat = categories.find((c) => c.id === affirmation.categoryId); + if (cat?.parentId) return cat.name; + } + return ''; + } + + let categoryInput = $state(getInitialCategoryName()); + let showNewCategory = $state(false); + let newCategoryName = $state(''); + + let subCategoryInput = $state(getInitialSubCategoryName()); + let showNewSubCategory = $state(false); + let newSubCategoryName = $state(''); + + const selectedParent = $derived(topLevelCategories.find((c) => c.name === categoryInput)); + const subCategories = $derived(selectedParent?.children ?? []); + + function handleCategoryChange(e: Event) { + const value = (e.target as HTMLSelectElement).value; + if (value === '__new__') { + showNewCategory = true; + categoryInput = ''; + } else { + showNewCategory = false; + newCategoryName = ''; + categoryInput = value; + } + subCategoryInput = ''; + showNewSubCategory = false; + newSubCategoryName = ''; + } + + function handleSubCategoryChange(e: Event) { + const value = (e.target as HTMLSelectElement).value; + if (value === '__new__') { + showNewSubCategory = true; + subCategoryInput = ''; + } else { + showNewSubCategory = false; + newSubCategoryName = ''; + subCategoryInput = value; + } + } + + const effectiveCategoryName = $derived(showNewCategory ? newCategoryName : categoryInput); + const effectiveSubCategoryName = $derived( + showNewSubCategory ? newSubCategoryName : subCategoryInput + );
@@ -60,6 +127,9 @@ {/if} + + +
- - - - {#each categories as category} - + + + + {#if showNewCategory} + + {/if}
+ {#if categoryInput || showNewCategory} +
+ + + + {#if showNewSubCategory} + + {/if} +
+ {/if} +
@@ -145,4 +257,4 @@ > {submitLabel} - \ No newline at end of file + diff --git a/src/lib/components/ui/AffirmationList/AffirmationList.svelte b/src/lib/components/ui/AffirmationList/AffirmationList.svelte index dbe6e49..7a2a80f 100644 --- a/src/lib/components/ui/AffirmationList/AffirmationList.svelte +++ b/src/lib/components/ui/AffirmationList/AffirmationList.svelte @@ -10,9 +10,16 @@ }; category?: { name: string; + parent?: { name: string } | null; }; }; + function categoryDisplayName(category?: Affirmation['category']): string { + if (!category) return ''; + if (category.parent) return `${category.parent.name} > ${category.name}`; + return category.name; + } + type SortKey = 'name' | 'category' | 'frequency' | 'price'; type SortDirection = 'asc' | 'desc'; @@ -28,7 +35,7 @@ let sortKey = $state('name'); let sortDirection = $state('asc'); - const categories = $derived([...new Set(affirmations.map((a) => a.category?.name).filter(Boolean))].sort()); + const categories = $derived([...new Set(affirmations.map((a) => categoryDisplayName(a.category)).filter(Boolean))].sort()); const frequencies = $derived([...new Set(affirmations.map((a) => a.solfeggioFrequency?.name).filter(Boolean))].sort()); const filteredAndSorted = $derived(() => { @@ -41,14 +48,14 @@ (a) => a.name.toLowerCase().includes(searchLower) || a.content.toLowerCase().includes(searchLower) || - a.category?.name.toLowerCase().includes(searchLower) || + categoryDisplayName(a.category).toLowerCase().includes(searchLower) || a.solfeggioFrequency?.name.toLowerCase().includes(searchLower) ); } // Category filter if (categoryFilter) { - result = result.filter((a) => a.category?.name === categoryFilter); + result = result.filter((a) => categoryDisplayName(a.category) === categoryFilter); } // Frequency filter @@ -67,8 +74,8 @@ bVal = b.name.toLowerCase(); break; case 'category': - aVal = a.category?.name?.toLowerCase() ?? ''; - bVal = b.category?.name?.toLowerCase() ?? ''; + aVal = categoryDisplayName(a.category).toLowerCase(); + bVal = categoryDisplayName(b.category).toLowerCase(); break; case 'frequency': aVal = a.solfeggioFrequency?.value ?? 0; @@ -206,7 +213,7 @@ {#each filteredAndSorted() as affirmation} {affirmation.name} - {affirmation.category?.name ?? '—'} + {categoryDisplayName(affirmation.category) || '—'} {affirmation.solfeggioFrequency?.name ?? '—'} ${affirmation.price.toFixed(2)} diff --git a/src/lib/generated/prisma/internal/class.ts b/src/lib/generated/prisma/internal/class.ts index 78f59fd..4ecfadf 100644 --- a/src/lib/generated/prisma/internal/class.ts +++ b/src/lib/generated/prisma/internal/class.ts @@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = { "clientVersion": "7.2.0", "engineVersion": "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3", "activeProvider": "postgresql", - "inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../src/lib/generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id\n name String\n email String\n emailVerified Boolean @default(false)\n image String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n roleId Int?\n role UserRole? @relation(fields: [roleId], references: [id])\n sessions Session[]\n accounts Account[]\n profile Profile?\n licenseKeys LicenseKey[]\n\n @@unique([email])\n @@map(\"user\")\n}\n\n// I chose to create a Profile table here because honestly, I didn't want to waste\n// a bunch of time trying to figure out how to customize the default Better-Auth model. \nmodel Profile {\n id String @id @default(uuid())\n userId String @unique\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n firstName String\n lastName String\n phoneNumber String\n addresses Address[]\n}\n\nmodel Address {\n id String @id @default(uuid())\n profileId String\n profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)\n street String\n city String\n state String\n postalCode String\n country String\n}\n\nmodel Session {\n id String @id\n expiresAt DateTime\n token String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n ipAddress String?\n userAgent String?\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@unique([token])\n @@index([userId])\n @@map(\"session\")\n}\n\nmodel Account {\n id String @id\n accountId String\n providerId String\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n accessToken String?\n refreshToken String?\n idToken String?\n accessTokenExpiresAt DateTime?\n refreshTokenExpiresAt DateTime?\n scope String?\n password String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"account\")\n}\n\nmodel UserRole {\n id Int @id @default(autoincrement())\n name String\n users User[]\n}\n\nmodel Verification {\n id String @id\n identifier String\n value String\n expiresAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([identifier])\n @@map(\"verification\")\n}\n\nmodel Affirmation {\n id String @id @default(uuid())\n name String @unique\n content String @unique\n solfeggioFrequencyId Int\n solfeggioFrequency SolfeggioFrequency @relation(fields: [solfeggioFrequencyId], references: [id])\n categoryId Int\n category AffirmationCategory @relation(fields: [categoryId], references: [id])\n price Float @default(0.99)\n}\n\nmodel SolfeggioFrequency {\n id Int @id @default(autoincrement())\n name String @unique\n value Float @unique\n affirmations Affirmation[]\n}\n\nmodel AffirmationCategory {\n id Int @id @default(autoincrement())\n name String @unique\n affirmations Affirmation[]\n}\n\nmodel LicenseKey {\n id String @id @default(uuid())\n key String\n active Boolean\n level String\n userId String\n user User @relation(fields: [userId], references: [id])\n}\n", + "inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../src/lib/generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id\n name String\n email String\n emailVerified Boolean @default(false)\n image String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n roleId Int?\n role UserRole? @relation(fields: [roleId], references: [id])\n sessions Session[]\n accounts Account[]\n profile Profile?\n licenseKeys LicenseKey[]\n\n @@unique([email])\n @@map(\"user\")\n}\n\n// I chose to create a Profile table here because honestly, I didn't want to waste\n// a bunch of time trying to figure out how to customize the default Better-Auth model. \nmodel Profile {\n id String @id @default(uuid())\n userId String @unique\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n firstName String\n lastName String\n phoneNumber String\n addresses Address[]\n}\n\nmodel Address {\n id String @id @default(uuid())\n profileId String\n profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)\n street String\n city String\n state String\n postalCode String\n country String\n}\n\nmodel Session {\n id String @id\n expiresAt DateTime\n token String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n ipAddress String?\n userAgent String?\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@unique([token])\n @@index([userId])\n @@map(\"session\")\n}\n\nmodel Account {\n id String @id\n accountId String\n providerId String\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n accessToken String?\n refreshToken String?\n idToken String?\n accessTokenExpiresAt DateTime?\n refreshTokenExpiresAt DateTime?\n scope String?\n password String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"account\")\n}\n\nmodel UserRole {\n id Int @id @default(autoincrement())\n name String\n users User[]\n}\n\nmodel Verification {\n id String @id\n identifier String\n value String\n expiresAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([identifier])\n @@map(\"verification\")\n}\n\nmodel Affirmation {\n id String @id @default(uuid())\n name String @unique\n content String @unique\n solfeggioFrequencyId Int\n solfeggioFrequency SolfeggioFrequency @relation(fields: [solfeggioFrequencyId], references: [id])\n categoryId Int\n category AffirmationCategory @relation(fields: [categoryId], references: [id])\n price Float @default(0.99)\n}\n\nmodel SolfeggioFrequency {\n id Int @id @default(autoincrement())\n name String @unique\n value Float @unique\n affirmations Affirmation[]\n}\n\nmodel AffirmationCategory {\n id Int @id @default(autoincrement())\n name String @unique\n parentId Int?\n parent AffirmationCategory? @relation(\"CategoryHierarchy\", fields: [parentId], references: [id])\n children AffirmationCategory[] @relation(\"CategoryHierarchy\")\n affirmations Affirmation[]\n}\n\nmodel LicenseKey {\n id String @id @default(uuid())\n key String\n active Boolean\n level String\n userId String\n user User @relation(fields: [userId], references: [id])\n}\n", "runtimeDataModel": { "models": {}, "enums": {}, @@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = { } } -config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"roleId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"role\",\"kind\":\"object\",\"type\":\"UserRole\",\"relationName\":\"UserToUserRole\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"accounts\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToUser\"},{\"name\":\"profile\",\"kind\":\"object\",\"type\":\"Profile\",\"relationName\":\"ProfileToUser\"},{\"name\":\"licenseKeys\",\"kind\":\"object\",\"type\":\"LicenseKey\",\"relationName\":\"LicenseKeyToUser\"}],\"dbName\":\"user\"},\"Profile\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ProfileToUser\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phoneNumber\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addresses\",\"kind\":\"object\",\"type\":\"Address\",\"relationName\":\"AddressToProfile\"}],\"dbName\":null},\"Address\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profileId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profile\",\"kind\":\"object\",\"type\":\"Profile\",\"relationName\":\"AddressToProfile\"},{\"name\":\"street\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"city\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"postalCode\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"country\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userAgent\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":\"session\"},\"Account\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"providerId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"AccountToUser\"},{\"name\":\"accessToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"idToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accessTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"scope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"account\"},\"UserRole\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"UserToUserRole\"}],\"dbName\":null},\"Verification\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"verification\"},\"Affirmation\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"solfeggioFrequencyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"solfeggioFrequency\",\"kind\":\"object\",\"type\":\"SolfeggioFrequency\",\"relationName\":\"AffirmationToSolfeggioFrequency\"},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"category\",\"kind\":\"object\",\"type\":\"AffirmationCategory\",\"relationName\":\"AffirmationToAffirmationCategory\"},{\"name\":\"price\",\"kind\":\"scalar\",\"type\":\"Float\"}],\"dbName\":null},\"SolfeggioFrequency\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"affirmations\",\"kind\":\"object\",\"type\":\"Affirmation\",\"relationName\":\"AffirmationToSolfeggioFrequency\"}],\"dbName\":null},\"AffirmationCategory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"affirmations\",\"kind\":\"object\",\"type\":\"Affirmation\",\"relationName\":\"AffirmationToAffirmationCategory\"}],\"dbName\":null},\"LicenseKey\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"active\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"level\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"LicenseKeyToUser\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"roleId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"role\",\"kind\":\"object\",\"type\":\"UserRole\",\"relationName\":\"UserToUserRole\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"accounts\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToUser\"},{\"name\":\"profile\",\"kind\":\"object\",\"type\":\"Profile\",\"relationName\":\"ProfileToUser\"},{\"name\":\"licenseKeys\",\"kind\":\"object\",\"type\":\"LicenseKey\",\"relationName\":\"LicenseKeyToUser\"}],\"dbName\":\"user\"},\"Profile\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ProfileToUser\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phoneNumber\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addresses\",\"kind\":\"object\",\"type\":\"Address\",\"relationName\":\"AddressToProfile\"}],\"dbName\":null},\"Address\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profileId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profile\",\"kind\":\"object\",\"type\":\"Profile\",\"relationName\":\"AddressToProfile\"},{\"name\":\"street\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"city\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"postalCode\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"country\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userAgent\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":\"session\"},\"Account\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"providerId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"AccountToUser\"},{\"name\":\"accessToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"idToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accessTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"scope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"account\"},\"UserRole\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"UserToUserRole\"}],\"dbName\":null},\"Verification\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"verification\"},\"Affirmation\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"solfeggioFrequencyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"solfeggioFrequency\",\"kind\":\"object\",\"type\":\"SolfeggioFrequency\",\"relationName\":\"AffirmationToSolfeggioFrequency\"},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"category\",\"kind\":\"object\",\"type\":\"AffirmationCategory\",\"relationName\":\"AffirmationToAffirmationCategory\"},{\"name\":\"price\",\"kind\":\"scalar\",\"type\":\"Float\"}],\"dbName\":null},\"SolfeggioFrequency\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"affirmations\",\"kind\":\"object\",\"type\":\"Affirmation\",\"relationName\":\"AffirmationToSolfeggioFrequency\"}],\"dbName\":null},\"AffirmationCategory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"parentId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parent\",\"kind\":\"object\",\"type\":\"AffirmationCategory\",\"relationName\":\"CategoryHierarchy\"},{\"name\":\"children\",\"kind\":\"object\",\"type\":\"AffirmationCategory\",\"relationName\":\"CategoryHierarchy\"},{\"name\":\"affirmations\",\"kind\":\"object\",\"type\":\"Affirmation\",\"relationName\":\"AffirmationToAffirmationCategory\"}],\"dbName\":null},\"LicenseKey\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"active\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"level\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"LicenseKeyToUser\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") async function decodeBase64AsWasm(wasmBase64: string): Promise { const { Buffer } = await import('node:buffer') diff --git a/src/lib/generated/prisma/internal/prismaNamespace.ts b/src/lib/generated/prisma/internal/prismaNamespace.ts index 86a4b07..4807c47 100644 --- a/src/lib/generated/prisma/internal/prismaNamespace.ts +++ b/src/lib/generated/prisma/internal/prismaNamespace.ts @@ -1381,7 +1381,8 @@ export type SolfeggioFrequencyScalarFieldEnum = (typeof SolfeggioFrequencyScalar export const AffirmationCategoryScalarFieldEnum = { id: 'id', - name: 'name' + name: 'name', + parentId: 'parentId' } as const export type AffirmationCategoryScalarFieldEnum = (typeof AffirmationCategoryScalarFieldEnum)[keyof typeof AffirmationCategoryScalarFieldEnum] diff --git a/src/lib/generated/prisma/internal/prismaNamespaceBrowser.ts b/src/lib/generated/prisma/internal/prismaNamespaceBrowser.ts index 26708ef..cbcc605 100644 --- a/src/lib/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/src/lib/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -194,7 +194,8 @@ export type SolfeggioFrequencyScalarFieldEnum = (typeof SolfeggioFrequencyScalar export const AffirmationCategoryScalarFieldEnum = { id: 'id', - name: 'name' + name: 'name', + parentId: 'parentId' } as const export type AffirmationCategoryScalarFieldEnum = (typeof AffirmationCategoryScalarFieldEnum)[keyof typeof AffirmationCategoryScalarFieldEnum] diff --git a/src/lib/generated/prisma/models/AffirmationCategory.ts b/src/lib/generated/prisma/models/AffirmationCategory.ts index ce4b49e..60c1af6 100644 --- a/src/lib/generated/prisma/models/AffirmationCategory.ts +++ b/src/lib/generated/prisma/models/AffirmationCategory.ts @@ -28,50 +28,60 @@ export type AggregateAffirmationCategory = { export type AffirmationCategoryAvgAggregateOutputType = { id: number | null + parentId: number | null } export type AffirmationCategorySumAggregateOutputType = { id: number | null + parentId: number | null } export type AffirmationCategoryMinAggregateOutputType = { id: number | null name: string | null + parentId: number | null } export type AffirmationCategoryMaxAggregateOutputType = { id: number | null name: string | null + parentId: number | null } export type AffirmationCategoryCountAggregateOutputType = { id: number name: number + parentId: number _all: number } export type AffirmationCategoryAvgAggregateInputType = { id?: true + parentId?: true } export type AffirmationCategorySumAggregateInputType = { id?: true + parentId?: true } export type AffirmationCategoryMinAggregateInputType = { id?: true name?: true + parentId?: true } export type AffirmationCategoryMaxAggregateInputType = { id?: true name?: true + parentId?: true } export type AffirmationCategoryCountAggregateInputType = { id?: true name?: true + parentId?: true _all?: true } @@ -164,6 +174,7 @@ export type AffirmationCategoryGroupByArgs | number name?: Prisma.StringFilter<"AffirmationCategory"> | string + parentId?: Prisma.IntNullableFilter<"AffirmationCategory"> | number | null + parent?: Prisma.XOR | null + children?: Prisma.AffirmationCategoryListRelationFilter affirmations?: Prisma.AffirmationListRelationFilter } export type AffirmationCategoryOrderByWithRelationInput = { id?: Prisma.SortOrder name?: Prisma.SortOrder + parentId?: Prisma.SortOrderInput | Prisma.SortOrder + parent?: Prisma.AffirmationCategoryOrderByWithRelationInput + children?: Prisma.AffirmationCategoryOrderByRelationAggregateInput affirmations?: Prisma.AffirmationOrderByRelationAggregateInput } @@ -207,12 +224,16 @@ export type AffirmationCategoryWhereUniqueInput = Prisma.AtLeast<{ AND?: Prisma.AffirmationCategoryWhereInput | Prisma.AffirmationCategoryWhereInput[] OR?: Prisma.AffirmationCategoryWhereInput[] NOT?: Prisma.AffirmationCategoryWhereInput | Prisma.AffirmationCategoryWhereInput[] + parentId?: Prisma.IntNullableFilter<"AffirmationCategory"> | number | null + parent?: Prisma.XOR | null + children?: Prisma.AffirmationCategoryListRelationFilter affirmations?: Prisma.AffirmationListRelationFilter }, "id" | "name"> export type AffirmationCategoryOrderByWithAggregationInput = { id?: Prisma.SortOrder name?: Prisma.SortOrder + parentId?: Prisma.SortOrderInput | Prisma.SortOrder _count?: Prisma.AffirmationCategoryCountOrderByAggregateInput _avg?: Prisma.AffirmationCategoryAvgOrderByAggregateInput _max?: Prisma.AffirmationCategoryMaxOrderByAggregateInput @@ -226,33 +247,43 @@ export type AffirmationCategoryScalarWhereWithAggregatesInput = { NOT?: Prisma.AffirmationCategoryScalarWhereWithAggregatesInput | Prisma.AffirmationCategoryScalarWhereWithAggregatesInput[] id?: Prisma.IntWithAggregatesFilter<"AffirmationCategory"> | number name?: Prisma.StringWithAggregatesFilter<"AffirmationCategory"> | string + parentId?: Prisma.IntNullableWithAggregatesFilter<"AffirmationCategory"> | number | null } export type AffirmationCategoryCreateInput = { name: string + parent?: Prisma.AffirmationCategoryCreateNestedOneWithoutChildrenInput + children?: Prisma.AffirmationCategoryCreateNestedManyWithoutParentInput affirmations?: Prisma.AffirmationCreateNestedManyWithoutCategoryInput } export type AffirmationCategoryUncheckedCreateInput = { id?: number name: string + parentId?: number | null + children?: Prisma.AffirmationCategoryUncheckedCreateNestedManyWithoutParentInput affirmations?: Prisma.AffirmationUncheckedCreateNestedManyWithoutCategoryInput } export type AffirmationCategoryUpdateInput = { name?: Prisma.StringFieldUpdateOperationsInput | string + parent?: Prisma.AffirmationCategoryUpdateOneWithoutChildrenNestedInput + children?: Prisma.AffirmationCategoryUpdateManyWithoutParentNestedInput affirmations?: Prisma.AffirmationUpdateManyWithoutCategoryNestedInput } export type AffirmationCategoryUncheckedUpdateInput = { id?: Prisma.IntFieldUpdateOperationsInput | number name?: Prisma.StringFieldUpdateOperationsInput | string + parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + children?: Prisma.AffirmationCategoryUncheckedUpdateManyWithoutParentNestedInput affirmations?: Prisma.AffirmationUncheckedUpdateManyWithoutCategoryNestedInput } export type AffirmationCategoryCreateManyInput = { id?: number name: string + parentId?: number | null } export type AffirmationCategoryUpdateManyMutationInput = { @@ -262,6 +293,7 @@ export type AffirmationCategoryUpdateManyMutationInput = { export type AffirmationCategoryUncheckedUpdateManyInput = { id?: Prisma.IntFieldUpdateOperationsInput | number name?: Prisma.StringFieldUpdateOperationsInput | string + parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null } export type AffirmationCategoryScalarRelationFilter = { @@ -269,27 +301,47 @@ export type AffirmationCategoryScalarRelationFilter = { isNot?: Prisma.AffirmationCategoryWhereInput } +export type AffirmationCategoryNullableScalarRelationFilter = { + is?: Prisma.AffirmationCategoryWhereInput | null + isNot?: Prisma.AffirmationCategoryWhereInput | null +} + +export type AffirmationCategoryListRelationFilter = { + every?: Prisma.AffirmationCategoryWhereInput + some?: Prisma.AffirmationCategoryWhereInput + none?: Prisma.AffirmationCategoryWhereInput +} + +export type AffirmationCategoryOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + export type AffirmationCategoryCountOrderByAggregateInput = { id?: Prisma.SortOrder name?: Prisma.SortOrder + parentId?: Prisma.SortOrder } export type AffirmationCategoryAvgOrderByAggregateInput = { id?: Prisma.SortOrder + parentId?: Prisma.SortOrder } export type AffirmationCategoryMaxOrderByAggregateInput = { id?: Prisma.SortOrder name?: Prisma.SortOrder + parentId?: Prisma.SortOrder } export type AffirmationCategoryMinOrderByAggregateInput = { id?: Prisma.SortOrder name?: Prisma.SortOrder + parentId?: Prisma.SortOrder } export type AffirmationCategorySumOrderByAggregateInput = { id?: Prisma.SortOrder + parentId?: Prisma.SortOrder } export type AffirmationCategoryCreateNestedOneWithoutAffirmationsInput = { @@ -306,13 +358,75 @@ export type AffirmationCategoryUpdateOneRequiredWithoutAffirmationsNestedInput = update?: Prisma.XOR, Prisma.AffirmationCategoryUncheckedUpdateWithoutAffirmationsInput> } +export type AffirmationCategoryCreateNestedOneWithoutChildrenInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.AffirmationCategoryCreateOrConnectWithoutChildrenInput + connect?: Prisma.AffirmationCategoryWhereUniqueInput +} + +export type AffirmationCategoryCreateNestedManyWithoutParentInput = { + create?: Prisma.XOR | Prisma.AffirmationCategoryCreateWithoutParentInput[] | Prisma.AffirmationCategoryUncheckedCreateWithoutParentInput[] + connectOrCreate?: Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput | Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput[] + createMany?: Prisma.AffirmationCategoryCreateManyParentInputEnvelope + connect?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] +} + +export type AffirmationCategoryUncheckedCreateNestedManyWithoutParentInput = { + create?: Prisma.XOR | Prisma.AffirmationCategoryCreateWithoutParentInput[] | Prisma.AffirmationCategoryUncheckedCreateWithoutParentInput[] + connectOrCreate?: Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput | Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput[] + createMany?: Prisma.AffirmationCategoryCreateManyParentInputEnvelope + connect?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] +} + +export type AffirmationCategoryUpdateOneWithoutChildrenNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.AffirmationCategoryCreateOrConnectWithoutChildrenInput + upsert?: Prisma.AffirmationCategoryUpsertWithoutChildrenInput + disconnect?: Prisma.AffirmationCategoryWhereInput | boolean + delete?: Prisma.AffirmationCategoryWhereInput | boolean + connect?: Prisma.AffirmationCategoryWhereUniqueInput + update?: Prisma.XOR, Prisma.AffirmationCategoryUncheckedUpdateWithoutChildrenInput> +} + +export type AffirmationCategoryUpdateManyWithoutParentNestedInput = { + create?: Prisma.XOR | Prisma.AffirmationCategoryCreateWithoutParentInput[] | Prisma.AffirmationCategoryUncheckedCreateWithoutParentInput[] + connectOrCreate?: Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput | Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput[] + upsert?: Prisma.AffirmationCategoryUpsertWithWhereUniqueWithoutParentInput | Prisma.AffirmationCategoryUpsertWithWhereUniqueWithoutParentInput[] + createMany?: Prisma.AffirmationCategoryCreateManyParentInputEnvelope + set?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + disconnect?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + delete?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + connect?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + update?: Prisma.AffirmationCategoryUpdateWithWhereUniqueWithoutParentInput | Prisma.AffirmationCategoryUpdateWithWhereUniqueWithoutParentInput[] + updateMany?: Prisma.AffirmationCategoryUpdateManyWithWhereWithoutParentInput | Prisma.AffirmationCategoryUpdateManyWithWhereWithoutParentInput[] + deleteMany?: Prisma.AffirmationCategoryScalarWhereInput | Prisma.AffirmationCategoryScalarWhereInput[] +} + +export type AffirmationCategoryUncheckedUpdateManyWithoutParentNestedInput = { + create?: Prisma.XOR | Prisma.AffirmationCategoryCreateWithoutParentInput[] | Prisma.AffirmationCategoryUncheckedCreateWithoutParentInput[] + connectOrCreate?: Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput | Prisma.AffirmationCategoryCreateOrConnectWithoutParentInput[] + upsert?: Prisma.AffirmationCategoryUpsertWithWhereUniqueWithoutParentInput | Prisma.AffirmationCategoryUpsertWithWhereUniqueWithoutParentInput[] + createMany?: Prisma.AffirmationCategoryCreateManyParentInputEnvelope + set?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + disconnect?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + delete?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + connect?: Prisma.AffirmationCategoryWhereUniqueInput | Prisma.AffirmationCategoryWhereUniqueInput[] + update?: Prisma.AffirmationCategoryUpdateWithWhereUniqueWithoutParentInput | Prisma.AffirmationCategoryUpdateWithWhereUniqueWithoutParentInput[] + updateMany?: Prisma.AffirmationCategoryUpdateManyWithWhereWithoutParentInput | Prisma.AffirmationCategoryUpdateManyWithWhereWithoutParentInput[] + deleteMany?: Prisma.AffirmationCategoryScalarWhereInput | Prisma.AffirmationCategoryScalarWhereInput[] +} + export type AffirmationCategoryCreateWithoutAffirmationsInput = { name: string + parent?: Prisma.AffirmationCategoryCreateNestedOneWithoutChildrenInput + children?: Prisma.AffirmationCategoryCreateNestedManyWithoutParentInput } export type AffirmationCategoryUncheckedCreateWithoutAffirmationsInput = { id?: number name: string + parentId?: number | null + children?: Prisma.AffirmationCategoryUncheckedCreateNestedManyWithoutParentInput } export type AffirmationCategoryCreateOrConnectWithoutAffirmationsInput = { @@ -333,11 +447,128 @@ export type AffirmationCategoryUpdateToOneWithWhereWithoutAffirmationsInput = { export type AffirmationCategoryUpdateWithoutAffirmationsInput = { name?: Prisma.StringFieldUpdateOperationsInput | string + parent?: Prisma.AffirmationCategoryUpdateOneWithoutChildrenNestedInput + children?: Prisma.AffirmationCategoryUpdateManyWithoutParentNestedInput } export type AffirmationCategoryUncheckedUpdateWithoutAffirmationsInput = { id?: Prisma.IntFieldUpdateOperationsInput | number name?: Prisma.StringFieldUpdateOperationsInput | string + parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + children?: Prisma.AffirmationCategoryUncheckedUpdateManyWithoutParentNestedInput +} + +export type AffirmationCategoryCreateWithoutChildrenInput = { + name: string + parent?: Prisma.AffirmationCategoryCreateNestedOneWithoutChildrenInput + affirmations?: Prisma.AffirmationCreateNestedManyWithoutCategoryInput +} + +export type AffirmationCategoryUncheckedCreateWithoutChildrenInput = { + id?: number + name: string + parentId?: number | null + affirmations?: Prisma.AffirmationUncheckedCreateNestedManyWithoutCategoryInput +} + +export type AffirmationCategoryCreateOrConnectWithoutChildrenInput = { + where: Prisma.AffirmationCategoryWhereUniqueInput + create: Prisma.XOR +} + +export type AffirmationCategoryCreateWithoutParentInput = { + name: string + children?: Prisma.AffirmationCategoryCreateNestedManyWithoutParentInput + affirmations?: Prisma.AffirmationCreateNestedManyWithoutCategoryInput +} + +export type AffirmationCategoryUncheckedCreateWithoutParentInput = { + id?: number + name: string + children?: Prisma.AffirmationCategoryUncheckedCreateNestedManyWithoutParentInput + affirmations?: Prisma.AffirmationUncheckedCreateNestedManyWithoutCategoryInput +} + +export type AffirmationCategoryCreateOrConnectWithoutParentInput = { + where: Prisma.AffirmationCategoryWhereUniqueInput + create: Prisma.XOR +} + +export type AffirmationCategoryCreateManyParentInputEnvelope = { + data: Prisma.AffirmationCategoryCreateManyParentInput | Prisma.AffirmationCategoryCreateManyParentInput[] + skipDuplicates?: boolean +} + +export type AffirmationCategoryUpsertWithoutChildrenInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.AffirmationCategoryWhereInput +} + +export type AffirmationCategoryUpdateToOneWithWhereWithoutChildrenInput = { + where?: Prisma.AffirmationCategoryWhereInput + data: Prisma.XOR +} + +export type AffirmationCategoryUpdateWithoutChildrenInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + parent?: Prisma.AffirmationCategoryUpdateOneWithoutChildrenNestedInput + affirmations?: Prisma.AffirmationUpdateManyWithoutCategoryNestedInput +} + +export type AffirmationCategoryUncheckedUpdateWithoutChildrenInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + affirmations?: Prisma.AffirmationUncheckedUpdateManyWithoutCategoryNestedInput +} + +export type AffirmationCategoryUpsertWithWhereUniqueWithoutParentInput = { + where: Prisma.AffirmationCategoryWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type AffirmationCategoryUpdateWithWhereUniqueWithoutParentInput = { + where: Prisma.AffirmationCategoryWhereUniqueInput + data: Prisma.XOR +} + +export type AffirmationCategoryUpdateManyWithWhereWithoutParentInput = { + where: Prisma.AffirmationCategoryScalarWhereInput + data: Prisma.XOR +} + +export type AffirmationCategoryScalarWhereInput = { + AND?: Prisma.AffirmationCategoryScalarWhereInput | Prisma.AffirmationCategoryScalarWhereInput[] + OR?: Prisma.AffirmationCategoryScalarWhereInput[] + NOT?: Prisma.AffirmationCategoryScalarWhereInput | Prisma.AffirmationCategoryScalarWhereInput[] + id?: Prisma.IntFilter<"AffirmationCategory"> | number + name?: Prisma.StringFilter<"AffirmationCategory"> | string + parentId?: Prisma.IntNullableFilter<"AffirmationCategory"> | number | null +} + +export type AffirmationCategoryCreateManyParentInput = { + id?: number + name: string +} + +export type AffirmationCategoryUpdateWithoutParentInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + children?: Prisma.AffirmationCategoryUpdateManyWithoutParentNestedInput + affirmations?: Prisma.AffirmationUpdateManyWithoutCategoryNestedInput +} + +export type AffirmationCategoryUncheckedUpdateWithoutParentInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + children?: Prisma.AffirmationCategoryUncheckedUpdateManyWithoutParentNestedInput + affirmations?: Prisma.AffirmationUncheckedUpdateManyWithoutCategoryNestedInput +} + +export type AffirmationCategoryUncheckedUpdateManyWithoutParentInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string } @@ -346,10 +577,12 @@ export type AffirmationCategoryUncheckedUpdateWithoutAffirmationsInput = { */ export type AffirmationCategoryCountOutputType = { + children: number affirmations: number } export type AffirmationCategoryCountOutputTypeSelect = { + children?: boolean | AffirmationCategoryCountOutputTypeCountChildrenArgs affirmations?: boolean | AffirmationCategoryCountOutputTypeCountAffirmationsArgs } @@ -363,6 +596,13 @@ export type AffirmationCategoryCountOutputTypeDefaultArgs | null } +/** + * AffirmationCategoryCountOutputType without action + */ +export type AffirmationCategoryCountOutputTypeCountChildrenArgs = { + where?: Prisma.AffirmationCategoryWhereInput +} + /** * AffirmationCategoryCountOutputType without action */ @@ -374,6 +614,9 @@ export type AffirmationCategoryCountOutputTypeCountAffirmationsArgs = runtime.Types.Extensions.GetSelect<{ id?: boolean name?: boolean + parentId?: boolean + parent?: boolean | Prisma.AffirmationCategory$parentArgs + children?: boolean | Prisma.AffirmationCategory$childrenArgs affirmations?: boolean | Prisma.AffirmationCategory$affirmationsArgs _count?: boolean | Prisma.AffirmationCategoryCountOutputTypeDefaultArgs }, ExtArgs["result"]["affirmationCategory"]> @@ -381,34 +624,48 @@ export type AffirmationCategorySelect = runtime.Types.Extensions.GetSelect<{ id?: boolean name?: boolean + parentId?: boolean + parent?: boolean | Prisma.AffirmationCategory$parentArgs }, ExtArgs["result"]["affirmationCategory"]> export type AffirmationCategorySelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean name?: boolean + parentId?: boolean + parent?: boolean | Prisma.AffirmationCategory$parentArgs }, ExtArgs["result"]["affirmationCategory"]> export type AffirmationCategorySelectScalar = { id?: boolean name?: boolean + parentId?: boolean } -export type AffirmationCategoryOmit = runtime.Types.Extensions.GetOmit<"id" | "name", ExtArgs["result"]["affirmationCategory"]> +export type AffirmationCategoryOmit = runtime.Types.Extensions.GetOmit<"id" | "name" | "parentId", ExtArgs["result"]["affirmationCategory"]> export type AffirmationCategoryInclude = { + parent?: boolean | Prisma.AffirmationCategory$parentArgs + children?: boolean | Prisma.AffirmationCategory$childrenArgs affirmations?: boolean | Prisma.AffirmationCategory$affirmationsArgs _count?: boolean | Prisma.AffirmationCategoryCountOutputTypeDefaultArgs } -export type AffirmationCategoryIncludeCreateManyAndReturn = {} -export type AffirmationCategoryIncludeUpdateManyAndReturn = {} +export type AffirmationCategoryIncludeCreateManyAndReturn = { + parent?: boolean | Prisma.AffirmationCategory$parentArgs +} +export type AffirmationCategoryIncludeUpdateManyAndReturn = { + parent?: boolean | Prisma.AffirmationCategory$parentArgs +} export type $AffirmationCategoryPayload = { name: "AffirmationCategory" objects: { + parent: Prisma.$AffirmationCategoryPayload | null + children: Prisma.$AffirmationCategoryPayload[] affirmations: Prisma.$AffirmationPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: number name: string + parentId: number | null }, ExtArgs["result"]["affirmationCategory"]> composites: {} } @@ -803,6 +1060,8 @@ readonly fields: AffirmationCategoryFieldRefs; */ export interface Prisma__AffirmationCategoryClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" + parent = {}>(args?: Prisma.Subset>): Prisma.Prisma__AffirmationCategoryClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + children = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> affirmations = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. @@ -835,6 +1094,7 @@ export interface Prisma__AffirmationCategoryClient readonly name: Prisma.FieldRef<"AffirmationCategory", 'String'> + readonly parentId: Prisma.FieldRef<"AffirmationCategory", 'Int'> } @@ -1084,6 +1344,10 @@ export type AffirmationCategoryCreateManyAndReturnArgs | null } /** @@ -1154,6 +1418,10 @@ export type AffirmationCategoryUpdateManyAndReturnArgs | null } /** @@ -1222,6 +1490,49 @@ export type AffirmationCategoryDeleteManyArgs = { + /** + * Select specific fields to fetch from the AffirmationCategory + */ + select?: Prisma.AffirmationCategorySelect | null + /** + * Omit specific fields from the AffirmationCategory + */ + omit?: Prisma.AffirmationCategoryOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.AffirmationCategoryInclude | null + where?: Prisma.AffirmationCategoryWhereInput +} + +/** + * AffirmationCategory.children + */ +export type AffirmationCategory$childrenArgs = { + /** + * Select specific fields to fetch from the AffirmationCategory + */ + select?: Prisma.AffirmationCategorySelect | null + /** + * Omit specific fields from the AffirmationCategory + */ + omit?: Prisma.AffirmationCategoryOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.AffirmationCategoryInclude | null + where?: Prisma.AffirmationCategoryWhereInput + orderBy?: Prisma.AffirmationCategoryOrderByWithRelationInput | Prisma.AffirmationCategoryOrderByWithRelationInput[] + cursor?: Prisma.AffirmationCategoryWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.AffirmationCategoryScalarFieldEnum | Prisma.AffirmationCategoryScalarFieldEnum[] +} + /** * AffirmationCategory.affirmations */ diff --git a/src/lib/svelte-admin/registry.ts b/src/lib/svelte-admin/registry.ts index 680ef30..522024e 100644 --- a/src/lib/svelte-admin/registry.ts +++ b/src/lib/svelte-admin/registry.ts @@ -96,7 +96,7 @@ const models: AdminModelConfig[] = [ displayName: 'Affirmations', displayNameSingular: 'Affirmation', idType: 'String', - include: { solfeggioFrequency: true, category: true }, + include: { solfeggioFrequency: true, category: { include: { parent: true } } }, listFields: ['name', 'category.name', 'solfeggioFrequency.name', 'price'], searchFields: ['name', 'content'], formFields: ['name', 'content', 'solfeggioFrequencyId', 'categoryId', 'price'], @@ -152,13 +152,17 @@ const models: AdminModelConfig[] = [ displayName: 'Affirmation Categories', displayNameSingular: 'Affirmation Category', idType: 'Int', - include: { affirmations: true }, - listFields: ['name'], + include: { affirmations: true, children: true, parent: true }, + listFields: ['name', 'parent.name'], searchFields: ['name'], - formFields: ['name'], + formFields: ['name', 'parentId'], readonlyFields: ['id'], fieldTypes: { - name: 'String' + name: 'String', + parentId: 'Int' + }, + relationOptions: { + parentId: { model: 'AffirmationCategory', labelField: 'name', valueField: 'id' } }, relatedRecords: [ { @@ -167,6 +171,13 @@ const models: AdminModelConfig[] = [ type: 'has-many', displayFields: ['name', 'price'], editSlug: 'affirmations' + }, + { + relationKey: 'children', + label: 'Sub-categories', + type: 'has-many', + displayFields: ['name'], + editSlug: 'affirmation-categories' } ] }, diff --git a/src/lib/svelte-admin/types.ts b/src/lib/svelte-admin/types.ts index f476a64..4eabf6f 100644 --- a/src/lib/svelte-admin/types.ts +++ b/src/lib/svelte-admin/types.ts @@ -25,7 +25,7 @@ export type AdminModelConfig = { displayName: string; displayNameSingular: string; idType: 'String' | 'Int'; - include?: Record; + include?: Record }>; listFields: string[]; searchFields: string[]; formFields: string[]; diff --git a/src/routes/(protected)/affirmations/+page.server.ts b/src/routes/(protected)/affirmations/+page.server.ts index 642e47f..4c656ed 100644 --- a/src/routes/(protected)/affirmations/+page.server.ts +++ b/src/routes/(protected)/affirmations/+page.server.ts @@ -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({ diff --git a/src/routes/(protected)/affirmations/[id]/+page.server.ts b/src/routes/(protected)/affirmations/[id]/+page.server.ts index a43971c..e98bb0c 100644 --- a/src/routes/(protected)/affirmations/[id]/+page.server.ts +++ b/src/routes/(protected)/affirmations/[id]/+page.server.ts @@ -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: {