diff --git a/messages/en-US.json b/messages/en-US.json index faf1cf8..bfd4c03 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -24,7 +24,22 @@ "noFoldersYet": "No folders yet", "folderInfo": "ID: {id} • {totalPairs} pairs", "enterFolderName": "Enter folder name:", - "confirmDelete": "Type \"{name}\" to delete:" + "confirmDelete": "Type \"{name}\" to delete:", + "myFolders": "My Folders", + "publicFolders": "Public Folders", + "public": "Public", + "private": "Private", + "setPublic": "Set Public", + "setPrivate": "Set Private", + "publicFolderInfo": "{userName} • {totalPairs} pairs", + "searchPlaceholder": "Search public folders...", + "loading": "Loading...", + "noPublicFolders": "No public folders found", + "unknownUser": "Unknown User", + "enterNewName": "Enter new name:", + "favorite": "Favorite", + "unfavorite": "Unfavorite", + "pleaseLogin": "Please login first" }, "folder_id": { "unauthorized": "You are not the owner of this folder", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 427f1e7..59c99d5 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -24,7 +24,22 @@ "noFoldersYet": "还没有文件夹", "folderInfo": "ID: {id} • {totalPairs} 个文本对", "enterFolderName": "输入文件夹名称:", - "confirmDelete": "输入 \"{name}\" 以删除:" + "confirmDelete": "输入 \"{name}\" 以删除:", + "myFolders": "我的文件夹", + "publicFolders": "公开文件夹", + "public": "公开", + "private": "私有", + "setPublic": "设为公开", + "setPrivate": "设为私有", + "publicFolderInfo": "{userName} • {totalPairs} 个文本对", + "searchPlaceholder": "搜索公开文件夹...", + "loading": "加载中...", + "noPublicFolders": "没有找到公开文件夹", + "unknownUser": "未知用户", + "enterNewName": "输入新名称:", + "favorite": "收藏", + "unfavorite": "取消收藏", + "pleaseLogin": "请先登录" }, "folder_id": { "unauthorized": "您不是此文件夹的所有者", diff --git a/package.json b/package.json index 5fffbc7..e97bf19 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "lint": "eslint" }, "dependencies": { - "@prisma/adapter-pg": "^7.2.0", - "@prisma/client": "^7.2.0", + "@prisma/adapter-pg": "^7.4.2", + "@prisma/client": "7.4.2", "bcryptjs": "^3.0.3", "better-auth": "^1.4.10", "class-variance-authority": "^0.7.1", @@ -43,7 +43,7 @@ "eslint": "^9.39.2", "eslint-config-next": "16.1.1", "eslint-plugin-react": "^7.37.5", - "prisma": "^7.2.0", + "prisma": "^7.4.2", "tailwindcss": "^4.1.18", "typescript": "^5.9.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5d51bb..6c80911 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,17 +13,17 @@ importers: .: dependencies: '@prisma/adapter-pg': - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^7.4.2 + version: 7.4.2 '@prisma/client': - specifier: ^7.2.0 - version: 7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) + specifier: 7.4.2 + version: 7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) bcryptjs: specifier: ^3.0.3 version: 3.0.3 better-auth: specifier: ^1.4.10 - version: 1.4.10(@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 1.4.10(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -69,7 +69,7 @@ importers: devDependencies: '@better-auth/cli': specifier: ^1.4.10 - version: 1.4.10(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.2)(@types/react@19.2.7)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(mysql2@3.15.3)(nanostores@1.1.0)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 1.4.10(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@types/react@19.2.7)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(mysql2@3.15.3)(nanostores@1.1.0)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@eslint/eslintrc': specifier: ^3.3.3 version: 3.3.3 @@ -104,8 +104,8 @@ importers: specifier: ^7.37.5 version: 7.37.5(eslint@9.39.2(jiti@2.6.1)) prisma: - specifier: ^7.2.0 - version: 7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + specifier: ^7.4.2 + version: 7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) tailwindcss: specifier: ^4.1.18 version: 4.1.18 @@ -321,19 +321,19 @@ packages: '@clack/prompts@0.11.0': resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} - '@electric-sql/pglite-socket@0.0.6': - resolution: {integrity: sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==} + '@electric-sql/pglite-socket@0.0.20': + resolution: {integrity: sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==} hasBin: true peerDependencies: - '@electric-sql/pglite': 0.3.2 + '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite-tools@0.2.7': - resolution: {integrity: sha512-9dAccClqxx4cZB+Ar9B+FZ5WgxDc/Xvl9DPrTWv+dYTf0YNubLzi4wHHRGRGhrJv15XwnyKcGOZAP1VXSneSUg==} + '@electric-sql/pglite-tools@0.2.20': + resolution: {integrity: sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==} peerDependencies: - '@electric-sql/pglite': 0.3.2 + '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite@0.3.2': - resolution: {integrity: sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==} + '@electric-sql/pglite@0.3.15': + resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==} '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -400,8 +400,8 @@ packages: '@formatjs/intl-localematcher@0.6.2': resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} - '@hono/node-server@1.19.6': - resolution: {integrity: sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==} + '@hono/node-server@1.19.9': + resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -452,89 +452,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -575,10 +591,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@mrleebo/prisma-ast@0.12.1': - resolution: {integrity: sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==} - engines: {node: '>=16'} - '@mrleebo/prisma-ast@0.13.1': resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==} engines: {node: '>=16'} @@ -609,24 +621,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.1.1': resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.1.1': resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.1.1': resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.1.1': resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==} @@ -693,36 +709,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -746,11 +768,11 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} - '@prisma/adapter-pg@7.2.0': - resolution: {integrity: sha512-euIdQ13cRB2wZ3jPsnDnFhINquo1PYFPCg6yVL8b2rp3EdinQHsX9EDdCtRr489D5uhphcRk463OdQAFlsCr0w==} + '@prisma/adapter-pg@7.4.2': + resolution: {integrity: sha512-oUo2Zhe9Tf6YwVL8kLPuOLTK1Z2pwi/Ua77t2PuGyBan2w7shRKqHvYK+3XXmRH9RWhPJ4SMtHZKpNo6Ax/4bQ==} - '@prisma/client-runtime-utils@7.2.0': - resolution: {integrity: sha512-dn7oB53v0tqkB0wBdMuTNFNPdEbfICEUe82Tn9FoKAhJCUkDH+fmyEp0ClciGh+9Hp2Tuu2K52kth2MTLstvmA==} + '@prisma/client-runtime-utils@7.4.2': + resolution: {integrity: sha512-cID+rzOEb38VyMsx5LwJMEY4NGIrWCNpKu/0ImbeooQ2Px7TI+kOt7cm0NelxUzF2V41UVVXAmYjANZQtCu1/Q==} '@prisma/client@5.22.0': resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==} @@ -761,8 +783,8 @@ packages: prisma: optional: true - '@prisma/client@7.2.0': - resolution: {integrity: sha512-JdLF8lWZ+LjKGKpBqyAlenxd/kXjd1Abf/xK+6vUA7R7L2Suo6AFTHFRpPSdAKCan9wzdFApsUpSa/F6+t1AtA==} + '@prisma/client@7.4.2': + resolution: {integrity: sha512-ts2mu+cQHriAhSxngO3StcYubBGTWDtu/4juZhXCUKOwgh26l+s4KD3vT2kMUzFyrYnll9u/3qWrtzRv9CGWzA==} engines: {node: ^20.19 || ^22.12 || >=24.0} peerDependencies: prisma: '*' @@ -773,41 +795,41 @@ packages: typescript: optional: true - '@prisma/config@7.2.0': - resolution: {integrity: sha512-qmvSnfQ6l/srBW1S7RZGfjTQhc44Yl3ldvU6y3pgmuLM+83SBDs6UQVgMtQuMRe9J3gGqB0RF8wER6RlXEr6jQ==} - - '@prisma/debug@6.8.2': - resolution: {integrity: sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==} + '@prisma/config@7.4.2': + resolution: {integrity: sha512-CftBjWxav99lzY1Z4oDgomdb1gh9BJFAOmWF6P2v1xRfXqQb56DfBub+QKcERRdNoAzCb3HXy3Zii8Vb4AsXhg==} '@prisma/debug@7.2.0': resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} - '@prisma/dev@0.17.0': - resolution: {integrity: sha512-6sGebe5jxX+FEsQTpjHLzvOGPn6ypFQprcs3jcuIWv1Xp/5v6P/rjfdvAwTkP2iF6pDx2tCd8vGLNWcsWzImTA==} + '@prisma/debug@7.4.2': + resolution: {integrity: sha512-aP7qzu+g/JnbF6U69LMwHoUkELiserKmWsE2shYuEpNUJ4GrtxBCvZwCyCBHFSH2kLTF2l1goBlBh4wuvRq62w==} - '@prisma/driver-adapter-utils@7.2.0': - resolution: {integrity: sha512-gzrUcbI9VmHS24Uf+0+7DNzdIw7keglJsD5m/MHxQOU68OhGVzlphQRobLiDMn8CHNA2XN8uugwKjudVtnfMVQ==} + '@prisma/dev@0.20.0': + resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==} - '@prisma/engines-version@7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3': - resolution: {integrity: sha512-KezsjCZDsbjNR7SzIiVlUsn9PnLePI7r5uxABlwL+xoerurZTfgQVbIjvjF2sVr3Uc0ZcsnREw3F84HvbggGdA==} + '@prisma/driver-adapter-utils@7.4.2': + resolution: {integrity: sha512-REdjFpT/ye9KdDs+CXAXPIbMQkVLhne9G5Pe97sNY4Ovx4r2DAbWM9hOFvvB1Oq8H8bOCdu0Ri3AoGALquQqVw==} - '@prisma/engines@7.2.0': - resolution: {integrity: sha512-HUeOI/SvCDsHrR9QZn24cxxZcujOjcS3w1oW/XVhnSATAli5SRMOfp/WkG3TtT5rCxDA4xOnlJkW7xkho4nURA==} + '@prisma/engines-version@7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919': + resolution: {integrity: sha512-5FIKY3KoYQlBuZC2yc16EXfVRQ8HY+fLqgxkYfWCtKhRb3ajCRzP/rPeoSx11+NueJDANdh4hjY36mdmrTcGSg==} - '@prisma/fetch-engine@7.2.0': - resolution: {integrity: sha512-Z5XZztJ8Ap+wovpjPD2lQKnB8nWFGNouCrglaNFjxIWAGWz0oeHXwUJRiclIoSSXN/ptcs9/behptSk8d0Yy6w==} + '@prisma/engines@7.4.2': + resolution: {integrity: sha512-B+ZZhI4rXlzjVqRw/93AothEKOU5/x4oVyJFGo9RpHPnBwaPwk4Pi0Q4iGXipKxeXPs/dqljgNBjK0m8nocOJA==} - '@prisma/get-platform@6.8.2': - resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==} + '@prisma/fetch-engine@7.4.2': + resolution: {integrity: sha512-f/c/MwYpdJO7taLETU8rahEstLeXfYgQGlz5fycG7Fbmva3iPdzGmjiSWHeSWIgNnlXnelUdCJqyZnFocurZuA==} '@prisma/get-platform@7.2.0': resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} - '@prisma/query-plan-executor@6.18.0': - resolution: {integrity: sha512-jZ8cfzFgL0jReE1R10gT8JLHtQxjWYLiQ//wHmVYZ2rVkFHoh0DT8IXsxcKcFlfKN7ak7k6j0XMNn2xVNyr5cA==} + '@prisma/get-platform@7.4.2': + resolution: {integrity: sha512-UTnChXRwiauzl/8wT4hhe7Xmixja9WE28oCnGpBtRejaHhvekx5kudr3R4Y9mLSA0kqGnAMeyTiKwDVMjaEVsw==} - '@prisma/studio-core@0.9.0': - resolution: {integrity: sha512-xA2zoR/ADu/NCSQuriBKTh6Ps4XjU0bErkEcgMfnSGh346K1VI7iWKnoq1l2DoxUqiddPHIEWwtxJ6xCHG6W7g==} + '@prisma/query-plan-executor@7.2.0': + resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} + + '@prisma/studio-core@0.13.1': + resolution: {integrity: sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==} peerDependencies: '@types/react': 19.2.7 react: ^18.0.0 || ^19.0.0 @@ -822,6 +844,9 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@swc/core-darwin-arm64@1.15.3': resolution: {integrity: sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==} engines: {node: '>=10'} @@ -845,24 +870,28 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [glibc] '@swc/core-linux-arm64-musl@1.15.3': resolution: {integrity: sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==} engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [musl] '@swc/core-linux-x64-gnu@1.15.3': resolution: {integrity: sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [glibc] '@swc/core-linux-x64-musl@1.15.3': resolution: {integrity: sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [musl] '@swc/core-win32-arm64-msvc@1.15.3': resolution: {integrity: sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==} @@ -938,24 +967,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.18': resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.18': resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.18': resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.18': resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} @@ -1181,41 +1214,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -1519,6 +1560,9 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -2003,8 +2047,8 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-port-please@3.1.2: - resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} + get-port-please@3.2.0: + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} @@ -2057,6 +2101,9 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphmatch@1.1.1: + resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} + h3@1.15.4: resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} @@ -2093,15 +2140,15 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hono@4.10.6: - resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} + hono@4.11.4: + resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} engines: {node: '>=16.9.0'} http-status-codes@2.3.0: resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} ieee754@1.2.1: @@ -2367,24 +2414,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} @@ -2429,8 +2480,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru.min@1.1.3: - resolution: {integrity: sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==} + lru.min@1.1.4: + resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} lucide-react@0.562.0: @@ -2477,8 +2528,8 @@ packages: resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} engines: {node: '>= 8.0'} - named-placeholders@1.1.4: - resolution: {integrity: sha512-/qfG0Kk/bLJIvej4FcPQ2KYUJP8iQdU1CTxysNb/U2wUNb+/4K485yeio8iNoiwfqJnsTInXoRPTza0dZWHVJQ==} + named-placeholders@1.1.6: + resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} engines: {node: '>=8.0.0'} nanoid@3.3.11: @@ -2750,8 +2801,8 @@ packages: engines: {node: '>=14'} hasBin: true - prisma@7.2.0: - resolution: {integrity: sha512-jSdHWgWOgFF24+nRyyNRVBIgGDQEsMEF8KPHvhBBg3jWyR9fUAK0Nq9ThUmiGlNgq2FA7vSk/ZoCvefod+a8qg==} + prisma@7.4.2: + resolution: {integrity: sha512-2bP8Ruww3Q95Z2eH4Yqh4KAENRsj/SxbdknIVBfd6DmjPwmpsC4OVFMLOeHt6tM3Amh8ebjvstrUz3V/hOe1dA==} engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: @@ -2827,8 +2878,8 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - remeda@2.21.3: - resolution: {integrity: sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg==} + remeda@2.33.4: + resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -2977,8 +3028,8 @@ packages: stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} - std-env@3.9.0: - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -3097,10 +3148,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -3283,8 +3330,8 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} - zeptomatch@2.0.2: - resolution: {integrity: sha512-H33jtSKf8Ijtb5BW6wua3G5DhnFjbFML36eFu+VdOoVY4HD9e7ggjqdM6639B+L87rjnR6Y+XeRzBXZdy52B/g==} + zeptomatch@2.1.0: + resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} @@ -3544,7 +3591,7 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@better-auth/cli@1.4.10(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.2)(@types/react@19.2.7)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(mysql2@3.15.3)(nanostores@1.1.0)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@better-auth/cli@1.4.10(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@types/react@19.2.7)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(mysql2@3.15.3)(nanostores@1.1.0)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/core': 7.28.5 '@babel/preset-react': 7.28.5(@babel/core@7.28.5) @@ -3554,15 +3601,15 @@ snapshots: '@better-auth/utils': 0.3.0 '@clack/prompts': 0.11.0 '@mrleebo/prisma-ast': 0.13.1 - '@prisma/client': 5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)) + '@prisma/client': 5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)) '@types/pg': 8.15.6 - better-auth: 1.4.10(@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + better-auth: 1.4.10(@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) better-sqlite3: 12.5.0 c12: 3.3.2 chalk: 5.6.2 commander: 12.1.0 dotenv: 17.2.3 - drizzle-orm: 0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3) + drizzle-orm: 0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3) open: 10.2.0 pg: 8.16.3 prettier: 3.7.4 @@ -3661,15 +3708,15 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 - '@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)': + '@electric-sql/pglite-socket@0.0.20(@electric-sql/pglite@0.3.15)': dependencies: - '@electric-sql/pglite': 0.3.2 + '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite-tools@0.2.7(@electric-sql/pglite@0.3.2)': + '@electric-sql/pglite-tools@0.2.20(@electric-sql/pglite@0.3.15)': dependencies: - '@electric-sql/pglite': 0.3.2 + '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite@0.3.2': {} + '@electric-sql/pglite@0.3.15': {} '@emnapi/core@1.7.1': dependencies: @@ -3763,9 +3810,9 @@ snapshots: dependencies: tslib: 2.8.1 - '@hono/node-server@1.19.6(hono@4.10.6)': + '@hono/node-server@1.19.9(hono@4.11.4)': dependencies: - hono: 4.10.6 + hono: 4.11.4 '@humanfs/core@0.19.1': {} @@ -3894,11 +3941,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@mrleebo/prisma-ast@0.12.1': - dependencies: - chevrotain: 10.5.0 - lilconfig: 2.1.0 - '@mrleebo/prisma-ast@0.13.1': dependencies: chevrotain: 10.5.0 @@ -4019,28 +4061,28 @@ snapshots: '@parcel/watcher-win32-ia32': 2.5.1 '@parcel/watcher-win32-x64': 2.5.1 - '@prisma/adapter-pg@7.2.0': + '@prisma/adapter-pg@7.4.2': dependencies: - '@prisma/driver-adapter-utils': 7.2.0 + '@prisma/driver-adapter-utils': 7.4.2 pg: 8.16.3 postgres-array: 3.0.4 transitivePeerDependencies: - pg-native - '@prisma/client-runtime-utils@7.2.0': {} + '@prisma/client-runtime-utils@7.4.2': {} - '@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))': + '@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))': optionalDependencies: - prisma: 7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + prisma: 7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3)': + '@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@prisma/client-runtime-utils': 7.2.0 + '@prisma/client-runtime-utils': 7.4.2 optionalDependencies: - prisma: 7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + prisma: 7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) typescript: 5.9.3 - '@prisma/config@7.2.0': + '@prisma/config@7.4.2': dependencies: c12: 3.1.0 deepmerge-ts: 7.1.5 @@ -4049,62 +4091,62 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma/debug@6.8.2': {} - '@prisma/debug@7.2.0': {} - '@prisma/dev@0.17.0(typescript@5.9.3)': + '@prisma/debug@7.4.2': {} + + '@prisma/dev@0.20.0(typescript@5.9.3)': dependencies: - '@electric-sql/pglite': 0.3.2 - '@electric-sql/pglite-socket': 0.0.6(@electric-sql/pglite@0.3.2) - '@electric-sql/pglite-tools': 0.2.7(@electric-sql/pglite@0.3.2) - '@hono/node-server': 1.19.6(hono@4.10.6) - '@mrleebo/prisma-ast': 0.12.1 - '@prisma/get-platform': 6.8.2 - '@prisma/query-plan-executor': 6.18.0 + '@electric-sql/pglite': 0.3.15 + '@electric-sql/pglite-socket': 0.0.20(@electric-sql/pglite@0.3.15) + '@electric-sql/pglite-tools': 0.2.20(@electric-sql/pglite@0.3.15) + '@hono/node-server': 1.19.9(hono@4.11.4) + '@mrleebo/prisma-ast': 0.13.1 + '@prisma/get-platform': 7.2.0 + '@prisma/query-plan-executor': 7.2.0 foreground-child: 3.3.1 - get-port-please: 3.1.2 - hono: 4.10.6 + get-port-please: 3.2.0 + hono: 4.11.4 http-status-codes: 2.3.0 pathe: 2.0.3 proper-lockfile: 4.1.2 - remeda: 2.21.3 - std-env: 3.9.0 + remeda: 2.33.4 + std-env: 3.10.0 valibot: 1.2.0(typescript@5.9.3) - zeptomatch: 2.0.2 + zeptomatch: 2.1.0 transitivePeerDependencies: - typescript - '@prisma/driver-adapter-utils@7.2.0': + '@prisma/driver-adapter-utils@7.4.2': dependencies: - '@prisma/debug': 7.2.0 + '@prisma/debug': 7.4.2 - '@prisma/engines-version@7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3': {} + '@prisma/engines-version@7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919': {} - '@prisma/engines@7.2.0': + '@prisma/engines@7.4.2': dependencies: - '@prisma/debug': 7.2.0 - '@prisma/engines-version': 7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3 - '@prisma/fetch-engine': 7.2.0 - '@prisma/get-platform': 7.2.0 + '@prisma/debug': 7.4.2 + '@prisma/engines-version': 7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919 + '@prisma/fetch-engine': 7.4.2 + '@prisma/get-platform': 7.4.2 - '@prisma/fetch-engine@7.2.0': + '@prisma/fetch-engine@7.4.2': dependencies: - '@prisma/debug': 7.2.0 - '@prisma/engines-version': 7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3 - '@prisma/get-platform': 7.2.0 - - '@prisma/get-platform@6.8.2': - dependencies: - '@prisma/debug': 6.8.2 + '@prisma/debug': 7.4.2 + '@prisma/engines-version': 7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919 + '@prisma/get-platform': 7.4.2 '@prisma/get-platform@7.2.0': dependencies: '@prisma/debug': 7.2.0 - '@prisma/query-plan-executor@6.18.0': {} + '@prisma/get-platform@7.4.2': + dependencies: + '@prisma/debug': 7.4.2 - '@prisma/studio-core@0.9.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@prisma/query-plan-executor@7.2.0': {} + + '@prisma/studio-core@0.13.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@types/react': 19.2.7 react: 19.2.3 @@ -4116,6 +4158,8 @@ snapshots: '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} + '@swc/core-darwin-arm64@1.15.3': optional: true @@ -4637,7 +4681,7 @@ snapshots: bcryptjs@3.0.3: {} - better-auth@1.4.10(@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + better-auth@1.4.10(@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@better-auth/core': 1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.10(@better-auth/core@1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0)) @@ -4652,17 +4696,17 @@ snapshots: nanostores: 1.1.0 zod: 4.3.5 optionalDependencies: - '@prisma/client': 5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)) + '@prisma/client': 5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)) better-sqlite3: 12.5.0 - drizzle-orm: 0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3) + drizzle-orm: 0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3) mysql2: 3.15.3 next: 16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) pg: 8.16.3 - prisma: 7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + prisma: 7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - better-auth@1.4.10(@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@5.22.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + better-auth@1.4.10(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(better-sqlite3@12.5.0)(drizzle-orm@0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@5.22.0(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3))(mysql2@3.15.3)(next@16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(pg@8.16.3)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@better-auth/core': 1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.10(@better-auth/core@1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.3.5))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0)) @@ -4677,13 +4721,13 @@ snapshots: nanostores: 1.1.0 zod: 4.3.5 optionalDependencies: - '@prisma/client': 7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) + '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) better-sqlite3: 12.5.0 - drizzle-orm: 0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3) + drizzle-orm: 0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3) mysql2: 3.15.3 next: 16.1.1(@babel/core@7.28.5)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) pg: 8.16.3 - prisma: 7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + prisma: 7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -4744,7 +4788,7 @@ snapshots: c12@3.1.0: dependencies: chokidar: 4.0.3 - confbox: 0.2.2 + confbox: 0.2.4 defu: 6.1.4 dotenv: 16.6.1 exsolve: 1.0.8 @@ -4838,6 +4882,8 @@ snapshots: confbox@0.2.2: {} + confbox@0.2.4: {} + consola@3.4.2: {} convert-source-map@2.0.0: {} @@ -4935,10 +4981,10 @@ snapshots: dotenv@17.2.3: {} - drizzle-orm@0.33.0(@electric-sql/pglite@0.3.2)(@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3): + drizzle-orm@0.33.0(@electric-sql/pglite@0.3.15)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(@types/react@19.2.7)(better-sqlite3@12.5.0)(kysely@0.28.8)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(react@19.2.3): optionalDependencies: - '@electric-sql/pglite': 0.3.2 - '@prisma/client': 7.2.0(prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) + '@electric-sql/pglite': 0.3.15 + '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) '@types/pg': 8.15.6 '@types/react': 19.2.7 better-sqlite3: 12.5.0 @@ -4946,7 +4992,7 @@ snapshots: mysql2: 3.15.3 pg: 8.16.3 postgres: 3.4.7 - prisma: 7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + prisma: 7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) react: 19.2.3 dunder-proto@1.0.1: @@ -4957,7 +5003,7 @@ snapshots: effect@3.18.4: dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 electron-to-chromium@1.5.265: {} @@ -5380,7 +5426,7 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-port-please@3.1.2: {} + get-port-please@3.2.0: {} get-proto@1.0.1: dependencies: @@ -5433,6 +5479,8 @@ snapshots: graphemer@1.4.0: {} + graphmatch@1.1.1: {} + h3@1.15.4: dependencies: cookie-es: 1.2.2 @@ -5473,11 +5521,11 @@ snapshots: dependencies: hermes-estree: 0.25.1 - hono@4.10.6: {} + hono@4.11.4: {} http-status-codes@2.3.0: {} - iconv-lite@0.7.0: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -5771,7 +5819,7 @@ snapshots: dependencies: yallist: 3.1.1 - lru.min@1.1.3: {} + lru.min@1.1.4: {} lucide-react@0.562.0(react@19.2.3): dependencies: @@ -5811,16 +5859,16 @@ snapshots: aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 - iconv-lite: 0.7.0 + iconv-lite: 0.7.2 long: 5.3.2 - lru.min: 1.1.3 - named-placeholders: 1.1.4 + lru.min: 1.1.4 + named-placeholders: 1.1.6 seq-queue: 0.0.5 sqlstring: 2.3.3 - named-placeholders@1.1.4: + named-placeholders@1.1.6: dependencies: - lru.min: 1.1.3 + lru.min: 1.1.4 nanoid@3.3.11: {} @@ -6095,12 +6143,12 @@ snapshots: prettier@3.7.4: {} - prisma@7.2.0(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3): + prisma@7.4.2(@types/react@19.2.7)(better-sqlite3@12.5.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3): dependencies: - '@prisma/config': 7.2.0 - '@prisma/dev': 0.17.0(typescript@5.9.3) - '@prisma/engines': 7.2.0 - '@prisma/studio-core': 0.9.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@prisma/config': 7.4.2 + '@prisma/dev': 0.20.0(typescript@5.9.3) + '@prisma/engines': 7.4.2 + '@prisma/studio-core': 0.13.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) mysql2: 3.15.3 postgres: 3.4.7 optionalDependencies: @@ -6193,9 +6241,7 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - remeda@2.21.3: - dependencies: - type-fest: 4.41.0 + remeda@2.33.4: {} resolve-from@4.0.0: {} @@ -6373,7 +6419,7 @@ snapshots: stable-hash@0.0.5: {} - std-env@3.9.0: {} + std-env@3.10.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -6510,8 +6556,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@4.41.0: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -6694,9 +6738,10 @@ snapshots: yoctocolors@2.1.2: {} - zeptomatch@2.0.2: + zeptomatch@2.1.0: dependencies: grammex: 3.1.12 + graphmatch: 1.1.1 zod-validation-error@4.0.2(zod@4.3.5): dependencies: diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 953bc3e..5eb367b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -20,6 +20,7 @@ model User { accounts Account[] dictionaryLookUps DictionaryLookUp[] folders Folder[] + folderFavorites FolderFavorite[] sessions Session[] translationHistories TranslationHistory[] @@ -91,19 +92,42 @@ model Pair { @@map("pairs") } +enum Visibility { + PRIVATE + PUBLIC +} + model Folder { - id Int @id @default(autoincrement()) - name String - userId String @map("user_id") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - pairs Pair[] + id Int @id @default(autoincrement()) + name String + userId String @map("user_id") + visibility Visibility @default(PRIVATE) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + pairs Pair[] + favorites FolderFavorite[] @@index([userId]) + @@index([visibility]) @@map("folders") } +model FolderFavorite { + id Int @id @default(autoincrement()) + userId String @map("user_id") + folderId Int @map("folder_id") + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade) + + @@unique([userId, folderId]) + @@index([userId]) + @@index([folderId]) + @@map("folder_favorites") +} + model DictionaryLookUp { id Int @id @default(autoincrement()) userId String? @map("user_id") diff --git a/src/app/folders/FoldersClient.tsx b/src/app/folders/FoldersClient.tsx index 3df6c74..c663d8e 100644 --- a/src/app/folders/FoldersClient.tsx +++ b/src/app/folders/FoldersClient.tsx @@ -5,6 +5,10 @@ import { Folder as Fd, FolderPen, FolderPlus, + Globe, + Heart, + Lock, + Search, Trash2, } from "lucide-react"; import { CircleButton, LightButton } from "@/design-system/base/button"; @@ -15,18 +19,41 @@ import { toast } from "sonner"; import { PageLayout } from "@/components/ui/PageLayout"; import { PageHeader } from "@/components/ui/PageHeader"; import { CardList } from "@/components/ui/CardList"; -import { actionCreateFolder, actionDeleteFolderById, actionGetFoldersWithTotalPairsByUserId, actionRenameFolderById } from "@/modules/folder/folder-aciton"; -import { TSharedFolderWithTotalPairs } from "@/shared/folder-type"; +import { + actionCreateFolder, + actionDeleteFolderById, + actionGetFoldersWithTotalPairsByUserId, + actionRenameFolderById, + actionSearchPublicFolders, + actionSetFolderVisibility, + actionToggleFavorite, + actionCheckFavorite, +} from "@/modules/folder/folder-aciton"; +import { TPublicFolder, TSharedFolderWithTotalPairs } from "@/shared/folder-type"; + +type TabType = "my" | "public"; interface FolderProps { folder: TSharedFolderWithTotalPairs; refresh: () => void; + showVisibility?: boolean; } -const FolderCard = ({ folder, refresh }: FolderProps) => { +const FolderCard = ({ folder, refresh, showVisibility = true }: FolderProps) => { const router = useRouter(); const t = useTranslations("folders"); + const handleToggleVisibility = async (e: React.MouseEvent) => { + e.stopPropagation(); + const newVisibility = folder.visibility === "PUBLIC" ? "PRIVATE" : "PUBLIC"; + const result = await actionSetFolderVisibility(folder.id, newVisibility); + if (result.success) { + refresh(); + } else { + toast.error(result.message); + } + }; + return (
{
-

{folder.name}

+
+

{folder.name}

+ {showVisibility && ( + + {folder.visibility === "PUBLIC" ? ( + + ) : ( + + )} + {folder.visibility === "PUBLIC" ? t("public") : t("private")} + + )} +

{t("folderInfo", { id: folder.id, @@ -52,10 +91,22 @@ const FolderCard = ({ folder, refresh }: FolderProps) => {

+ {showVisibility && ( + + {folder.visibility === "PUBLIC" ? ( + + ) : ( + + )} + + )} { e.stopPropagation(); - const newName = prompt("Input a new name.")?.trim(); + const newName = prompt(t("enterNewName"))?.trim(); if (newName && newName.length > 0) { actionRenameFolderById(folder.id, newName) .then(result => { @@ -97,91 +148,263 @@ const FolderCard = ({ folder, refresh }: FolderProps) => { ); }; -export function FoldersClient({ userId }: { userId: string; }) { +interface PublicFolderCardProps { + folder: TPublicFolder; + currentUserId?: string; + onFavoriteChange?: () => void; +} + +const PublicFolderCard = ({ folder, currentUserId, onFavoriteChange }: PublicFolderCardProps) => { + const router = useRouter(); const t = useTranslations("folders"); - const [folders, setFolders] = useState( - [], - ); - const [loading, setLoading] = useState(false); + const [isFavorited, setIsFavorited] = useState(false); + const [favoriteCount, setFavoriteCount] = useState(folder.favoriteCount); useEffect(() => { - setLoading(true); - actionGetFoldersWithTotalPairsByUserId(userId) - .then((folders) => { - if (folders.success && folders.data) { - setFolders(folders.data); - setLoading(false); + if (currentUserId) { + actionCheckFavorite(folder.id).then(result => { + if (result.success && result.data) { + setIsFavorited(result.data.isFavorited); + setFavoriteCount(result.data.favoriteCount); } }); + } + }, [folder.id, currentUserId]); + + const handleToggleFavorite = async (e: React.MouseEvent) => { + e.stopPropagation(); + if (!currentUserId) { + toast.error(t("pleaseLogin")); + return; + } + const result = await actionToggleFavorite(folder.id); + if (result.success && result.data) { + setIsFavorited(result.data.isFavorited); + setFavoriteCount(result.data.favoriteCount); + onFavoriteChange?.(); + } else { + toast.error(result.message); + } + }; + + return ( +
{ + router.push(`/folders/${folder.id}`); + }} + > +
+
+ +
+ +
+

{folder.name}

+

+ {t("publicFolderInfo", { + userName: folder.userName ?? folder.userUsername ?? t("unknownUser"), + totalPairs: folder.totalPairs, + })} +

+
+
+ +
+
+ + {favoriteCount} +
+ + + + +
+
+ ); +}; + +interface FoldersClientProps { + userId: string | null; + initialPublicFolders: TPublicFolder[]; +} + +export function FoldersClient({ userId, initialPublicFolders }: FoldersClientProps) { + const t = useTranslations("folders"); + const [folders, setFolders] = useState([]); + const [publicFolders, setPublicFolders] = useState(initialPublicFolders); + const [loading, setLoading] = useState(false); + const [activeTab, setActiveTab] = useState(userId ? "my" : "public"); + const [searchQuery, setSearchQuery] = useState(""); + + useEffect(() => { + if (userId) { + setLoading(true); + actionGetFoldersWithTotalPairsByUserId(userId) + .then((result) => { + if (result.success && result.data) { + setFolders(result.data); + } + }) + .finally(() => { + setLoading(false); + }); + } }, [userId]); const updateFolders = async () => { + if (!userId) return; setLoading(true); - await actionGetFoldersWithTotalPairsByUserId(userId) - .then(async result => { - if (!result.success) toast.error(result.message); - else await actionGetFoldersWithTotalPairsByUserId(userId) - .then((folders) => { - if (folders.success && folders.data) { - setFolders(folders.data); - } - }); - }); + const result = await actionGetFoldersWithTotalPairsByUserId(userId); + if (result.success && result.data) { + setFolders(result.data); + } setLoading(false); }; + const handleSearch = async () => { + if (!searchQuery.trim()) { + setPublicFolders(initialPublicFolders); + return; + } + setLoading(true); + const result = await actionSearchPublicFolders(searchQuery.trim()); + if (result.success && result.data) { + setPublicFolders(result.data); + } + setLoading(false); + }; + + const handleCreateFolder = async () => { + if (!userId) return; + const folderName = prompt(t("enterFolderName")); + if (!folderName) return; + setLoading(true); + try { + const result = await actionCreateFolder(userId, folderName); + if (result.success) { + updateFolders(); + } else { + toast.error(result.message); + } + } finally { + setLoading(false); + } + }; + return ( - {/* 新建文件夹按钮 */} - { - const folderName = prompt(t("enterFolderName")); - if (!folderName) return; - setLoading(true); - try { - await actionCreateFolder(userId, folderName) - .then(result => { - if (result.success) { - updateFolders(); - } else { - toast.error(result.message); - } - }); - } finally { - setLoading(false); - } - }} - disabled={loading} - className="w-full border-dashed" - > - - {loading ? t("creating") : t("newFolder")} - +
+ {userId && ( + + )} + +
+ + {activeTab === "public" && ( +
+
+ + setSearchQuery(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleSearch()} + placeholder={t("searchPlaceholder")} + className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500" + /> +
+ + + +
+ )} + + {activeTab === "my" && userId && ( + + + {loading ? t("creating") : t("newFolder")} + + )} - {/* 文件夹列表 */}
- {folders.length === 0 ? ( - // 空状态 -
-
- -
-

{t("noFoldersYet")}

+ {loading ? ( +
+
+

{t("loading")}

+ ) : activeTab === "my" && userId ? ( + folders.length === 0 ? ( +
+
+ +
+

{t("noFoldersYet")}

+
+ ) : ( + folders + .toSorted((a, b) => a.id - b.id) + .map((folder) => ( + + )) + ) ) : ( - // 文件夹卡片列表 - folders - .toSorted((a, b) => a.id - b.id) - .map((folder) => ( - +
+ +
+

{t("noPublicFolders")}

+
+ ) : ( + publicFolders.map((folder) => ( + )) + ) )}
diff --git a/src/app/folders/[folder_id]/page.tsx b/src/app/folders/[folder_id]/page.tsx index 271c4d9..80ad60f 100644 --- a/src/app/folders/[folder_id]/page.tsx +++ b/src/app/folders/[folder_id]/page.tsx @@ -3,7 +3,7 @@ import { getTranslations } from "next-intl/server"; import { InFolder } from "./InFolder"; import { auth } from "@/auth"; import { headers } from "next/headers"; -import { actionGetUserIdByFolderId } from "@/modules/folder/folder-aciton"; +import { actionGetFolderVisibility } from "@/modules/folder/folder-aciton"; export default async function FoldersPage({ params, @@ -18,9 +18,19 @@ export default async function FoldersPage({ redirect("/folders"); } - // Allow non-authenticated users to view folders (read-only mode) - const folderUserId = (await actionGetUserIdByFolderId(Number(folder_id))).data; - const isOwner = session?.user?.id === folderUserId; + const folderInfo = (await actionGetFolderVisibility(Number(folder_id))).data; + + if (!folderInfo) { + redirect("/folders"); + } + + const isOwner = session?.user?.id === folderInfo.userId; + const isPublic = folderInfo.visibility === "PUBLIC"; + + if (!isOwner && !isPublic) { + redirect("/folders"); + } + const isReadOnly = !isOwner; return ; diff --git a/src/app/folders/page.tsx b/src/app/folders/page.tsx index ebe684a..7ab85e6 100644 --- a/src/app/folders/page.tsx +++ b/src/app/folders/page.tsx @@ -1,12 +1,29 @@ import { auth } from "@/auth"; import { FoldersClient } from "./FoldersClient"; -import { redirect } from "next/navigation"; import { headers } from "next/headers"; +import { actionGetPublicFolders } from "@/modules/folder/folder-aciton"; export default async function FoldersPage() { const session = await auth.api.getSession( { headers: await headers() } ); - if (!session) redirect(`/login?redirect=/folders`); - return ; + + const publicFoldersResult = await actionGetPublicFolders(); + const publicFolders = publicFoldersResult.success ? publicFoldersResult.data ?? [] : []; + + if (!session) { + return ( + + ); + } + + return ( + + ); } diff --git a/src/modules/folder/folder-aciton.ts b/src/modules/folder/folder-aciton.ts index ec633d8..7ac36b6 100644 --- a/src/modules/folder/folder-aciton.ts +++ b/src/modules/folder/folder-aciton.ts @@ -3,15 +3,13 @@ import { auth } from "@/auth"; import { headers } from "next/headers"; import { ValidateError } from "@/lib/errors"; -import { ActionInputCreatePair, ActionInputUpdatePairById, ActionOutputGetFoldersWithTotalPairsByUserId, validateActionInputCreatePair, validateActionInputUpdatePairById } from "./folder-action-dto"; -import { repoCreateFolder, repoCreatePair, repoDeleteFolderById, repoDeletePairById, repoGetFolderIdByPairId, repoGetFoldersByUserId, repoGetFoldersWithTotalPairsByUserId, repoGetPairsByFolderId, repoGetUserIdByFolderId, repoRenameFolderById, repoUpdatePairById } from "./folder-repository"; +import { ActionInputCreatePair, ActionInputUpdatePairById, ActionOutputGetFoldersWithTotalPairsByUserId, ActionOutputGetPublicFolders, ActionOutputSetFolderVisibility, ActionOutputToggleFavorite, ActionOutputCheckFavorite, validateActionInputCreatePair, validateActionInputUpdatePairById } from "./folder-action-dto"; +import { repoCreateFolder, repoCreatePair, repoDeleteFolderById, repoDeletePairById, repoGetFolderIdByPairId, repoGetFolderVisibility, repoGetFoldersByUserId, repoGetFoldersWithTotalPairsByUserId, repoGetPairsByFolderId, repoGetPublicFolders, repoGetUserIdByFolderId, repoRenameFolderById, repoSearchPublicFolders, repoUpdateFolderVisibility, repoUpdatePairById, repoToggleFavorite, repoCheckFavorite } from "./folder-repository"; import { validate } from "@/utils/validate"; import z from "zod"; import { LENGTH_MAX_FOLDER_NAME, LENGTH_MIN_FOLDER_NAME } from "@/shared/constant"; +import { Visibility } from "../../../generated/prisma/enums"; -/** - * Helper function to check if the current user is the owner of a folder - */ async function checkFolderOwnership(folderId: number): Promise { const session = await auth.api.getSession({ headers: await headers() }); if (!session?.user?.id) return false; @@ -20,9 +18,6 @@ async function checkFolderOwnership(folderId: number): Promise { return folderOwnerId === session.user.id; } -/** - * Helper function to check if the current user is the owner of a pair's folder - */ async function checkPairOwnership(pairId: number): Promise { const session = await auth.api.getSession({ headers: await headers() }); if (!session?.user?.id) return false; @@ -52,7 +47,6 @@ export async function actionGetPairsByFolderId(folderId: number) { export async function actionUpdatePairById(id: number, dto: ActionInputUpdatePairById) { try { - // Check ownership const isOwner = await checkPairOwnership(id); if (!isOwner) { return { @@ -92,9 +86,24 @@ export async function actionGetUserIdByFolderId(folderId: number) { } } +export async function actionGetFolderVisibility(folderId: number) { + try { + return { + success: true, + message: 'success', + data: await repoGetFolderVisibility(folderId) + }; + } catch (e) { + console.log(e); + return { + success: false, + message: 'Unknown error occured.' + }; + } +} + export async function actionDeleteFolderById(folderId: number) { try { - // Check ownership const isOwner = await checkFolderOwnership(folderId); if (!isOwner) { return { @@ -119,7 +128,6 @@ export async function actionDeleteFolderById(folderId: number) { export async function actionDeletePairById(id: number) { try { - // Check ownership const isOwner = await checkPairOwnership(id); if (!isOwner) { return { @@ -176,7 +184,6 @@ export async function actionGetFoldersByUserId(userId: string) { export async function actionCreatePair(dto: ActionInputCreatePair) { try { - // Check ownership const isOwner = await checkFolderOwnership(dto.folderId); if (!isOwner) { return { @@ -238,7 +245,6 @@ export async function actionCreateFolder(userId: string, folderName: string) { export async function actionRenameFolderById(id: number, newName: string) { try { - // Check ownership const isOwner = await checkFolderOwnership(id); if (!isOwner) { return { @@ -272,3 +278,150 @@ export async function actionRenameFolderById(id: number, newName: string) { }; } } + +export async function actionSetFolderVisibility( + folderId: number, + visibility: "PRIVATE" | "PUBLIC", +): Promise { + try { + const isOwner = await checkFolderOwnership(folderId); + if (!isOwner) { + return { + success: false, + message: 'You do not have permission to change this folder visibility.', + }; + } + + await repoUpdateFolderVisibility({ + folderId, + visibility: visibility as Visibility, + }); + return { + success: true, + message: 'success', + }; + } catch (e) { + console.log(e); + return { + success: false, + message: 'Unknown error occured.', + }; + } +} + +export async function actionGetPublicFolders(): Promise { + try { + const data = await repoGetPublicFolders({}); + return { + success: true, + message: 'success', + data: data.map((folder) => ({ + ...folder, + visibility: folder.visibility as "PRIVATE" | "PUBLIC", + })), + }; + } catch (e) { + console.log(e); + return { + success: false, + message: 'Unknown error occured.', + }; + } +} + +export async function actionSearchPublicFolders(query: string): Promise { + try { + const data = await repoSearchPublicFolders({ query, limit: 50 }); + return { + success: true, + message: 'success', + data: data.map((folder) => ({ + ...folder, + visibility: folder.visibility as "PRIVATE" | "PUBLIC", + })), + }; + } catch (e) { + console.log(e); + return { + success: false, + message: 'Unknown error occured.', + }; + } +} + +export async function actionToggleFavorite( + folderId: number, +): Promise { + try { + const session = await auth.api.getSession({ headers: await headers() }); + if (!session?.user?.id) { + return { + success: false, + message: 'Unauthorized', + }; + } + + const isFavorited = await repoToggleFavorite({ + folderId, + userId: session.user.id, + }); + + const { favoriteCount } = await repoCheckFavorite({ + folderId, + userId: session.user.id, + }); + + return { + success: true, + message: 'success', + data: { + isFavorited, + favoriteCount, + }, + }; + } catch (e) { + console.log(e); + return { + success: false, + message: 'Unknown error occured.', + }; + } +} + +export async function actionCheckFavorite( + folderId: number, +): Promise { + try { + const session = await auth.api.getSession({ headers: await headers() }); + if (!session?.user?.id) { + return { + success: true, + message: 'success', + data: { + isFavorited: false, + favoriteCount: 0, + }, + }; + } + + const { isFavorited, favoriteCount } = await repoCheckFavorite({ + folderId, + userId: session.user.id, + }); + + return { + success: true, + message: 'success', + data: { + isFavorited, + favoriteCount, + }, + }; + } catch (e) { + console.log(e); + return { + success: false, + message: 'Unknown error occured.', + }; + } +} diff --git a/src/modules/folder/folder-action-dto.ts b/src/modules/folder/folder-action-dto.ts index 6566e02..54b8b2d 100644 --- a/src/modules/folder/folder-action-dto.ts +++ b/src/modules/folder/folder-action-dto.ts @@ -32,3 +32,55 @@ export type ActionOutputGetFoldersWithTotalPairsByUserId = { success: boolean, data?: TSharedFolderWithTotalPairs[]; }; + +export const schemaActionInputSetFolderVisibility = z.object({ + folderId: z.number().int().positive(), + visibility: z.enum(["PRIVATE", "PUBLIC"]), +}); +export type ActionInputSetFolderVisibility = z.infer; + +export const schemaActionInputSearchPublicFolders = z.object({ + query: z.string().min(1).max(100), +}); +export type ActionInputSearchPublicFolders = z.infer; + +export type ActionOutputPublicFolder = { + id: number; + name: string; + visibility: "PRIVATE" | "PUBLIC"; + createdAt: Date; + userId: string; + userName: string | null; + userUsername: string | null; + totalPairs: number; + favoriteCount: number; +}; + +export type ActionOutputGetPublicFolders = { + message: string; + success: boolean; + data?: ActionOutputPublicFolder[]; +}; + +export type ActionOutputSetFolderVisibility = { + message: string; + success: boolean; +}; + +export type ActionOutputToggleFavorite = { + message: string; + success: boolean; + data?: { + isFavorited: boolean; + favoriteCount: number; + }; +}; + +export type ActionOutputCheckFavorite = { + message: string; + success: boolean; + data?: { + isFavorited: boolean; + favoriteCount: number; + }; +}; diff --git a/src/modules/folder/folder-repository-dto.ts b/src/modules/folder/folder-repository-dto.ts index 844ef14..a8f628d 100644 --- a/src/modules/folder/folder-repository-dto.ts +++ b/src/modules/folder/folder-repository-dto.ts @@ -1,3 +1,5 @@ +import { Visibility } from "../../../generated/prisma/enums"; + export interface RepoInputCreateFolder { name: string; userId: string; @@ -21,3 +23,51 @@ export interface RepoInputUpdatePair { ipa1?: string; ipa2?: string; } + +export interface RepoInputUpdateFolderVisibility { + folderId: number; + visibility: Visibility; +} + +export interface RepoInputSearchPublicFolders { + query: string; + limit?: number; +} + +export interface RepoInputGetPublicFolders { + limit?: number; + offset?: number; + orderBy?: "createdAt" | "name"; +} + +export type RepoOutputPublicFolder = { + id: number; + name: string; + visibility: Visibility; + createdAt: Date; + userId: string; + userName: string | null; + userUsername: string | null; + totalPairs: number; + favoriteCount: number; +}; + +export type RepoOutputFolderVisibility = { + visibility: Visibility; + userId: string; +}; + +export interface RepoInputToggleFavorite { + folderId: number; + userId: string; +} + +export interface RepoInputCheckFavorite { + folderId: number; + userId: string; +} + +export type RepoOutputFavoriteStatus = { + isFavorited: boolean; + favoriteCount: number; +}; diff --git a/src/modules/folder/folder-repository.ts b/src/modules/folder/folder-repository.ts index 6d8b857..477a0c1 100644 --- a/src/modules/folder/folder-repository.ts +++ b/src/modules/folder/folder-repository.ts @@ -1,5 +1,18 @@ import { prisma } from "@/lib/db"; -import { RepoInputCreateFolder, RepoInputCreatePair, RepoInputUpdatePair } from "./folder-repository-dto"; +import { + RepoInputCreateFolder, + RepoInputCreatePair, + RepoInputUpdatePair, + RepoInputUpdateFolderVisibility, + RepoInputSearchPublicFolders, + RepoInputGetPublicFolders, + RepoOutputPublicFolder, + RepoOutputFolderVisibility, + RepoInputToggleFavorite, + RepoInputCheckFavorite, + RepoOutputFavoriteStatus, +} from "./folder-repository-dto"; +import { Visibility } from "../../../generated/prisma/enums"; export async function repoCreatePair(data: RepoInputCreatePair) { return (await prisma.pair.create({ @@ -63,7 +76,8 @@ export async function repoGetFoldersByUserId(userId: string) { return { id: v.id, name: v.name, - userId: v.userId + userId: v.userId, + visibility: v.visibility, }; }); } @@ -95,6 +109,7 @@ export async function repoGetFoldersWithTotalPairsByUserId(userId: string) { id: folder.id, name: folder.name, userId: folder.userId, + visibility: folder.visibility, total: folder._count?.pairs ?? 0, createdAt: folder.createdAt, })); @@ -134,3 +149,126 @@ export async function repoGetFolderIdByPairId(pairId: number) { }); return pair?.folderId; } + +export async function repoUpdateFolderVisibility( + input: RepoInputUpdateFolderVisibility, +): Promise { + await prisma.folder.update({ + where: { id: input.folderId }, + data: { visibility: input.visibility }, + }); +} + +export async function repoGetFolderVisibility( + folderId: number, +): Promise { + const folder = await prisma.folder.findUnique({ + where: { id: folderId }, + select: { visibility: true, userId: true }, + }); + return folder; +} + +export async function repoGetPublicFolders( + input: RepoInputGetPublicFolders = {}, +): Promise { + const { limit = 50, offset = 0, orderBy = "createdAt" } = input; + + const folders = await prisma.folder.findMany({ + where: { visibility: Visibility.PUBLIC }, + include: { + _count: { select: { pairs: true, favorites: true } }, + user: { select: { name: true, username: true } }, + }, + orderBy: { [orderBy]: "desc" }, + take: limit, + skip: offset, + }); + return folders.map((folder) => ({ + id: folder.id, + name: folder.name, + visibility: folder.visibility, + createdAt: folder.createdAt, + userId: folder.userId, + userName: folder.user.name, + userUsername: folder.user.username, + totalPairs: folder._count.pairs, + favoriteCount: folder._count.favorites, + })); +} + +export async function repoSearchPublicFolders( + input: RepoInputSearchPublicFolders, +): Promise { + const { query, limit = 50 } = input; + const folders = await prisma.folder.findMany({ + where: { + visibility: Visibility.PUBLIC, + name: { contains: query, mode: "insensitive" }, + }, + include: { + _count: { select: { pairs: true, favorites: true } }, + user: { select: { name: true, username: true } }, + }, + orderBy: { createdAt: "desc" }, + take: limit, + }); + return folders.map((folder) => ({ + id: folder.id, + name: folder.name, + visibility: folder.visibility, + createdAt: folder.createdAt, + userId: folder.userId, + userName: folder.user.name, + userUsername: folder.user.username, + totalPairs: folder._count.pairs, + favoriteCount: folder._count.favorites, + })); +} + +export async function repoToggleFavorite( + input: RepoInputToggleFavorite, +): Promise { + const existing = await prisma.folderFavorite.findUnique({ + where: { + userId_folderId: { + userId: input.userId, + folderId: input.folderId, + }, + }, + }); + if (existing) { + await prisma.folderFavorite.delete({ + where: { id: existing.id }, + }); + return false; + } else { + await prisma.folderFavorite.create({ + data: { + userId: input.userId, + folderId: input.folderId, + }, + }); + return true; + } +} + +export async function repoCheckFavorite( + input: RepoInputCheckFavorite, +): Promise { + const favorite = await prisma.folderFavorite.findUnique({ + where: { + userId_folderId: { + userId: input.userId, + folderId: input.folderId, + }, + }, + }); + const count = await prisma.folderFavorite.count({ + where: { folderId: input.folderId }, + }); + return { + isFavorited: !!favorite, + favoriteCount: count, + }; +} diff --git a/src/shared/folder-type.ts b/src/shared/folder-type.ts index cd2b6aa..afa15f4 100644 --- a/src/shared/folder-type.ts +++ b/src/shared/folder-type.ts @@ -2,12 +2,14 @@ export type TSharedFolder = { id: number, name: string, userId: string; + visibility: "PRIVATE" | "PUBLIC"; }; export type TSharedFolderWithTotalPairs = { id: number, name: string, userId: string, + visibility: "PRIVATE" | "PUBLIC"; total: number; }; @@ -20,4 +22,16 @@ export type TSharedPair = { ipa2: string | null; id: number; folderId: number; +}; + +export type TPublicFolder = { + id: number; + name: string; + visibility: "PRIVATE" | "PUBLIC"; + createdAt: Date; + userId: string; + userName: string | null; + userUsername: string | null; + totalPairs: number; + favoriteCount: number; }; \ No newline at end of file