From 531b385ac53f3235d7731c147f427bc582a96f2b Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 25 Nov 2025 12:50:30 +0100 Subject: [PATCH 01/39] add initial db setup --- .gitignore | 2 + lib/prisma.ts | 13 + package-lock.json | 385 +++++++++++++++++- package.json | 10 +- .../20251125112453_init/migration.sql | 119 ++++++ prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 102 +++++ scripts/init-db.sql | 17 +- 8 files changed, 633 insertions(+), 18 deletions(-) create mode 100644 lib/prisma.ts create mode 100644 prisma/migrations/20251125112453_init/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma diff --git a/.gitignore b/.gitignore index 5ef6a52..62fcee8 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +/lib/generated/prisma diff --git a/lib/prisma.ts b/lib/prisma.ts new file mode 100644 index 0000000..33c4c2b --- /dev/null +++ b/lib/prisma.ts @@ -0,0 +1,13 @@ +import { PrismaClient } from '@prisma/client'; + +const globalForPrisma = globalThis as unknown as { + prisma: PrismaClient | undefined; +}; + +export const prisma = + globalForPrisma.prisma ?? + new PrismaClient({ + log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], + }); + +if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; diff --git a/package-lock.json b/package-lock.json index b420aca..324a905 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,12 @@ "name": "memo", "version": "0.1.0", "dependencies": { + "@prisma/client": "^6.19.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.525.0", "next": "^15.5.3", + "prisma": "^6.19.0", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.3.1" @@ -995,6 +997,85 @@ "node": ">=12.4.0" } }, + "node_modules/@prisma/client": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.0.tgz", + "integrity": "sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", + "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", + "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", + "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.0", + "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "@prisma/fetch-engine": "6.19.0", + "@prisma/get-platform": "6.19.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", + "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", + "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.0", + "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "@prisma/get-platform": "6.19.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", + "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.0" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1009,6 +1090,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -2229,6 +2316,34 @@ "node": ">=8" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2326,6 +2441,21 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -2336,6 +2466,15 @@ "node": ">=18" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -2390,6 +2529,21 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2498,6 +2652,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2534,6 +2697,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", @@ -2557,6 +2732,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2572,6 +2759,16 @@ "node": ">= 0.4" } }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2579,6 +2776,15 @@ "dev": true, "license": "MIT" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.2", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", @@ -3209,6 +3415,34 @@ "node": ">=0.10.0" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3461,6 +3695,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4114,7 +4365,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -4128,9 +4378,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4752,6 +5002,31 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4875,6 +5150,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4983,6 +5264,18 @@ "dev": true, "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5002,6 +5295,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5067,6 +5371,31 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prisma": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", + "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.19.0", + "@prisma/engines": "6.19.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5089,6 +5418,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5110,6 +5455,16 @@ ], "license": "MIT" }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", @@ -5138,6 +5493,19 @@ "dev": true, "license": "MIT" }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5783,6 +6151,15 @@ "node": ">=18" } }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", diff --git a/package.json b/package.json index 441476b..707d22d 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,21 @@ "format:check": "prettier --check .", "type-check": "tsc --noEmit", "quality": "npm run type-check && npm run lint && npm run format:check", - "quality:fix": "npm run type-check && npm run lint:fix && npm run format" + "quality:fix": "npm run type-check && npm run lint:fix && npm run format", + "db:generate": "prisma generate", + "db:migrate": "prisma migrate dev", + "db:migrate:prod": "prisma migrate deploy", + "db:push": "prisma db push", + "db:studio": "prisma studio", + "db:reset": "prisma migrate reset" }, "dependencies": { + "@prisma/client": "^6.19.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.525.0", "next": "^15.5.3", + "prisma": "^6.19.0", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.3.1" diff --git a/prisma/migrations/20251125112453_init/migration.sql b/prisma/migrations/20251125112453_init/migration.sql new file mode 100644 index 0000000..a2fa5d5 --- /dev/null +++ b/prisma/migrations/20251125112453_init/migration.sql @@ -0,0 +1,119 @@ +CREATE TYPE "ContributorRole" AS ENUM ( + 'CONTRIBUTOR', + 'MAINTAINER', + 'ADMIN' + ); + +CREATE TYPE "RelationshipType" AS ENUM ( + 'PREREQUISITE', + 'HIERARCHICAL', + 'RELATED' + ); + +CREATE TYPE "ResourceType" AS ENUM ( + 'LECTURE_MATERIAL', + 'RESEARCH_PAPER', + 'BOOK', + 'OTHER' + ); + +CREATE TABLE "contributors" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "role" "ContributorRole" NOT NULL DEFAULT 'CONTRIBUTOR', + "institution" TEXT, + "isVerified" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "contributors_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "competencies" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "knowledgeArea" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "competency_relationships" ( + "id" TEXT NOT NULL, + "relationshipType" "RelationshipType" NOT NULL, + "originId" TEXT NOT NULL, + "destinationId" TEXT NOT NULL, + "creatorId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competency_relationships_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "learning_resources" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "url" TEXT, + "type" "ResourceType" NOT NULL, + "author" TEXT, + "abstract" TEXT, + "isbn" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "learning_resources_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "competency_resource_links" ( + "id" TEXT NOT NULL, + "alignmentType" TEXT, + "competencyId" TEXT NOT NULL, + "resourceId" TEXT NOT NULL, + "creatorId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competency_resource_links_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "contributors_email_key" ON "contributors"("email"); + +ALTER TABLE "competency_relationships" + ADD CONSTRAINT "competency_relationships_originId_fkey" + FOREIGN KEY ("originId") REFERENCES "competencies"("id") + ON DELETE CASCADE + ON UPDATE CASCADE; + +ALTER TABLE "competency_relationships" + ADD CONSTRAINT "competency_relationships_destinationId_fkey" + FOREIGN KEY ("destinationId") REFERENCES "competencies"("id") + ON DELETE CASCADE + ON UPDATE CASCADE; + +ALTER TABLE "competency_relationships" + ADD CONSTRAINT "competency_relationships_creatorId_fkey" + FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") + ON DELETE RESTRICT + ON UPDATE CASCADE; + +ALTER TABLE "competency_resource_links" + ADD CONSTRAINT "competency_resource_links_competencyId_fkey" + FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") + ON DELETE CASCADE + ON UPDATE CASCADE; + +ALTER TABLE "competency_resource_links" + ADD CONSTRAINT "competency_resource_links_resourceId_fkey" + FOREIGN KEY ("resourceId") REFERENCES "learning_resources"("id") + ON DELETE CASCADE + ON UPDATE CASCADE; + +ALTER TABLE "competency_resource_links" + ADD CONSTRAINT "competency_resource_links_creatorId_fkey" + FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") + ON DELETE RESTRICT + ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..c965933 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,102 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Contributor { + id String @id @default(cuid()) + name String + email String @unique + role ContributorRole @default(CONTRIBUTOR) + institution String? + isVerified Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + createdRelationships CompetencyRelationship[] @relation("RelationshipCreator") + createdResourceLinks CompetencyResourceLink[] @relation("ResourceLinkCreator") + + @@map("contributors") +} + +model Competency { + id String @id @default(cuid()) + title String + description String? + knowledgeArea String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + originRelationships CompetencyRelationship[] @relation("OriginCompetency") + destinationRelationships CompetencyRelationship[] @relation("DestinationCompetency") + resourceLinks CompetencyResourceLink[] + + @@map("competencies") +} + +model CompetencyRelationship { + id String @id @default(cuid()) + relationshipType RelationshipType + originId String + destinationId String + creatorId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + origin Competency @relation("OriginCompetency", fields: [originId], references: [id], onDelete: Cascade) + destination Competency @relation("DestinationCompetency", fields: [destinationId], references: [id], onDelete: Cascade) + creator Contributor @relation("RelationshipCreator", fields: [creatorId], references: [id]) + + @@map("competency_relationships") +} + +model LearningResource { + id String @id @default(cuid()) + title String + description String? + url String? + type ResourceType + author String? + abstract String? + isbn String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencyLinks CompetencyResourceLink[] + + @@map("learning_resources") +} + +model CompetencyResourceLink { + id String @id @default(cuid()) + alignmentType String? + competencyId String + resourceId String + creatorId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) + resource LearningResource @relation(fields: [resourceId], references: [id], onDelete: Cascade) + creator Contributor @relation("ResourceLinkCreator", fields: [creatorId], references: [id]) + + @@map("competency_resource_links") +} + +enum ContributorRole { + CONTRIBUTOR + MAINTAINER + ADMIN +} + +enum RelationshipType { + PREREQUISITE + HIERARCHICAL + RELATED +} + +enum ResourceType { + LECTURE_MATERIAL + RESEARCH_PAPER + BOOK + OTHER +} diff --git a/scripts/init-db.sql b/scripts/init-db.sql index f36de4f..9b50266 100644 --- a/scripts/init-db.sql +++ b/scripts/init-db.sql @@ -1,16 +1,7 @@ --- Initialize database with basic setup --- This script will run when the PostgreSQL container starts for the first time +-- Initial database setup for Memo +-- This script runs when the PostgreSQL container is first created -- Create extensions if needed --- CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; --- You can add initial table creation scripts here --- Example: --- CREATE TABLE IF NOT EXISTS users ( --- id SERIAL PRIMARY KEY, --- email VARCHAR(255) UNIQUE NOT NULL, --- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP --- ); - --- Add any initial data seeding here --- INSERT INTO users (email) VALUES ('admin@example.com') ON CONFLICT DO NOTHING; +-- Prisma will handle table creation via migrations From 6871c305ef0500cc6491b7a708afd8e4f18f3db3 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 2 Dec 2025 12:05:03 +0100 Subject: [PATCH 02/39] remove redundant fields in db --- package-lock.json | 37 +++++- package.json | 2 +- .../20251125112453_init/migration.sql | 119 ------------------ .../20251202110245_init/migration.sql | 66 ++++++++++ prisma/schema.prisma | 22 +--- 5 files changed, 103 insertions(+), 143 deletions(-) delete mode 100644 prisma/migrations/20251125112453_init/migration.sql create mode 100644 prisma/migrations/20251202110245_init/migration.sql diff --git a/package-lock.json b/package-lock.json index 324a905..126173a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "clsx": "^2.1.1", "lucide-react": "^0.525.0", "next": "^15.5.3", - "prisma": "^6.19.0", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.3.1" @@ -27,6 +26,7 @@ "eslint": "^9", "eslint-config-next": "15.3.4", "prettier": "^3.6.2", + "prisma": "^6.19.0", "tailwindcss": "^4", "tw-animate-css": "^1.3.4", "typescript": "^5" @@ -1023,6 +1023,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -1035,12 +1036,14 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1054,12 +1057,14 @@ "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.19.0", @@ -1071,6 +1076,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.19.0" @@ -1094,6 +1100,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "devOptional": true, "license": "MIT" }, "node_modules/@swc/helpers": { @@ -2320,6 +2327,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -2445,6 +2453,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -2470,6 +2479,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -2533,12 +2543,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -2656,6 +2668,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -2701,12 +2714,14 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, "license": "MIT" }, "node_modules/destr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -2736,6 +2751,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -2763,6 +2779,7 @@ "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -2780,6 +2797,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -3419,12 +3437,14 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "devOptional": true, "license": "MIT" }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, "funding": [ { "type": "individual", @@ -3699,6 +3719,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -4365,6 +4386,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -5006,12 +5028,14 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, "license": "MIT" }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -5154,6 +5178,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, "license": "MIT" }, "node_modules/optionator": { @@ -5268,12 +5293,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, "license": "MIT" }, "node_modules/picocolors": { @@ -5299,6 +5326,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -5375,6 +5403,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -5422,6 +5451,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, "funding": [ { "type": "individual", @@ -5459,6 +5489,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -5497,6 +5528,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -6155,6 +6187,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -6355,7 +6388,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 707d22d..de88827 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "clsx": "^2.1.1", "lucide-react": "^0.525.0", "next": "^15.5.3", - "prisma": "^6.19.0", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.3.1" @@ -40,6 +39,7 @@ "eslint": "^9", "eslint-config-next": "15.3.4", "prettier": "^3.6.2", + "prisma": "^6.19.0", "tailwindcss": "^4", "tw-animate-css": "^1.3.4", "typescript": "^5" diff --git a/prisma/migrations/20251125112453_init/migration.sql b/prisma/migrations/20251125112453_init/migration.sql deleted file mode 100644 index a2fa5d5..0000000 --- a/prisma/migrations/20251125112453_init/migration.sql +++ /dev/null @@ -1,119 +0,0 @@ -CREATE TYPE "ContributorRole" AS ENUM ( - 'CONTRIBUTOR', - 'MAINTAINER', - 'ADMIN' - ); - -CREATE TYPE "RelationshipType" AS ENUM ( - 'PREREQUISITE', - 'HIERARCHICAL', - 'RELATED' - ); - -CREATE TYPE "ResourceType" AS ENUM ( - 'LECTURE_MATERIAL', - 'RESEARCH_PAPER', - 'BOOK', - 'OTHER' - ); - -CREATE TABLE "contributors" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "email" TEXT NOT NULL, - "role" "ContributorRole" NOT NULL DEFAULT 'CONTRIBUTOR', - "institution" TEXT, - "isVerified" BOOLEAN NOT NULL DEFAULT false, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "contributors_pkey" PRIMARY KEY ("id") -); - -CREATE TABLE "competencies" ( - "id" TEXT NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT, - "knowledgeArea" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") -); - -CREATE TABLE "competency_relationships" ( - "id" TEXT NOT NULL, - "relationshipType" "RelationshipType" NOT NULL, - "originId" TEXT NOT NULL, - "destinationId" TEXT NOT NULL, - "creatorId" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competency_relationships_pkey" PRIMARY KEY ("id") -); - -CREATE TABLE "learning_resources" ( - "id" TEXT NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT, - "url" TEXT, - "type" "ResourceType" NOT NULL, - "author" TEXT, - "abstract" TEXT, - "isbn" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "learning_resources_pkey" PRIMARY KEY ("id") -); - -CREATE TABLE "competency_resource_links" ( - "id" TEXT NOT NULL, - "alignmentType" TEXT, - "competencyId" TEXT NOT NULL, - "resourceId" TEXT NOT NULL, - "creatorId" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competency_resource_links_pkey" PRIMARY KEY ("id") -); - -CREATE UNIQUE INDEX "contributors_email_key" ON "contributors"("email"); - -ALTER TABLE "competency_relationships" - ADD CONSTRAINT "competency_relationships_originId_fkey" - FOREIGN KEY ("originId") REFERENCES "competencies"("id") - ON DELETE CASCADE - ON UPDATE CASCADE; - -ALTER TABLE "competency_relationships" - ADD CONSTRAINT "competency_relationships_destinationId_fkey" - FOREIGN KEY ("destinationId") REFERENCES "competencies"("id") - ON DELETE CASCADE - ON UPDATE CASCADE; - -ALTER TABLE "competency_relationships" - ADD CONSTRAINT "competency_relationships_creatorId_fkey" - FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") - ON DELETE RESTRICT - ON UPDATE CASCADE; - -ALTER TABLE "competency_resource_links" - ADD CONSTRAINT "competency_resource_links_competencyId_fkey" - FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") - ON DELETE CASCADE - ON UPDATE CASCADE; - -ALTER TABLE "competency_resource_links" - ADD CONSTRAINT "competency_resource_links_resourceId_fkey" - FOREIGN KEY ("resourceId") REFERENCES "learning_resources"("id") - ON DELETE CASCADE - ON UPDATE CASCADE; - -ALTER TABLE "competency_resource_links" - ADD CONSTRAINT "competency_resource_links_creatorId_fkey" - FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") - ON DELETE RESTRICT - ON UPDATE CASCADE; diff --git a/prisma/migrations/20251202110245_init/migration.sql b/prisma/migrations/20251202110245_init/migration.sql new file mode 100644 index 0000000..07cb464 --- /dev/null +++ b/prisma/migrations/20251202110245_init/migration.sql @@ -0,0 +1,66 @@ +CREATE TYPE "ContributorRole" AS ENUM ('CONTRIBUTOR', 'MAINTAINER', 'ADMIN'); + +CREATE TYPE "RelationshipType" AS ENUM ('PREREQUISITE', 'HIERARCHICAL', 'RELATED'); + +CREATE TABLE "contributors" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "role" "ContributorRole" NOT NULL DEFAULT 'CONTRIBUTOR', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "contributors_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "competencies" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "competency_relationships" ( + "id" TEXT NOT NULL, + "relationshipType" "RelationshipType" NOT NULL, + "originId" TEXT NOT NULL, + "destinationId" TEXT NOT NULL, + "creatorId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "competency_relationships_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "learning_resources" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "url" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "learning_resources_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "competency_resource_links" ( + "id" TEXT NOT NULL, + "competencyId" TEXT NOT NULL, + "resourceId" TEXT NOT NULL, + "creatorId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "competency_resource_links_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "contributors_email_key" ON "contributors"("email"); + +ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_originId_fkey" FOREIGN KEY ("originId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_destinationId_fkey" FOREIGN KEY ("destinationId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_resourceId_fkey" FOREIGN KEY ("resourceId") REFERENCES "learning_resources"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c965933..c623572 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -12,10 +12,7 @@ model Contributor { name String email String @unique role ContributorRole @default(CONTRIBUTOR) - institution String? - isVerified Boolean @default(false) createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt createdRelationships CompetencyRelationship[] @relation("RelationshipCreator") createdResourceLinks CompetencyResourceLink[] @relation("ResourceLinkCreator") @@ -26,9 +23,7 @@ model Competency { id String @id @default(cuid()) title String description String? - knowledgeArea String? createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt originRelationships CompetencyRelationship[] @relation("OriginCompetency") destinationRelationships CompetencyRelationship[] @relation("DestinationCompetency") resourceLinks CompetencyResourceLink[] @@ -43,7 +38,6 @@ model CompetencyRelationship { destinationId String creatorId String createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt origin Competency @relation("OriginCompetency", fields: [originId], references: [id], onDelete: Cascade) destination Competency @relation("DestinationCompetency", fields: [destinationId], references: [id], onDelete: Cascade) creator Contributor @relation("RelationshipCreator", fields: [creatorId], references: [id]) @@ -54,14 +48,8 @@ model CompetencyRelationship { model LearningResource { id String @id @default(cuid()) title String - description String? - url String? - type ResourceType - author String? - abstract String? - isbn String? + url String createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt competencyLinks CompetencyResourceLink[] @@map("learning_resources") @@ -69,12 +57,10 @@ model LearningResource { model CompetencyResourceLink { id String @id @default(cuid()) - alignmentType String? competencyId String resourceId String creatorId String createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) resource LearningResource @relation(fields: [resourceId], references: [id], onDelete: Cascade) creator Contributor @relation("ResourceLinkCreator", fields: [creatorId], references: [id]) @@ -94,9 +80,3 @@ enum RelationshipType { RELATED } -enum ResourceType { - LECTURE_MATERIAL - RESEARCH_PAPER - BOOK - OTHER -} From 377fab220dc19072c4cddd234af51bd462ed0965 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 2 Dec 2025 12:13:01 +0100 Subject: [PATCH 03/39] fix CI and code quality issues --- lib/prisma.ts | 5 +- package-lock.json | 612 ++++++++++++++++++++++++++++++++++++---------- package.json | 3 + 3 files changed, 487 insertions(+), 133 deletions(-) diff --git a/lib/prisma.ts b/lib/prisma.ts index 33c4c2b..ab2f4da 100644 --- a/lib/prisma.ts +++ b/lib/prisma.ts @@ -7,7 +7,10 @@ const globalForPrisma = globalThis as unknown as { export const prisma = globalForPrisma.prisma ?? new PrismaClient({ - log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], + log: + process.env.NODE_ENV === 'development' + ? ['query', 'error', 'warn'] + : ['error'], }); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; diff --git a/package-lock.json b/package-lock.json index 97913bc..b7ff86c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.1.0", "dependencies": { "@prisma/client": "^6.19.0", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-slot": "^1.2.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.525.0", @@ -62,15 +65,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -1094,6 +1088,475 @@ "@prisma/debug": "6.19.0" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1446,6 +1909,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1461,21 +1925,6 @@ "@types/react": "^19.0.0" } }, - "node_modules/@types/react-reconciler": { - "version": "0.32.3", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.32.3.tgz", - "integrity": "sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/webxr": { - "version": "0.5.24", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", - "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.35.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", @@ -2325,26 +2774,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2621,6 +3050,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3969,26 +4399,6 @@ "node": ">= 0.4" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4474,27 +4884,6 @@ "node": ">= 0.4" } }, - "node_modules/its-fine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", - "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", - "license": "MIT", - "dependencies": { - "@types/react-reconciler": "^0.28.9" - }, - "peerDependencies": { - "react": "^19.0.0" - } - }, - "node_modules/its-fine/node_modules/@types/react-reconciler": { - "version": "0.28.9", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", - "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, "node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", @@ -6292,15 +6681,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/suspend-react": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", - "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", - "license": "MIT", - "peerDependencies": { - "react": ">=17.0" - } - }, "node_modules/tailwind-merge": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", @@ -6364,6 +6744,12 @@ "node": ">=18" } }, + "node_modules/three": { + "version": "0.181.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", + "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", + "license": "MIT" + }, "node_modules/tinyexec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", @@ -6650,15 +7036,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6796,35 +7173,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zustand": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", - "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } } } } diff --git a/package.json b/package.json index a3d7386..8acda22 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ }, "dependencies": { "@prisma/client": "^6.19.0", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-slot": "^1.2.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.525.0", From f114fa8a6b1f4cfb78b58e86913103cd14d57012 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 3 Dec 2025 16:46:14 +0100 Subject: [PATCH 04/39] remove manually migration, update db setup --- DATABASE.md | 82 +++++++++++++++++++ Dockerfile | 6 +- README.md | 12 ++- docker/staging/docker-compose.yml | 3 +- instrumentation.ts | 6 ++ lib/run-migrations.ts | 19 +++++ package-lock.json | 11 +++ package.json | 1 + .../migration.sql | 32 ++++++-- prisma/schema.prisma | 19 ++--- 10 files changed, 168 insertions(+), 23 deletions(-) create mode 100644 DATABASE.md create mode 100644 instrumentation.ts create mode 100644 lib/run-migrations.ts rename prisma/migrations/{20251202110245_init => 20251203125815_init}/migration.sql (75%) diff --git a/DATABASE.md b/DATABASE.md new file mode 100644 index 0000000..e7263b8 --- /dev/null +++ b/DATABASE.md @@ -0,0 +1,82 @@ +# Database Setup and Migrations + +## Overview + +PostgreSQL 16 with Prisma ORM. **Migrations auto-apply on server startup** in all environments. + +## Quick Start + +```bash +# Development +./docker-manage.sh up development + +# Production/Staging +./docker-manage.sh up production +./docker-manage.sh up staging +``` + +## How Migrations Work + +**Automatic on startup:** +- **Production/Staging**: Crashes if migration fails (safe) +- **Development**: Continues if migration fails (non-blocking) + +**Creating migrations:** +```bash +# 1. Edit prisma/schema.prisma +# 2. Create migration file +npm run db:migrate + +# 3. Restart server - migration auto-applies +./docker-manage.sh restart development +``` + +## Database Scripts + +| Script | Purpose | +| -------------- | ----------------------------------- | +| `db:migrate` | Create new migration file | +| `db:studio` | Open Prisma Studio GUI | +| `db:reset` | Reset database (⚠️ destroys data) | +| `db:generate` | Regenerate Prisma Client | + +## Common Tasks + +**View database:** +```bash +npm run db:studio +# or +./docker-manage.sh db-shell development +``` + +**Check migration status:** +```bash +npx prisma migrate status +``` + +**Reset database (dev only):** +```bash +npm run db:reset +``` + +## Schema + +See [prisma/schema.prisma](../prisma/schema.prisma) for the complete schema. + +Models: User, Competency, CompetencyRelationship, LearningResource, CompetencyResourceLink + +## Troubleshooting + +**"Database schema is not in sync"** +```bash +npm run db:migrate +``` + +**"Prisma Client out of sync"** +```bash +npm run db:generate +``` + +**Production migration failed** +- Check `DATABASE_URL` environment variable +- View logs: `./docker-manage.sh logs production` diff --git a/Dockerfile b/Dockerfile index 4b528eb..487c7b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,8 @@ CMD ["npm", "run", "dev"] # Dependencies stage (for production) FROM base AS deps COPY package.json package-lock.json ./ -RUN npm ci --only=production --legacy-peer-deps +RUN npm ci --only=production --legacy-peer-deps && \ + npm install prisma --legacy-peer-deps # Builder stage FROM base AS builder @@ -37,5 +38,8 @@ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/next.config.ts ./next.config.ts +# Copy Prisma schema and migrations for runtime migration execution +COPY --from=builder /app/prisma ./prisma + EXPOSE 3000 CMD ["npm", "start"] diff --git a/README.md b/README.md index 27a3aba..d6d5c61 100644 --- a/README.md +++ b/README.md @@ -192,13 +192,21 @@ See [DOCKER.md](DOCKER.md) for detailed Docker setup information. See 7. Push to the branch: `git push origin feature/amazing-feature` 8. Open a Pull Request -## 📚 Learn More +## 📚 Documentation -To learn more about the technologies used: +### Project Documentation + +- [Database Setup & Migrations](DATABASE.md) - Database management, migrations, and Prisma + scripts +- [Docker Setup](DOCKER.md) - Detailed Docker configuration and deployment +- [Claude Code Guide](CLAUDE.md) - Project conventions and AI assistant usage + +### External Resources - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API - [Docker Documentation](https://docs.docker.com/) - containerization platform - [PostgreSQL Documentation](https://www.postgresql.org/docs/) - database system +- [Prisma Documentation](https://www.prisma.io/docs) - ORM and database toolkit ## 🆘 Troubleshooting diff --git a/docker/staging/docker-compose.yml b/docker/staging/docker-compose.yml index e6d7bb6..8e8dd4d 100644 --- a/docker/staging/docker-compose.yml +++ b/docker/staging/docker-compose.yml @@ -19,7 +19,8 @@ services: container_name: memo-staging-app image: 'ghcr.io/ls1intum/memo/memo-app:${IMAGE_TAG:-latest}' environment: - - NODE_ENV=staging + - NODE_ENV=production + - APP_ENV=staging - DATABASE_URL=${DATABASE_URL:-postgresql://memo_user:memo_password@db:5432/memo_staging} - NEXT_PUBLIC_APP_ENV=${NEXT_PUBLIC_APP_ENV:-staging} - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-https://staging.memo.aet.cit.tum.de/api} diff --git a/instrumentation.ts b/instrumentation.ts new file mode 100644 index 0000000..a56e012 --- /dev/null +++ b/instrumentation.ts @@ -0,0 +1,6 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === 'nodejs') { + const { runMigrations } = await import('./lib/run-migrations'); + await runMigrations(); + } +} diff --git a/lib/run-migrations.ts b/lib/run-migrations.ts new file mode 100644 index 0000000..ad8a4ee --- /dev/null +++ b/lib/run-migrations.ts @@ -0,0 +1,19 @@ +/* eslint-disable no-console */ +import { exec } from 'child_process'; +import { promisify } from 'util'; + +const execAsync = promisify(exec); + +export async function runMigrations() { + const isProduction = + process.env.NODE_ENV === 'production' || process.env.APP_ENV === 'staging'; + + try { + await execAsync('npx prisma migrate deploy'); + } catch { + if (isProduction) { + throw new Error('Database migration failed'); + } + console.warn('[Migrations] Failed - continuing in development'); + } +} diff --git a/package-lock.json b/package-lock.json index b7ff86c..dea1ecd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "lucide-react": "^0.525.0", "motion": "^12.23.24", "next": "^15.5.3", + "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.3.1", @@ -5539,6 +5540,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", diff --git a/package.json b/package.json index 8acda22..c732903 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "lucide-react": "^0.525.0", "motion": "^12.23.24", "next": "^15.5.3", + "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.3.1", diff --git a/prisma/migrations/20251202110245_init/migration.sql b/prisma/migrations/20251203125815_init/migration.sql similarity index 75% rename from prisma/migrations/20251202110245_init/migration.sql rename to prisma/migrations/20251203125815_init/migration.sql index 07cb464..687ea90 100644 --- a/prisma/migrations/20251202110245_init/migration.sql +++ b/prisma/migrations/20251203125815_init/migration.sql @@ -1,17 +1,21 @@ -CREATE TYPE "ContributorRole" AS ENUM ('CONTRIBUTOR', 'MAINTAINER', 'ADMIN'); +-- CreateEnum +CREATE TYPE "UserRole" AS ENUM ('USER', 'ADMIN'); +-- CreateEnum CREATE TYPE "RelationshipType" AS ENUM ('PREREQUISITE', 'HIERARCHICAL', 'RELATED'); -CREATE TABLE "contributors" ( +-- CreateTable +CREATE TABLE "users" ( "id" TEXT NOT NULL, "name" TEXT NOT NULL, "email" TEXT NOT NULL, - "role" "ContributorRole" NOT NULL DEFAULT 'CONTRIBUTOR', + "role" "UserRole" NOT NULL DEFAULT 'USER', "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT "contributors_pkey" PRIMARY KEY ("id") + CONSTRAINT "users_pkey" PRIMARY KEY ("id") ); +-- CreateTable CREATE TABLE "competencies" ( "id" TEXT NOT NULL, "title" TEXT NOT NULL, @@ -21,17 +25,19 @@ CREATE TABLE "competencies" ( CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") ); +-- CreateTable CREATE TABLE "competency_relationships" ( "id" TEXT NOT NULL, "relationshipType" "RelationshipType" NOT NULL, "originId" TEXT NOT NULL, "destinationId" TEXT NOT NULL, - "creatorId" TEXT NOT NULL, + "userId" TEXT NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "competency_relationships_pkey" PRIMARY KEY ("id") ); +-- CreateTable CREATE TABLE "learning_resources" ( "id" TEXT NOT NULL, "title" TEXT NOT NULL, @@ -41,26 +47,34 @@ CREATE TABLE "learning_resources" ( CONSTRAINT "learning_resources_pkey" PRIMARY KEY ("id") ); +-- CreateTable CREATE TABLE "competency_resource_links" ( "id" TEXT NOT NULL, "competencyId" TEXT NOT NULL, "resourceId" TEXT NOT NULL, - "creatorId" TEXT NOT NULL, + "userId" TEXT NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "competency_resource_links_pkey" PRIMARY KEY ("id") ); -CREATE UNIQUE INDEX "contributors_email_key" ON "contributors"("email"); +-- CreateIndex +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); +-- AddForeignKey ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_originId_fkey" FOREIGN KEY ("originId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; +-- AddForeignKey ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_destinationId_fkey" FOREIGN KEY ("destinationId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +-- AddForeignKey +ALTER TABLE "competency_relationships" ADD CONSTRAINT "competency_relationships_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +-- AddForeignKey ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; +-- AddForeignKey ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_resourceId_fkey" FOREIGN KEY ("resourceId") REFERENCES "learning_resources"("id") ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "contributors"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +-- AddForeignKey +ALTER TABLE "competency_resource_links" ADD CONSTRAINT "competency_resource_links_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c623572..138038f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -7,16 +7,16 @@ datasource db { url = env("DATABASE_URL") } -model Contributor { +model User { id String @id @default(cuid()) name String email String @unique - role ContributorRole @default(CONTRIBUTOR) + role UserRole @default(USER) createdAt DateTime @default(now()) createdRelationships CompetencyRelationship[] @relation("RelationshipCreator") createdResourceLinks CompetencyResourceLink[] @relation("ResourceLinkCreator") - @@map("contributors") + @@map("users") } model Competency { @@ -36,11 +36,11 @@ model CompetencyRelationship { relationshipType RelationshipType originId String destinationId String - creatorId String + userId String createdAt DateTime @default(now()) origin Competency @relation("OriginCompetency", fields: [originId], references: [id], onDelete: Cascade) destination Competency @relation("DestinationCompetency", fields: [destinationId], references: [id], onDelete: Cascade) - creator Contributor @relation("RelationshipCreator", fields: [creatorId], references: [id]) + user User @relation("RelationshipCreator", fields: [userId], references: [id]) @@map("competency_relationships") } @@ -59,18 +59,17 @@ model CompetencyResourceLink { id String @id @default(cuid()) competencyId String resourceId String - creatorId String + userId String createdAt DateTime @default(now()) competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) resource LearningResource @relation(fields: [resourceId], references: [id], onDelete: Cascade) - creator Contributor @relation("ResourceLinkCreator", fields: [creatorId], references: [id]) + user User @relation("ResourceLinkCreator", fields: [userId], references: [id]) @@map("competency_resource_links") } -enum ContributorRole { - CONTRIBUTOR - MAINTAINER +enum UserRole { + USER ADMIN } From 2b86a1654132245b2050e5e7441806f0ba38d9ad Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 3 Dec 2025 16:52:20 +0100 Subject: [PATCH 05/39] check resolved --- package-lock.json | 75 ++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index dea1ecd..a3a3006 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1030,7 +1030,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -1043,14 +1043,14 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1064,14 +1064,14 @@ "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.19.0", @@ -1083,7 +1083,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.19.0" @@ -1576,7 +1576,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@swc/helpers": { @@ -1910,7 +1910,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1920,7 +1920,7 @@ "version": "19.1.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -2803,7 +2803,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -2929,7 +2929,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -2955,7 +2955,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -3019,14 +3019,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -3051,7 +3051,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3144,7 +3144,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -3190,14 +3190,14 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/destr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -3227,7 +3227,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3255,7 +3255,7 @@ "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -3273,7 +3273,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -3913,14 +3913,14 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "devOptional": true, + "dev": true, "funding": [ { "type": "individual", @@ -4222,7 +4222,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -4889,7 +4889,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -5582,14 +5582,14 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -5732,7 +5732,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/optionator": { @@ -5847,14 +5847,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/picocolors": { @@ -5880,7 +5880,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -5957,7 +5957,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -6005,7 +6005,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "individual", @@ -6043,7 +6043,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -6082,7 +6082,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -6725,6 +6725,7 @@ "version": "4.1.11", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { @@ -6765,7 +6766,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6966,7 +6967,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", From 68bf56e572902c6f26d94b7aec629f0035e0a0bd Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 3 Dec 2025 17:03:49 +0100 Subject: [PATCH 06/39] feat: enhance database schema and migration system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add unique constraint on CompetencyRelationship to prevent duplicates - Add database indexes on CompetencyResourceLink foreign keys for performance - Make userId optional in CompetencyResourceLink with SetNull on delete - Add error logging and visibility in migration system - Move database docs to root as DATABASE.md - Sync package-lock.json with package.json (TypeScript 5.9.3) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- DATABASE.md | 2 +- instrumentation.ts | 9 ++++++++- lib/run-migrations.ts | 3 ++- package-lock.json | 6 +++--- prisma/schema.prisma | 8 ++++++-- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/DATABASE.md b/DATABASE.md index e7263b8..6642c7d 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -61,7 +61,7 @@ npm run db:reset ## Schema -See [prisma/schema.prisma](../prisma/schema.prisma) for the complete schema. +See [prisma/schema.prisma](prisma/schema.prisma) for the complete schema. Models: User, Competency, CompetencyRelationship, LearningResource, CompetencyResourceLink diff --git a/instrumentation.ts b/instrumentation.ts index a56e012..d1feda4 100644 --- a/instrumentation.ts +++ b/instrumentation.ts @@ -1,6 +1,13 @@ +/* eslint-disable no-console */ export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { const { runMigrations } = await import('./lib/run-migrations'); - await runMigrations(); + try { + await runMigrations(); + console.log('[Instrumentation] Migrations completed successfully'); + } catch (error) { + console.error('[Instrumentation] Migration failed:', error); + throw error; + } } } diff --git a/lib/run-migrations.ts b/lib/run-migrations.ts index ad8a4ee..0c3676f 100644 --- a/lib/run-migrations.ts +++ b/lib/run-migrations.ts @@ -10,7 +10,8 @@ export async function runMigrations() { try { await execAsync('npx prisma migrate deploy'); - } catch { + } catch (error) { + console.error('[Migrations] Error:', error); if (isProduction) { throw new Error('Database migration failed'); } diff --git a/package-lock.json b/package-lock.json index 5665334..7282718 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6937,9 +6937,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 138038f..aae77a2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -42,6 +42,7 @@ model CompetencyRelationship { destination Competency @relation("DestinationCompetency", fields: [destinationId], references: [id], onDelete: Cascade) user User @relation("RelationshipCreator", fields: [userId], references: [id]) + @@unique([originId, destinationId, relationshipType]) @@map("competency_relationships") } @@ -59,12 +60,15 @@ model CompetencyResourceLink { id String @id @default(cuid()) competencyId String resourceId String - userId String + userId String? createdAt DateTime @default(now()) competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) resource LearningResource @relation(fields: [resourceId], references: [id], onDelete: Cascade) - user User @relation("ResourceLinkCreator", fields: [userId], references: [id]) + user User? @relation("ResourceLinkCreator", fields: [userId], references: [id], onDelete: SetNull) + @@index([competencyId]) + @@index([resourceId]) + @@index([userId]) @@map("competency_resource_links") } From 5c538fe095cce8151bd3fcc6ba4dfbf6bd98c031 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 3 Dec 2025 17:06:23 +0100 Subject: [PATCH 07/39] style: fix prettier formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- DATABASE.md | 20 ++++++++++++++------ README.md | 3 +-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/DATABASE.md b/DATABASE.md index 6642c7d..e7885b9 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -18,10 +18,12 @@ PostgreSQL 16 with Prisma ORM. **Migrations auto-apply on server startup** in al ## How Migrations Work **Automatic on startup:** + - **Production/Staging**: Crashes if migration fails (safe) - **Development**: Continues if migration fails (non-blocking) **Creating migrations:** + ```bash # 1. Edit prisma/schema.prisma # 2. Create migration file @@ -33,16 +35,17 @@ npm run db:migrate ## Database Scripts -| Script | Purpose | -| -------------- | ----------------------------------- | -| `db:migrate` | Create new migration file | -| `db:studio` | Open Prisma Studio GUI | -| `db:reset` | Reset database (⚠️ destroys data) | -| `db:generate` | Regenerate Prisma Client | +| Script | Purpose | +| ------------- | --------------------------------- | +| `db:migrate` | Create new migration file | +| `db:studio` | Open Prisma Studio GUI | +| `db:reset` | Reset database (⚠️ destroys data) | +| `db:generate` | Regenerate Prisma Client | ## Common Tasks **View database:** + ```bash npm run db:studio # or @@ -50,11 +53,13 @@ npm run db:studio ``` **Check migration status:** + ```bash npx prisma migrate status ``` **Reset database (dev only):** + ```bash npm run db:reset ``` @@ -68,15 +73,18 @@ Models: User, Competency, CompetencyRelationship, LearningResource, CompetencyRe ## Troubleshooting **"Database schema is not in sync"** + ```bash npm run db:migrate ``` **"Prisma Client out of sync"** + ```bash npm run db:generate ``` **Production migration failed** + - Check `DATABASE_URL` environment variable - View logs: `./docker-manage.sh logs production` diff --git a/README.md b/README.md index d6d5c61..259b833 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,7 @@ See [DOCKER.md](DOCKER.md) for detailed Docker setup information. See ### Project Documentation -- [Database Setup & Migrations](DATABASE.md) - Database management, migrations, and Prisma - scripts +- [Database Setup & Migrations](DATABASE.md) - Database management, migrations, and Prisma scripts - [Docker Setup](DOCKER.md) - Detailed Docker configuration and deployment - [Claude Code Guide](CLAUDE.md) - Project conventions and AI assistant usage From 0d2d8ddf50c9349dc4231f9b536cc8f145ba6285 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 9 Dec 2025 10:35:36 +0100 Subject: [PATCH 08/39] docs: clarify Prisma Studio usage and DATABASE_URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- DATABASE.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/DATABASE.md b/DATABASE.md index e7885b9..9e37278 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -47,8 +47,11 @@ npm run db:migrate **View database:** ```bash +# Prisma Studio (GUI) - requires .env file with DATABASE_URL npm run db:studio -# or +# Opens at http://localhost:5555 + +# Or connect to PostgreSQL directly ./docker-manage.sh db-shell development ``` @@ -88,3 +91,7 @@ npm run db:generate - Check `DATABASE_URL` environment variable - View logs: `./docker-manage.sh logs production` + +**"Environment variable not found: DATABASE_URL"** + +- Run `npm run db:studio` From 1b6cb4f5f61cc086af1e79bbd64abcf272dda961 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 9 Dec 2025 10:42:00 +0100 Subject: [PATCH 09/39] docs: clarify database setup and .env file usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- DATABASE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/DATABASE.md b/DATABASE.md index 9e37278..e30ed18 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -4,6 +4,25 @@ PostgreSQL 16 with Prisma ORM. **Migrations auto-apply on server startup** in all environments. +## Setup + +**Using Docker (recommended):** + +```bash +./docker-manage.sh up development +``` + +- Database runs automatically in Docker +- No `.env` file needed - `DATABASE_URL` is set in `docker-compose.yml` +- Migrations run on startup + +**Using Prisma Studio GUI:** + +1. Ensure Docker database is running: `./docker-manage.sh up development` +2. Create `.env` file in project root with `DATABASE_URL` from + `docker/development/docker-compose.yml` +3. Run: `npm run db:studio` + ## Quick Start ```bash From 626561cdf1828fd6eeae04314287eae89aca952b Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 9 Dec 2025 10:43:06 +0100 Subject: [PATCH 10/39] remove unnecessary code --- DATABASE.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/DATABASE.md b/DATABASE.md index e30ed18..a96049d 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -110,7 +110,3 @@ npm run db:generate - Check `DATABASE_URL` environment variable - View logs: `./docker-manage.sh logs production` - -**"Environment variable not found: DATABASE_URL"** - -- Run `npm run db:studio` From 9b19b6896fbec9cd737c4807f22359bd13473ecb Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 9 Dec 2025 10:47:40 +0100 Subject: [PATCH 11/39] fix npm Ci issues --- package-lock.json | 153 +++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7282718..7aca01c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -813,9 +813,9 @@ } }, "node_modules/@next/env": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz", - "integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz", + "integrity": "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -829,9 +829,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.3.tgz", - "integrity": "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", + "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==", "cpu": [ "arm64" ], @@ -845,9 +845,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.3.tgz", - "integrity": "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz", + "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==", "cpu": [ "x64" ], @@ -861,9 +861,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.3.tgz", - "integrity": "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz", + "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==", "cpu": [ "arm64" ], @@ -877,9 +877,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.3.tgz", - "integrity": "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz", + "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==", "cpu": [ "arm64" ], @@ -893,9 +893,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.3.tgz", - "integrity": "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", + "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==", "cpu": [ "x64" ], @@ -909,9 +909,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.3.tgz", - "integrity": "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", + "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==", "cpu": [ "x64" ], @@ -925,9 +925,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.3.tgz", - "integrity": "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz", + "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==", "cpu": [ "arm64" ], @@ -941,9 +941,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.3.tgz", - "integrity": "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz", + "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==", "cpu": [ "x64" ], @@ -1030,7 +1030,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -1043,14 +1043,14 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1064,14 +1064,14 @@ "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.19.0", @@ -1083,7 +1083,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.19.0" @@ -1576,7 +1576,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@swc/helpers": { @@ -1910,7 +1910,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1920,7 +1920,7 @@ "version": "19.1.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -2773,7 +2773,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -2899,7 +2899,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -2925,7 +2925,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -2989,14 +2989,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -3021,7 +3021,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3114,7 +3114,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -3160,14 +3160,14 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/destr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -3197,7 +3197,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3225,7 +3225,7 @@ "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -3243,7 +3243,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -3883,14 +3883,14 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -4192,7 +4192,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -4859,7 +4859,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -5459,12 +5459,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", - "integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz", + "integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==", "license": "MIT", "dependencies": { - "@next/env": "15.5.3", + "@next/env": "15.5.7", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -5477,14 +5477,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.3", - "@next/swc-darwin-x64": "15.5.3", - "@next/swc-linux-arm64-gnu": "15.5.3", - "@next/swc-linux-arm64-musl": "15.5.3", - "@next/swc-linux-x64-gnu": "15.5.3", - "@next/swc-linux-x64-musl": "15.5.3", - "@next/swc-win32-arm64-msvc": "15.5.3", - "@next/swc-win32-x64-msvc": "15.5.3", + "@next/swc-darwin-arm64": "15.5.7", + "@next/swc-darwin-x64": "15.5.7", + "@next/swc-linux-arm64-gnu": "15.5.7", + "@next/swc-linux-arm64-musl": "15.5.7", + "@next/swc-linux-x64-gnu": "15.5.7", + "@next/swc-linux-x64-musl": "15.5.7", + "@next/swc-win32-arm64-msvc": "15.5.7", + "@next/swc-win32-x64-msvc": "15.5.7", "sharp": "^0.34.3" }, "peerDependencies": { @@ -5552,14 +5552,14 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -5702,7 +5702,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/optionator": { @@ -5817,14 +5817,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/picocolors": { @@ -5850,7 +5850,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -5927,7 +5927,7 @@ "version": "6.19.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -5975,7 +5975,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -6013,7 +6013,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -6052,7 +6052,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -6695,7 +6695,6 @@ "version": "4.1.11", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", - "dev": true, "license": "MIT" }, "node_modules/tapable": { @@ -6736,7 +6735,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -6940,7 +6939,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", From 1bffd0d48d19e059169dcea56818e51e91e5e15a Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 9 Dec 2025 13:56:49 +0100 Subject: [PATCH 12/39] update doc --- DATABASE.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/DATABASE.md b/DATABASE.md index a96049d..8ddd6a5 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -69,11 +69,12 @@ npm run db:migrate # Prisma Studio (GUI) - requires .env file with DATABASE_URL npm run db:studio # Opens at http://localhost:5555 - -# Or connect to PostgreSQL directly -./docker-manage.sh db-shell development ``` +Alternatively, use VS Code PostgreSQL extensions with connection: +- Host: `localhost`, Port: `5432`, Database: `memo_dev` +- Username: `memo_user`, Password: `memo_password` + **Check migration status:** ```bash From c7fe36a26b14e6a26341d123c51f735c17a4cd1d Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Tue, 9 Dec 2025 14:08:58 +0100 Subject: [PATCH 13/39] fix CI issued --- DATABASE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DATABASE.md b/DATABASE.md index 8ddd6a5..eb1231a 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -72,6 +72,7 @@ npm run db:studio ``` Alternatively, use VS Code PostgreSQL extensions with connection: + - Host: `localhost`, Port: `5432`, Database: `memo_dev` - Username: `memo_user`, Password: `memo_password` From a7f4c7fb9bf10e496b6c27b4d257e421c228d340 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 10 Dec 2025 12:33:28 +0100 Subject: [PATCH 14/39] fix inconsistency between prisma clinet and db setup file --- prisma/schema.prisma | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index aae77a2..46d2bf2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -60,11 +60,11 @@ model CompetencyResourceLink { id String @id @default(cuid()) competencyId String resourceId String - userId String? + userId String createdAt DateTime @default(now()) competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) resource LearningResource @relation(fields: [resourceId], references: [id], onDelete: Cascade) - user User? @relation("ResourceLinkCreator", fields: [userId], references: [id], onDelete: SetNull) + user User @relation("ResourceLinkCreator", fields: [userId], references: [id]) @@index([competencyId]) @@index([resourceId]) From abb04f0a602b750e2ae421569cbf379023af3bf3 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 10 Dec 2025 14:42:30 +0100 Subject: [PATCH 15/39] Update prisma/migrations/20251203125815_init/migration.sql Co-authored-by: Maximilian Anzinger <44003963+MaximilianAnzinger@users.noreply.github.com> --- prisma/migrations/20251203125815_init/migration.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/migrations/20251203125815_init/migration.sql b/prisma/migrations/20251203125815_init/migration.sql index 687ea90..f9302b2 100644 --- a/prisma/migrations/20251203125815_init/migration.sql +++ b/prisma/migrations/20251203125815_init/migration.sql @@ -2,7 +2,7 @@ CREATE TYPE "UserRole" AS ENUM ('USER', 'ADMIN'); -- CreateEnum -CREATE TYPE "RelationshipType" AS ENUM ('PREREQUISITE', 'HIERARCHICAL', 'RELATED'); +CREATE TYPE "RelationshipType" AS ENUM ('ASSUMES', 'EXTENDS', 'MATCHES'); -- CreateTable CREATE TABLE "users" ( From 791f7f38694326f088e12c73fcd3d8f5ecb0f600 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 10 Dec 2025 14:42:49 +0100 Subject: [PATCH 16/39] Update prisma/schema.prisma Co-authored-by: Maximilian Anzinger <44003963+MaximilianAnzinger@users.noreply.github.com> --- prisma/schema.prisma | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 46d2bf2..91d12c1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -78,8 +78,8 @@ enum UserRole { } enum RelationshipType { - PREREQUISITE - HIERARCHICAL - RELATED + ASSUMES + EXTENDS + MATCHES } From 555c4b78236cb5c3a7d0591958ff84769aa94595 Mon Sep 17 00:00:00 2001 From: Viktoriya Totalova Date: Wed, 10 Dec 2025 15:21:09 +0100 Subject: [PATCH 17/39] set up domain core --- DOMAIN_CORE.md | 218 ++++++++++++++++++++ app/actions/competencies.ts | 77 ++++++++ app/actions/competency_relationships.ts | 88 +++++++++ app/actions/competency_resource_links.ts | 84 ++++++++ app/actions/learning_resources.ts | 73 +++++++ app/actions/users.ts | 56 ++++++ components/navbar.tsx | 2 +- components/ui/accordion.tsx | 2 +- components/ui/avatar.tsx | 2 +- components/ui/badge.tsx | 2 +- components/ui/button.tsx | 2 +- components/ui/card.tsx | 2 +- components/ui/label.tsx | 2 +- components/ui/radio-group.tsx | 2 +- components/ui/tabs.tsx | 2 +- instrumentation.ts | 2 +- lib/{ => client}/prisma.ts | 0 lib/{ => client}/run-migrations.ts | 0 lib/{ => client}/utils.ts | 0 lib/domain/domain_core.ts | 84 ++++++++ lib/repositories/dc_interface.ts | 59 ++++++ lib/repositories/dc_repo.ts | 242 +++++++++++++++++++++++ lib/services/competency.ts | 35 ++++ lib/services/competency_relationship.ts | 43 ++++ lib/services/competency_resource_link.ts | 40 ++++ lib/services/learning_resource.ts | 46 +++++ lib/services/user.ts | 45 +++++ 27 files changed, 1200 insertions(+), 10 deletions(-) create mode 100644 DOMAIN_CORE.md create mode 100644 app/actions/competencies.ts create mode 100644 app/actions/competency_relationships.ts create mode 100644 app/actions/competency_resource_links.ts create mode 100644 app/actions/learning_resources.ts create mode 100644 app/actions/users.ts rename lib/{ => client}/prisma.ts (100%) rename lib/{ => client}/run-migrations.ts (100%) rename lib/{ => client}/utils.ts (100%) create mode 100644 lib/domain/domain_core.ts create mode 100644 lib/repositories/dc_interface.ts create mode 100644 lib/repositories/dc_repo.ts create mode 100644 lib/services/competency.ts create mode 100644 lib/services/competency_relationship.ts create mode 100644 lib/services/competency_resource_link.ts create mode 100644 lib/services/learning_resource.ts create mode 100644 lib/services/user.ts diff --git a/DOMAIN_CORE.md b/DOMAIN_CORE.md new file mode 100644 index 0000000..e797ef3 --- /dev/null +++ b/DOMAIN_CORE.md @@ -0,0 +1,218 @@ +# Domain Core Architecture + +This project uses a layered architecture where **only the domain core communicates with the +database**. + +## Architecture Flow + +``` +Client Side + ↓ +Server Actions (app/actions/) + ↓ +Service Layer (lib/services/) + ↓ +Repository Interface (lib/repositories/dc_interface.ts) + ↓ +Repository Implementation (lib/repositories/dc_user_repo.ts) ← ONLY layer that touches DB + ↓ +Prisma Client (lib/prisma.ts) + ↓ +PostgreSQL Database +``` + +## How It Works + +### 1. Domain Layer (`lib/domain/domain_core.ts`) + +Pure entities and input types. No database dependencies. + +```typescript +export interface Competency { + id: string; + title: string; + description: string | null; + createdAt: Date; +} + +export interface CreateCompetencyInput { + title: string; + description?: string; +} +``` + +### 2. Repository Interface (`lib/repositories/dc_interface.ts`) + +Contract definition. No implementation. + +```typescript +export interface CompetencyRepository { + create(data: CreateCompetencyInput): Promise; + findById(id: string): Promise; + findAll(): Promise; +} +``` + +### 3. Repository Implementation (`lib/repositories/dc_user_repo.ts`) + +Prisma implementation. **ONLY this layer touches the database.** + +```typescript +export class PrismaCompetencyRepository implements CompetencyRepository { + async create(data: CreateCompetencyInput): Promise { + return await prisma.competency.create({ data }); + } +} + +export const competencyRepository = new PrismaCompetencyRepository(); +``` + +### 4. Service Layer (`lib/services/`) + +Business logic and validation. + +```typescript +export class CompetencyService { + constructor(private readonly repository: CompetencyRepository) {} + + async createCompetency(data: CreateCompetencyInput) { + return await this.repository.create(data); + } +} + +export const competencyService = new CompetencyService(competencyRepository); +``` + +### 5. Server Actions (`app/actions/`) + +Exposes operations to Client Side. + +```typescript +'use server'; + +import { competencyService } from '@/lib/services/competency-service'; + +export async function createCompetencyAction(formData: FormData) { + try { + const title = formData.get('title') as string; + const competency = await competencyService.createCompetency({ title }); + return { success: true, competency }; + } catch (error) { + return { success: false, error: error.message }; + } +} +``` + +## Usage from Client Side + +### Server Components (Default) + +```tsx +import { getAllCompetenciesAction } from '@/app/actions/competencies'; + +export default async function CompetenciesPage() { + const { success, competencies } = await getAllCompetenciesAction(); + + if (!success) return
Error loading competencies
; + + return ( +
+ {competencies?.map((comp) => ( +
+

{comp.title}

+

{comp.description}

+
+ ))} +
+ ); +} +``` + +### Client Components + +```tsx +'use client'; + +import { createCompetencyAction } from '@/app/actions/competencies'; +import { useState } from 'react'; + +export default function CreateCompetencyForm() { + const [message, setMessage] = useState(''); + + async function handleSubmit(formData: FormData) { + const result = await createCompetencyAction(formData); + setMessage(result.success ? 'Created!' : result.error); + } + + return ( +
+ +