diff --git a/modules/frontend/package-lock.json b/modules/frontend/package-lock.json
index f5dde46..6a06afb 100644
--- a/modules/frontend/package-lock.json
+++ b/modules/frontend/package-lock.json
@@ -8,13 +8,10 @@
"name": "nyanimedb-frontend",
"version": "0.0.0",
"dependencies": {
- "@heroicons/react": "^2.2.0",
- "@tailwindcss/vite": "^4.1.17",
"axios": "^1.12.2",
"react": "^19.1.1",
"react-dom": "^19.1.1",
- "react-router-dom": "^7.9.4",
- "tailwindcss": "^4.1.17"
+ "react-router-dom": "^7.9.4"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
@@ -340,6 +337,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -356,6 +354,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -372,6 +371,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -388,6 +388,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -404,6 +405,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -420,6 +422,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -436,6 +439,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -452,6 +456,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -468,6 +473,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -484,6 +490,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -500,6 +507,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -516,6 +524,7 @@
"cpu": [
"loong64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -532,6 +541,7 @@
"cpu": [
"mips64el"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -548,6 +558,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -564,6 +575,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -580,6 +592,7 @@
"cpu": [
"s390x"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -596,6 +609,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -612,6 +626,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -628,6 +643,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -644,6 +660,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -660,6 +677,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -676,6 +694,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -692,6 +711,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -708,6 +728,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -724,6 +745,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -740,6 +762,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -906,15 +929,6 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@heroicons/react": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
- "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
- "license": "MIT",
- "peerDependencies": {
- "react": ">= 16 || ^19.0.0-rc"
- }
- },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -971,6 +985,7 @@
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -981,6 +996,7 @@
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -991,6 +1007,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -1000,12 +1017,14 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -1071,6 +1090,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1084,6 +1104,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1097,6 +1118,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1110,6 +1132,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1123,6 +1146,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1136,6 +1160,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1149,6 +1174,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1162,6 +1188,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1175,6 +1202,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1188,6 +1216,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1201,6 +1230,7 @@
"cpu": [
"loong64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1214,6 +1244,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1227,6 +1258,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1240,6 +1272,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1253,6 +1286,7 @@
"cpu": [
"s390x"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1266,6 +1300,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1279,6 +1314,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1292,6 +1328,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1305,6 +1342,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1318,6 +1356,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1331,6 +1370,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1344,269 +1384,13 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
- "node_modules/@tailwindcss/node": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
- "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/remapping": "^2.3.4",
- "enhanced-resolve": "^5.18.3",
- "jiti": "^2.6.1",
- "lightningcss": "1.30.2",
- "magic-string": "^0.30.21",
- "source-map-js": "^1.2.1",
- "tailwindcss": "4.1.17"
- }
- },
- "node_modules/@tailwindcss/oxide": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz",
- "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==",
- "license": "MIT",
- "engines": {
- "node": ">= 10"
- },
- "optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.1.17",
- "@tailwindcss/oxide-darwin-arm64": "4.1.17",
- "@tailwindcss/oxide-darwin-x64": "4.1.17",
- "@tailwindcss/oxide-freebsd-x64": "4.1.17",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17",
- "@tailwindcss/oxide-linux-arm64-musl": "4.1.17",
- "@tailwindcss/oxide-linux-x64-gnu": "4.1.17",
- "@tailwindcss/oxide-linux-x64-musl": "4.1.17",
- "@tailwindcss/oxide-wasm32-wasi": "4.1.17",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17",
- "@tailwindcss/oxide-win32-x64-msvc": "4.1.17"
- }
- },
- "node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz",
- "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz",
- "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz",
- "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz",
- "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz",
- "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz",
- "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz",
- "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz",
- "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz",
- "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz",
- "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==",
- "bundleDependencies": [
- "@napi-rs/wasm-runtime",
- "@emnapi/core",
- "@emnapi/runtime",
- "@tybys/wasm-util",
- "@emnapi/wasi-threads",
- "tslib"
- ],
- "cpu": [
- "wasm32"
- ],
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/core": "^1.6.0",
- "@emnapi/runtime": "^1.6.0",
- "@emnapi/wasi-threads": "^1.1.0",
- "@napi-rs/wasm-runtime": "^1.0.7",
- "@tybys/wasm-util": "^0.10.1",
- "tslib": "^2.4.0"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz",
- "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz",
- "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/vite": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz",
- "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==",
- "license": "MIT",
- "dependencies": {
- "@tailwindcss/node": "4.1.17",
- "@tailwindcss/oxide": "4.1.17",
- "tailwindcss": "4.1.17"
- },
- "peerDependencies": {
- "vite": "^5.2.0 || ^6 || ^7"
- }
- },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -1656,6 +1440,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
@@ -1669,7 +1454,7 @@
"version": "24.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz",
"integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -2342,15 +2127,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/detect-libc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2372,19 +2148,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/enhanced-resolve": {
- "version": "5.18.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
- "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -2434,6 +2197,7 @@
"version": "0.25.10",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz",
"integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
+ "dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -2853,6 +2617,7 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -2961,6 +2726,7 @@
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
"license": "ISC"
},
"node_modules/graphemer": {
@@ -3118,15 +2884,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/jiti": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
- "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
- "license": "MIT",
- "bin": {
- "jiti": "lib/jiti-cli.mjs"
- }
- },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3231,255 +2988,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/lightningcss": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
- "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
- "license": "MPL-2.0",
- "dependencies": {
- "detect-libc": "^2.0.3"
- },
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- },
- "optionalDependencies": {
- "lightningcss-android-arm64": "1.30.2",
- "lightningcss-darwin-arm64": "1.30.2",
- "lightningcss-darwin-x64": "1.30.2",
- "lightningcss-freebsd-x64": "1.30.2",
- "lightningcss-linux-arm-gnueabihf": "1.30.2",
- "lightningcss-linux-arm64-gnu": "1.30.2",
- "lightningcss-linux-arm64-musl": "1.30.2",
- "lightningcss-linux-x64-gnu": "1.30.2",
- "lightningcss-linux-x64-musl": "1.30.2",
- "lightningcss-win32-arm64-msvc": "1.30.2",
- "lightningcss-win32-x64-msvc": "1.30.2"
- }
- },
- "node_modules/lightningcss-android-arm64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
- "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
- "cpu": [
- "arm64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-arm64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
- "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
- "cpu": [
- "arm64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-x64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
- "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
- "cpu": [
- "x64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-freebsd-x64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
- "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
- "cpu": [
- "x64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
- "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
- "cpu": [
- "arm"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
- "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
- "cpu": [
- "arm64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
- "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
- "cpu": [
- "arm64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
- "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
- "cpu": [
- "x64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-musl": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
- "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
- "cpu": [
- "x64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
- "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
- "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
- "cpu": [
- "x64"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -3513,15 +3021,6 @@
"yallist": "^3.0.2"
}
},
- "node_modules/magic-string": {
- "version": "0.30.21",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
- }
- },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -3610,6 +3109,7 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -3749,6 +3249,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -3768,6 +3269,7 @@
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
"funding": [
{
"type": "opencollective",
@@ -3935,6 +3437,7 @@
"version": "4.52.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
"integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
@@ -4055,6 +3558,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -4086,29 +3590,11 @@
"node": ">=8"
}
},
- "node_modules/tailwindcss": {
- "version": "4.1.17",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
- "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
- "license": "MIT"
- },
- "node_modules/tapable": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
- "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -4125,6 +3611,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -4142,6 +3629,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
"license": "MIT",
"peer": true,
"engines": {
@@ -4247,7 +3735,7 @@
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/universalify": {
@@ -4305,6 +3793,7 @@
"version": "7.1.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz",
"integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==",
+ "dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -4380,6 +3869,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -4397,6 +3887,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
"license": "MIT",
"peer": true,
"engines": {
diff --git a/modules/frontend/package.json b/modules/frontend/package.json
index cc468cf..f15ffd5 100644
--- a/modules/frontend/package.json
+++ b/modules/frontend/package.json
@@ -10,13 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
- "@heroicons/react": "^2.2.0",
- "@tailwindcss/vite": "^4.1.17",
"axios": "^1.12.2",
"react": "^19.1.1",
"react-dom": "^19.1.1",
- "react-router-dom": "^7.9.4",
- "tailwindcss": "^4.1.17"
+ "react-router-dom": "^7.9.4"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
diff --git a/modules/frontend/src/api/index.ts b/modules/frontend/src/api/index.ts
index 80ae491..f0d09ee 100644
--- a/modules/frontend/src/api/index.ts
+++ b/modules/frontend/src/api/index.ts
@@ -8,19 +8,16 @@ export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';
export type { cursor } from './models/cursor';
-export type { CursorObj } from './models/CursorObj';
export type { Image } from './models/Image';
-export type { ReleaseSeason } from './models/ReleaseSeason';
+export { ReleaseSeason } from './models/ReleaseSeason';
export type { Review } from './models/Review';
export type { Studio } from './models/Studio';
export type { Tag } from './models/Tag';
export type { Tags } from './models/Tags';
export type { Title } from './models/Title';
-export type { title_sort } from './models/title_sort';
-export type { TitleSort } from './models/TitleSort';
-export type { TitleStatus } from './models/TitleStatus';
+export { TitleStatus } from './models/TitleStatus';
export type { User } from './models/User';
export type { UserTitle } from './models/UserTitle';
-export type { UserTitleStatus } from './models/UserTitleStatus';
+export { UserTitleStatus } from './models/UserTitleStatus';
export { DefaultService } from './services/DefaultService';
diff --git a/modules/frontend/src/api/models/ReleaseSeason.ts b/modules/frontend/src/api/models/ReleaseSeason.ts
index ad9f930..182b980 100644
--- a/modules/frontend/src/api/models/ReleaseSeason.ts
+++ b/modules/frontend/src/api/models/ReleaseSeason.ts
@@ -5,4 +5,9 @@
/**
* Title release season
*/
-export type ReleaseSeason = 'winter' | 'spring' | 'summer' | 'fall';
+export enum ReleaseSeason {
+ WINTER = 'winter',
+ SPRING = 'spring',
+ SUMMER = 'summer',
+ FALL = 'fall',
+}
diff --git a/modules/frontend/src/api/models/TitleStatus.ts b/modules/frontend/src/api/models/TitleStatus.ts
index 72e0261..811ece8 100644
--- a/modules/frontend/src/api/models/TitleStatus.ts
+++ b/modules/frontend/src/api/models/TitleStatus.ts
@@ -5,4 +5,8 @@
/**
* Title status
*/
-export type TitleStatus = 'finished' | 'ongoing' | 'planned';
+export enum TitleStatus {
+ FINISHED = 'finished',
+ ONGOING = 'ongoing',
+ PLANNED = 'planned',
+}
diff --git a/modules/frontend/src/api/models/User.ts b/modules/frontend/src/api/models/User.ts
index cd76dbe..541028e 100644
--- a/modules/frontend/src/api/models/User.ts
+++ b/modules/frontend/src/api/models/User.ts
@@ -6,11 +6,11 @@ export type User = {
/**
* Unique user ID (primary key)
*/
- id?: number;
+ id: number;
/**
* ID of the user avatar (references images table)
*/
- avatar_id?: number;
+ avatar_id?: number | null;
/**
* User email
*/
diff --git a/modules/frontend/src/api/models/UserTitleStatus.ts b/modules/frontend/src/api/models/UserTitleStatus.ts
index 0a29626..20651fe 100644
--- a/modules/frontend/src/api/models/UserTitleStatus.ts
+++ b/modules/frontend/src/api/models/UserTitleStatus.ts
@@ -5,4 +5,9 @@
/**
* User's title status
*/
-export type UserTitleStatus = 'finished' | 'planned' | 'dropped' | 'in-progress';
+export enum UserTitleStatus {
+ FINISHED = 'finished',
+ PLANNED = 'planned',
+ DROPPED = 'dropped',
+ IN_PROGRESS = 'in-progress',
+}
diff --git a/modules/frontend/src/api/services/DefaultService.ts b/modules/frontend/src/api/services/DefaultService.ts
index 52321b8..b0ae54d 100644
--- a/modules/frontend/src/api/services/DefaultService.ts
+++ b/modules/frontend/src/api/services/DefaultService.ts
@@ -2,23 +2,17 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
-import type { CursorObj } from '../models/CursorObj';
import type { ReleaseSeason } from '../models/ReleaseSeason';
import type { Title } from '../models/Title';
-import type { TitleSort } from '../models/TitleSort';
import type { TitleStatus } from '../models/TitleStatus';
import type { User } from '../models/User';
import type { UserTitle } from '../models/UserTitle';
-import type { UserTitleStatus } from '../models/UserTitleStatus';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
export class DefaultService {
/**
* Get titles
- * @param cursor
- * @param sort
- * @param sortForward
* @param word
* @param status
* @param rating
@@ -27,13 +21,10 @@ export class DefaultService {
* @param limit
* @param offset
* @param fields
- * @returns any List of titles with cursor
+ * @returns Title List of titles
* @throws ApiError
*/
public static getTitles(
- cursor?: string,
- sort?: TitleSort,
- sortForward: boolean = true,
word?: string,
status?: TitleStatus,
rating?: number,
@@ -42,20 +33,11 @@ export class DefaultService {
limit: number = 10,
offset?: number,
fields: string = 'all',
- ): CancelablePromise<{
- /**
- * List of titles
- */
- data: Array
;
- cursor: CursorObj;
- }> {
+ ): CancelablePromise> {
return __request(OpenAPI, {
method: 'GET',
url: '/titles',
query: {
- 'cursor': cursor,
- 'sort': sort,
- 'sort_forward': sortForward,
'word': word,
'status': status,
'rating': rating,
@@ -129,13 +111,9 @@ export class DefaultService {
* Get user titles
* @param userId
* @param cursor
- * @param word
- * @param status
- * @param watchStatus
- * @param rating
- * @param releaseYear
- * @param releaseSeason
+ * @param query
* @param limit
+ * @param offset
* @param fields
* @returns UserTitle List of user titles
* @throws ApiError
@@ -143,30 +121,22 @@ export class DefaultService {
public static getUsersTitles(
userId: string,
cursor?: string,
- word?: string,
- status?: TitleStatus,
- watchStatus?: UserTitleStatus,
- rating?: number,
- releaseYear?: number,
- releaseSeason?: ReleaseSeason,
+ query?: string,
limit: number = 10,
+ offset?: number,
fields: string = 'all',
): CancelablePromise> {
return __request(OpenAPI, {
method: 'GET',
- url: '/users/{user_id}/titles/',
+ url: '/users/{user_id}/titles',
path: {
'user_id': userId,
},
query: {
'cursor': cursor,
- 'word': word,
- 'status': status,
- 'watch_status': watchStatus,
- 'rating': rating,
- 'release_year': releaseYear,
- 'release_season': releaseSeason,
+ 'query': query,
'limit': limit,
+ 'offset': offset,
'fields': fields,
},
errors: {
diff --git a/modules/frontend/src/api_/core/ApiError.ts b/modules/frontend/src/api_/core/ApiError.ts
new file mode 100644
index 0000000..ec7b16a
--- /dev/null
+++ b/modules/frontend/src/api_/core/ApiError.ts
@@ -0,0 +1,25 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+
+export class ApiError extends Error {
+ public readonly url: string;
+ public readonly status: number;
+ public readonly statusText: string;
+ public readonly body: any;
+ public readonly request: ApiRequestOptions;
+
+ constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
+ super(message);
+
+ this.name = 'ApiError';
+ this.url = response.url;
+ this.status = response.status;
+ this.statusText = response.statusText;
+ this.body = response.body;
+ this.request = request;
+ }
+}
diff --git a/modules/frontend/src/api_/core/ApiRequestOptions.ts b/modules/frontend/src/api_/core/ApiRequestOptions.ts
new file mode 100644
index 0000000..93143c3
--- /dev/null
+++ b/modules/frontend/src/api_/core/ApiRequestOptions.ts
@@ -0,0 +1,17 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiRequestOptions = {
+ readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
+ readonly url: string;
+ readonly path?: Record;
+ readonly cookies?: Record;
+ readonly headers?: Record;
+ readonly query?: Record;
+ readonly formData?: Record;
+ readonly body?: any;
+ readonly mediaType?: string;
+ readonly responseHeader?: string;
+ readonly errors?: Record;
+};
diff --git a/modules/frontend/src/api_/core/ApiResult.ts b/modules/frontend/src/api_/core/ApiResult.ts
new file mode 100644
index 0000000..ee1126e
--- /dev/null
+++ b/modules/frontend/src/api_/core/ApiResult.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiResult = {
+ readonly url: string;
+ readonly ok: boolean;
+ readonly status: number;
+ readonly statusText: string;
+ readonly body: any;
+};
diff --git a/modules/frontend/src/api_/core/CancelablePromise.ts b/modules/frontend/src/api_/core/CancelablePromise.ts
new file mode 100644
index 0000000..d70de92
--- /dev/null
+++ b/modules/frontend/src/api_/core/CancelablePromise.ts
@@ -0,0 +1,131 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export class CancelError extends Error {
+
+ constructor(message: string) {
+ super(message);
+ this.name = 'CancelError';
+ }
+
+ public get isCancelled(): boolean {
+ return true;
+ }
+}
+
+export interface OnCancel {
+ readonly isResolved: boolean;
+ readonly isRejected: boolean;
+ readonly isCancelled: boolean;
+
+ (cancelHandler: () => void): void;
+}
+
+export class CancelablePromise implements Promise {
+ #isResolved: boolean;
+ #isRejected: boolean;
+ #isCancelled: boolean;
+ readonly #cancelHandlers: (() => void)[];
+ readonly #promise: Promise;
+ #resolve?: (value: T | PromiseLike) => void;
+ #reject?: (reason?: any) => void;
+
+ constructor(
+ executor: (
+ resolve: (value: T | PromiseLike) => void,
+ reject: (reason?: any) => void,
+ onCancel: OnCancel
+ ) => void
+ ) {
+ this.#isResolved = false;
+ this.#isRejected = false;
+ this.#isCancelled = false;
+ this.#cancelHandlers = [];
+ this.#promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
+
+ const onResolve = (value: T | PromiseLike): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isResolved = true;
+ if (this.#resolve) this.#resolve(value);
+ };
+
+ const onReject = (reason?: any): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isRejected = true;
+ if (this.#reject) this.#reject(reason);
+ };
+
+ const onCancel = (cancelHandler: () => void): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#cancelHandlers.push(cancelHandler);
+ };
+
+ Object.defineProperty(onCancel, 'isResolved', {
+ get: (): boolean => this.#isResolved,
+ });
+
+ Object.defineProperty(onCancel, 'isRejected', {
+ get: (): boolean => this.#isRejected,
+ });
+
+ Object.defineProperty(onCancel, 'isCancelled', {
+ get: (): boolean => this.#isCancelled,
+ });
+
+ return executor(onResolve, onReject, onCancel as OnCancel);
+ });
+ }
+
+ get [Symbol.toStringTag]() {
+ return "Cancellable Promise";
+ }
+
+ public then(
+ onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null,
+ onRejected?: ((reason: any) => TResult2 | PromiseLike) | null
+ ): Promise {
+ return this.#promise.then(onFulfilled, onRejected);
+ }
+
+ public catch(
+ onRejected?: ((reason: any) => TResult | PromiseLike) | null
+ ): Promise {
+ return this.#promise.catch(onRejected);
+ }
+
+ public finally(onFinally?: (() => void) | null): Promise {
+ return this.#promise.finally(onFinally);
+ }
+
+ public cancel(): void {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isCancelled = true;
+ if (this.#cancelHandlers.length) {
+ try {
+ for (const cancelHandler of this.#cancelHandlers) {
+ cancelHandler();
+ }
+ } catch (error) {
+ console.warn('Cancellation threw an error', error);
+ return;
+ }
+ }
+ this.#cancelHandlers.length = 0;
+ if (this.#reject) this.#reject(new CancelError('Request aborted'));
+ }
+
+ public get isCancelled(): boolean {
+ return this.#isCancelled;
+ }
+}
diff --git a/modules/frontend/src/api_/core/OpenAPI.ts b/modules/frontend/src/api_/core/OpenAPI.ts
new file mode 100644
index 0000000..185e5c3
--- /dev/null
+++ b/modules/frontend/src/api_/core/OpenAPI.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+
+type Resolver = (options: ApiRequestOptions) => Promise;
+type Headers = Record;
+
+export type OpenAPIConfig = {
+ BASE: string;
+ VERSION: string;
+ WITH_CREDENTIALS: boolean;
+ CREDENTIALS: 'include' | 'omit' | 'same-origin';
+ TOKEN?: string | Resolver | undefined;
+ USERNAME?: string | Resolver | undefined;
+ PASSWORD?: string | Resolver | undefined;
+ HEADERS?: Headers | Resolver | undefined;
+ ENCODE_PATH?: ((path: string) => string) | undefined;
+};
+
+export const OpenAPI: OpenAPIConfig = {
+ BASE: '/api/v1',
+ VERSION: '1.0.0',
+ WITH_CREDENTIALS: false,
+ CREDENTIALS: 'include',
+ TOKEN: undefined,
+ USERNAME: undefined,
+ PASSWORD: undefined,
+ HEADERS: undefined,
+ ENCODE_PATH: undefined,
+};
diff --git a/modules/frontend/src/api_/core/request.ts b/modules/frontend/src/api_/core/request.ts
new file mode 100644
index 0000000..1dc6fef
--- /dev/null
+++ b/modules/frontend/src/api_/core/request.ts
@@ -0,0 +1,323 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import axios from 'axios';
+import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
+import FormData from 'form-data';
+
+import { ApiError } from './ApiError';
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+import { CancelablePromise } from './CancelablePromise';
+import type { OnCancel } from './CancelablePromise';
+import type { OpenAPIConfig } from './OpenAPI';
+
+export const isDefined = (value: T | null | undefined): value is Exclude => {
+ return value !== undefined && value !== null;
+};
+
+export const isString = (value: any): value is string => {
+ return typeof value === 'string';
+};
+
+export const isStringWithValue = (value: any): value is string => {
+ return isString(value) && value !== '';
+};
+
+export const isBlob = (value: any): value is Blob => {
+ return (
+ typeof value === 'object' &&
+ typeof value.type === 'string' &&
+ typeof value.stream === 'function' &&
+ typeof value.arrayBuffer === 'function' &&
+ typeof value.constructor === 'function' &&
+ typeof value.constructor.name === 'string' &&
+ /^(Blob|File)$/.test(value.constructor.name) &&
+ /^(Blob|File)$/.test(value[Symbol.toStringTag])
+ );
+};
+
+export const isFormData = (value: any): value is FormData => {
+ return value instanceof FormData;
+};
+
+export const isSuccess = (status: number): boolean => {
+ return status >= 200 && status < 300;
+};
+
+export const base64 = (str: string): string => {
+ try {
+ return btoa(str);
+ } catch (err) {
+ // @ts-ignore
+ return Buffer.from(str).toString('base64');
+ }
+};
+
+export const getQueryString = (params: Record): string => {
+ const qs: string[] = [];
+
+ const append = (key: string, value: any) => {
+ qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
+ };
+
+ const process = (key: string, value: any) => {
+ if (isDefined(value)) {
+ if (Array.isArray(value)) {
+ value.forEach(v => {
+ process(key, v);
+ });
+ } else if (typeof value === 'object') {
+ Object.entries(value).forEach(([k, v]) => {
+ process(`${key}[${k}]`, v);
+ });
+ } else {
+ append(key, value);
+ }
+ }
+ };
+
+ Object.entries(params).forEach(([key, value]) => {
+ process(key, value);
+ });
+
+ if (qs.length > 0) {
+ return `?${qs.join('&')}`;
+ }
+
+ return '';
+};
+
+const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
+ const encoder = config.ENCODE_PATH || encodeURI;
+
+ const path = options.url
+ .replace('{api-version}', config.VERSION)
+ .replace(/{(.*?)}/g, (substring: string, group: string) => {
+ if (options.path?.hasOwnProperty(group)) {
+ return encoder(String(options.path[group]));
+ }
+ return substring;
+ });
+
+ const url = `${config.BASE}${path}`;
+ if (options.query) {
+ return `${url}${getQueryString(options.query)}`;
+ }
+ return url;
+};
+
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+ if (options.formData) {
+ const formData = new FormData();
+
+ const process = (key: string, value: any) => {
+ if (isString(value) || isBlob(value)) {
+ formData.append(key, value);
+ } else {
+ formData.append(key, JSON.stringify(value));
+ }
+ };
+
+ Object.entries(options.formData)
+ .filter(([_, value]) => isDefined(value))
+ .forEach(([key, value]) => {
+ if (Array.isArray(value)) {
+ value.forEach(v => process(key, v));
+ } else {
+ process(key, value);
+ }
+ });
+
+ return formData;
+ }
+ return undefined;
+};
+
+type Resolver = (options: ApiRequestOptions) => Promise;
+
+export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => {
+ if (typeof resolver === 'function') {
+ return (resolver as Resolver)(options);
+ }
+ return resolver;
+};
+
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise> => {
+ const [token, username, password, additionalHeaders] = await Promise.all([
+ resolve(options, config.TOKEN),
+ resolve(options, config.USERNAME),
+ resolve(options, config.PASSWORD),
+ resolve(options, config.HEADERS),
+ ]);
+
+ const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
+
+ const headers = Object.entries({
+ Accept: 'application/json',
+ ...additionalHeaders,
+ ...options.headers,
+ ...formHeaders,
+ })
+ .filter(([_, value]) => isDefined(value))
+ .reduce((headers, [key, value]) => ({
+ ...headers,
+ [key]: String(value),
+ }), {} as Record);
+
+ if (isStringWithValue(token)) {
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ if (isStringWithValue(username) && isStringWithValue(password)) {
+ const credentials = base64(`${username}:${password}`);
+ headers['Authorization'] = `Basic ${credentials}`;
+ }
+
+ if (options.body !== undefined) {
+ if (options.mediaType) {
+ headers['Content-Type'] = options.mediaType;
+ } else if (isBlob(options.body)) {
+ headers['Content-Type'] = options.body.type || 'application/octet-stream';
+ } else if (isString(options.body)) {
+ headers['Content-Type'] = 'text/plain';
+ } else if (!isFormData(options.body)) {
+ headers['Content-Type'] = 'application/json';
+ }
+ }
+
+ return headers;
+};
+
+export const getRequestBody = (options: ApiRequestOptions): any => {
+ if (options.body) {
+ return options.body;
+ }
+ return undefined;
+};
+
+export const sendRequest = async (
+ config: OpenAPIConfig,
+ options: ApiRequestOptions,
+ url: string,
+ body: any,
+ formData: FormData | undefined,
+ headers: Record,
+ onCancel: OnCancel,
+ axiosClient: AxiosInstance
+): Promise> => {
+ const source = axios.CancelToken.source();
+
+ const requestConfig: AxiosRequestConfig = {
+ url,
+ headers,
+ data: body ?? formData,
+ method: options.method,
+ withCredentials: config.WITH_CREDENTIALS,
+ withXSRFToken: config.CREDENTIALS === 'include' ? config.WITH_CREDENTIALS : false,
+ cancelToken: source.token,
+ };
+
+ onCancel(() => source.cancel('The user aborted a request.'));
+
+ try {
+ return await axiosClient.request(requestConfig);
+ } catch (error) {
+ const axiosError = error as AxiosError;
+ if (axiosError.response) {
+ return axiosError.response;
+ }
+ throw error;
+ }
+};
+
+export const getResponseHeader = (response: AxiosResponse, responseHeader?: string): string | undefined => {
+ if (responseHeader) {
+ const content = response.headers[responseHeader];
+ if (isString(content)) {
+ return content;
+ }
+ }
+ return undefined;
+};
+
+export const getResponseBody = (response: AxiosResponse): any => {
+ if (response.status !== 204) {
+ return response.data;
+ }
+ return undefined;
+};
+
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+ const errors: Record = {
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 500: 'Internal Server Error',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ ...options.errors,
+ }
+
+ const error = errors[result.status];
+ if (error) {
+ throw new ApiError(options, result, error);
+ }
+
+ if (!result.ok) {
+ const errorStatus = result.status ?? 'unknown';
+ const errorStatusText = result.statusText ?? 'unknown';
+ const errorBody = (() => {
+ try {
+ return JSON.stringify(result.body, null, 2);
+ } catch (e) {
+ return undefined;
+ }
+ })();
+
+ throw new ApiError(options, result,
+ `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+ );
+ }
+};
+
+/**
+ * Request method
+ * @param config The OpenAPI configuration object
+ * @param options The request options from the service
+ * @param axiosClient The axios client instance to use
+ * @returns CancelablePromise
+ * @throws ApiError
+ */
+export const request = (config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise => {
+ return new CancelablePromise(async (resolve, reject, onCancel) => {
+ try {
+ const url = getUrl(config, options);
+ const formData = getFormData(options);
+ const body = getRequestBody(options);
+ const headers = await getHeaders(config, options, formData);
+
+ if (!onCancel.isCancelled) {
+ const response = await sendRequest(config, options, url, body, formData, headers, onCancel, axiosClient);
+ const responseBody = getResponseBody(response);
+ const responseHeader = getResponseHeader(response, options.responseHeader);
+
+ const result: ApiResult = {
+ url,
+ ok: isSuccess(response.status),
+ status: response.status,
+ statusText: response.statusText,
+ body: responseHeader ?? responseBody,
+ };
+
+ catchErrorCodes(options, result);
+
+ resolve(result.body);
+ }
+ } catch (error) {
+ reject(error);
+ }
+ });
+};
diff --git a/modules/frontend/src/api_/index.ts b/modules/frontend/src/api_/index.ts
new file mode 100644
index 0000000..f0d09ee
--- /dev/null
+++ b/modules/frontend/src/api_/index.ts
@@ -0,0 +1,23 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export { ApiError } from './core/ApiError';
+export { CancelablePromise, CancelError } from './core/CancelablePromise';
+export { OpenAPI } from './core/OpenAPI';
+export type { OpenAPIConfig } from './core/OpenAPI';
+
+export type { cursor } from './models/cursor';
+export type { Image } from './models/Image';
+export { ReleaseSeason } from './models/ReleaseSeason';
+export type { Review } from './models/Review';
+export type { Studio } from './models/Studio';
+export type { Tag } from './models/Tag';
+export type { Tags } from './models/Tags';
+export type { Title } from './models/Title';
+export { TitleStatus } from './models/TitleStatus';
+export type { User } from './models/User';
+export type { UserTitle } from './models/UserTitle';
+export { UserTitleStatus } from './models/UserTitleStatus';
+
+export { DefaultService } from './services/DefaultService';
diff --git a/modules/frontend/src/api_/models/Image.ts b/modules/frontend/src/api_/models/Image.ts
new file mode 100644
index 0000000..1317db7
--- /dev/null
+++ b/modules/frontend/src/api_/models/Image.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type Image = {
+ id?: number;
+ storage_type?: string;
+ image_path?: string;
+};
+
diff --git a/modules/frontend/src/api_/models/ReleaseSeason.ts b/modules/frontend/src/api_/models/ReleaseSeason.ts
new file mode 100644
index 0000000..182b980
--- /dev/null
+++ b/modules/frontend/src/api_/models/ReleaseSeason.ts
@@ -0,0 +1,13 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Title release season
+ */
+export enum ReleaseSeason {
+ WINTER = 'winter',
+ SPRING = 'spring',
+ SUMMER = 'summer',
+ FALL = 'fall',
+}
diff --git a/modules/frontend/src/api/models/CursorObj.ts b/modules/frontend/src/api_/models/Review.ts
similarity index 66%
rename from modules/frontend/src/api/models/CursorObj.ts
rename to modules/frontend/src/api_/models/Review.ts
index f54abb1..9b453b7 100644
--- a/modules/frontend/src/api/models/CursorObj.ts
+++ b/modules/frontend/src/api_/models/Review.ts
@@ -2,8 +2,4 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
-export type CursorObj = {
- id: number;
- param?: string;
-};
-
+export type Review = Record;
diff --git a/modules/frontend/src/api_/models/Studio.ts b/modules/frontend/src/api_/models/Studio.ts
new file mode 100644
index 0000000..062695a
--- /dev/null
+++ b/modules/frontend/src/api_/models/Studio.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { Image } from './Image';
+export type Studio = {
+ id: number;
+ name: string;
+ poster?: Image;
+ description?: string;
+};
+
diff --git a/modules/frontend/src/api_/models/Tag.ts b/modules/frontend/src/api_/models/Tag.ts
new file mode 100644
index 0000000..665c724
--- /dev/null
+++ b/modules/frontend/src/api_/models/Tag.ts
@@ -0,0 +1,8 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * A localized tag: keys are language codes (ISO 639-1), values are tag names
+ */
+export type Tag = Record;
diff --git a/modules/frontend/src/api/models/TitleSort.ts b/modules/frontend/src/api_/models/Tags.ts
similarity index 60%
rename from modules/frontend/src/api/models/TitleSort.ts
rename to modules/frontend/src/api_/models/Tags.ts
index 1c9385e..748f066 100644
--- a/modules/frontend/src/api/models/TitleSort.ts
+++ b/modules/frontend/src/api_/models/Tags.ts
@@ -2,7 +2,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
+import type { Tag } from './Tag';
/**
- * Title sort order
+ * Array of localized tags
*/
-export type TitleSort = 'id' | 'year' | 'rating' | 'views';
+export type Tags = Array;
diff --git a/modules/frontend/src/api/models/title_sort.ts b/modules/frontend/src/api_/models/Title.ts
similarity index 61%
rename from modules/frontend/src/api/models/title_sort.ts
rename to modules/frontend/src/api_/models/Title.ts
index 69b01a7..4da7aa3 100644
--- a/modules/frontend/src/api/models/title_sort.ts
+++ b/modules/frontend/src/api_/models/Title.ts
@@ -2,5 +2,4 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
-import type { TitleSort } from './TitleSort';
-export type title_sort = TitleSort;
+export type Title = Record;
diff --git a/modules/frontend/src/api_/models/TitleStatus.ts b/modules/frontend/src/api_/models/TitleStatus.ts
new file mode 100644
index 0000000..811ece8
--- /dev/null
+++ b/modules/frontend/src/api_/models/TitleStatus.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Title status
+ */
+export enum TitleStatus {
+ FINISHED = 'finished',
+ ONGOING = 'ongoing',
+ PLANNED = 'planned',
+}
diff --git a/modules/frontend/src/api_/models/User.ts b/modules/frontend/src/api_/models/User.ts
new file mode 100644
index 0000000..541028e
--- /dev/null
+++ b/modules/frontend/src/api_/models/User.ts
@@ -0,0 +1,35 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type User = {
+ /**
+ * Unique user ID (primary key)
+ */
+ id: number;
+ /**
+ * ID of the user avatar (references images table)
+ */
+ avatar_id?: number | null;
+ /**
+ * User email
+ */
+ mail?: string;
+ /**
+ * Username (alphanumeric + _ or -)
+ */
+ nickname: string;
+ /**
+ * Display name
+ */
+ disp_name?: string;
+ /**
+ * User description
+ */
+ user_desc?: string;
+ /**
+ * Timestamp when the user was created
+ */
+ creation_date?: string;
+};
+
diff --git a/modules/frontend/src/api_/models/UserTitle.ts b/modules/frontend/src/api_/models/UserTitle.ts
new file mode 100644
index 0000000..26d5ddc
--- /dev/null
+++ b/modules/frontend/src/api_/models/UserTitle.ts
@@ -0,0 +1,5 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type UserTitle = Record;
diff --git a/modules/frontend/src/api_/models/UserTitleStatus.ts b/modules/frontend/src/api_/models/UserTitleStatus.ts
new file mode 100644
index 0000000..20651fe
--- /dev/null
+++ b/modules/frontend/src/api_/models/UserTitleStatus.ts
@@ -0,0 +1,13 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * User's title status
+ */
+export enum UserTitleStatus {
+ FINISHED = 'finished',
+ PLANNED = 'planned',
+ DROPPED = 'dropped',
+ IN_PROGRESS = 'in-progress',
+}
diff --git a/modules/frontend/src/api_/models/cursor.ts b/modules/frontend/src/api_/models/cursor.ts
new file mode 100644
index 0000000..5788e14
--- /dev/null
+++ b/modules/frontend/src/api_/models/cursor.ts
@@ -0,0 +1,5 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type cursor = string;
diff --git a/modules/frontend/src/api_/services/DefaultService.ts b/modules/frontend/src/api_/services/DefaultService.ts
new file mode 100644
index 0000000..b0ae54d
--- /dev/null
+++ b/modules/frontend/src/api_/services/DefaultService.ts
@@ -0,0 +1,148 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ReleaseSeason } from '../models/ReleaseSeason';
+import type { Title } from '../models/Title';
+import type { TitleStatus } from '../models/TitleStatus';
+import type { User } from '../models/User';
+import type { UserTitle } from '../models/UserTitle';
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+export class DefaultService {
+ /**
+ * Get titles
+ * @param word
+ * @param status
+ * @param rating
+ * @param releaseYear
+ * @param releaseSeason
+ * @param limit
+ * @param offset
+ * @param fields
+ * @returns Title List of titles
+ * @throws ApiError
+ */
+ public static getTitles(
+ word?: string,
+ status?: TitleStatus,
+ rating?: number,
+ releaseYear?: number,
+ releaseSeason?: ReleaseSeason,
+ limit: number = 10,
+ offset?: number,
+ fields: string = 'all',
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/titles',
+ query: {
+ 'word': word,
+ 'status': status,
+ 'rating': rating,
+ 'release_year': releaseYear,
+ 'release_season': releaseSeason,
+ 'limit': limit,
+ 'offset': offset,
+ 'fields': fields,
+ },
+ errors: {
+ 400: `Request params are not correct`,
+ 500: `Unknown server error`,
+ },
+ });
+ }
+ /**
+ * Get title description
+ * @param titleId
+ * @param fields
+ * @returns Title Title description
+ * @throws ApiError
+ */
+ public static getTitles1(
+ titleId: number,
+ fields: string = 'all',
+ ): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/titles/{title_id}',
+ path: {
+ 'title_id': titleId,
+ },
+ query: {
+ 'fields': fields,
+ },
+ errors: {
+ 400: `Request params are not correct`,
+ 404: `Title not found`,
+ 500: `Unknown server error`,
+ },
+ });
+ }
+ /**
+ * Get user info
+ * @param userId
+ * @param fields
+ * @returns User User info
+ * @throws ApiError
+ */
+ public static getUsers(
+ userId: string,
+ fields: string = 'all',
+ ): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/users/{user_id}',
+ path: {
+ 'user_id': userId,
+ },
+ query: {
+ 'fields': fields,
+ },
+ errors: {
+ 400: `Request params are not correct`,
+ 404: `User not found`,
+ 500: `Unknown server error`,
+ },
+ });
+ }
+ /**
+ * Get user titles
+ * @param userId
+ * @param cursor
+ * @param query
+ * @param limit
+ * @param offset
+ * @param fields
+ * @returns UserTitle List of user titles
+ * @throws ApiError
+ */
+ public static getUsersTitles(
+ userId: string,
+ cursor?: string,
+ query?: string,
+ limit: number = 10,
+ offset?: number,
+ fields: string = 'all',
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/users/{user_id}/titles',
+ path: {
+ 'user_id': userId,
+ },
+ query: {
+ 'cursor': cursor,
+ 'query': query,
+ 'limit': limit,
+ 'offset': offset,
+ 'fields': fields,
+ },
+ errors: {
+ 400: `Request params are not correct`,
+ 500: `Unknown server error`,
+ },
+ });
+ }
+}
diff --git a/modules/frontend/src/components/ListView/ListView.tsx b/modules/frontend/src/components/ListView/ListView.tsx
index e0e8ab9..77fea97 100644
--- a/modules/frontend/src/components/ListView/ListView.tsx
+++ b/modules/frontend/src/components/ListView/ListView.tsx
@@ -1,103 +1,52 @@
-import React, { useState, useEffect } from "react";
-import { Squares2X2Icon, Bars3Icon } from "@heroicons/react/24/solid";
-import type { CursorObj } from "../../api";
+import React from "react";
-export type ListViewProps = {
- fetchItems: (cursor: string, limit: number) => Promise<{ items: T[]; cursor: CursorObj}>;
- renderItem: (item: T, layout: "square" | "horizontal") => React.ReactNode;
- pageSize?: number;
- searchPlaceholder?: string;
- setSearch: any;
-};
+interface ListViewProps {
+ hook: ReturnType>;
+ renderHorizontal: (item: TItem) => React.ReactNode;
+ renderSquare: (item: TItem) => React.ReactNode;
+}
-export function ListView({
- fetchItems,
- renderItem,
- pageSize = 20,
- searchPlaceholder = "Search...",
-}: ListViewProps) {
- const [items, setItems] = useState([]);
- const [cursorObj, setCursorObj] = useState(undefined);
- const [loading, setLoading] = useState(true);
- const [loadingMore, setLoadingMore] = useState(false);
- const [search, setSearch] = useState("");
- const [layout, setLayout] = useState<"square" | "horizontal">("horizontal");
- const [error, setError] = useState(null);
-
- const loadItems = async (reset: boolean = false) => {
- try {
- if (reset) {
- setLoading(true);
- setCursorObj(undefined);
- } else {
- setLoadingMore(true);
- }
-
- const cursorStr = cursorObj ? btoa(JSON.stringify(cursorObj)) : ""
- console.log("encoded cursor: " + cursorStr)
-
- const result = await fetchItems(cursorStr, pageSize);
-
- if (reset) setItems(result.items);
- else setItems(prev => [...prev, ...result.items]);
-
- setCursorObj(result.cursor);
- setError(null);
- } catch (err: any) {
- console.error(err);
- setError("Failed to fetch items.");
- } finally {
- setLoading(false);
- setLoadingMore(false);
- }
- };
-
- useEffect(() => {
- loadItems(true);
- }, [search]);
+export function ListView({
+ hook,
+ renderHorizontal,
+ renderSquare
+}: ListViewProps) {
+ const { items, search, setSearch, viewMode, setViewMode, loadMore, hasMore } = hook;
return (
-
-
+
+ {/* Search + Layout Switcher */}
+
setSearch(e.target.value)}
- className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-black"
/>
-
+
+
+
- {error &&
{error}
}
-
+ {/* Items */}
- {items.map(item => renderItem(item, layout))}
+ {items.map(item =>
+ viewMode === "horizontal"
+ ? renderHorizontal(item)
+ : renderSquare(item)
+ )}
- {cursorObj && (
-
-
-
+ {hasMore && (
+
)}
-
- {loading &&
Loading...
}
);
-}
+}
\ No newline at end of file
diff --git a/modules/frontend/src/components/ListView/useListView.tsx b/modules/frontend/src/components/ListView/useListView.tsx
new file mode 100644
index 0000000..20c3597
--- /dev/null
+++ b/modules/frontend/src/components/ListView/useListView.tsx
@@ -0,0 +1,37 @@
+import { useState, useEffect } from "react";
+import type { FetchFunction } from "../../types/list";
+
+export function useListView
(fetchFn: FetchFunction) {
+ const [items, setItems] = useState([]);
+ const [cursor, setCursor] = useState();
+ const [search, setSearch] = useState("");
+ const [viewMode, setViewMode] = useState<"horizontal" | "square">("horizontal");
+ const [isLoading, setIsLoading] = useState(false);
+
+ useEffect(() => {
+ loadItems(true);
+ }, [search]);
+
+ const loadItems = async (reset = false) => {
+ setIsLoading(true);
+ const result = await fetchFn({
+ search,
+ cursor: reset ? undefined : cursor,
+ });
+
+ setItems(prev => reset ? result.items : [...prev, ...result.items]);
+ setCursor(result.nextCursor);
+ setIsLoading(false);
+ };
+
+ return {
+ items,
+ search,
+ setSearch,
+ viewMode,
+ setViewMode,
+ loadMore: () => loadItems(),
+ hasMore: Boolean(cursor),
+ isLoading,
+ };
+}
\ No newline at end of file
diff --git a/modules/frontend/src/components/cards/TitleCardHorizontal.tsx b/modules/frontend/src/components/cards/TitleCardHorizontal.tsx
index cde6037..c3a8159 100644
--- a/modules/frontend/src/components/cards/TitleCardHorizontal.tsx
+++ b/modules/frontend/src/components/cards/TitleCardHorizontal.tsx
@@ -9,13 +9,13 @@ export function TitleCardHorizontal({ title }: { title: Title }) {
border: "1px solid #ddd",
borderRadius: 8
}}>
- {title.poster?.image_path && (
-
+ {title.posterUrl && (
+
)}
-
{title.title_names["en"]}
-
{title.release_year} · {title.release_season} · Rating: {title.rating}
-
Status: {title.title_status}
+
{title.name}
+
{title.year} · {title.season} · Rating: {title.rating}
+
Status: {title.status}
);
diff --git a/modules/frontend/src/components/cards/TitleCardSquare.tsx b/modules/frontend/src/components/cards/TitleCardSquare.tsx
index e21c258..0fc0339 100644
--- a/modules/frontend/src/components/cards/TitleCardSquare.tsx
+++ b/modules/frontend/src/components/cards/TitleCardSquare.tsx
@@ -10,12 +10,12 @@ export function TitleCardSquare({ title }: { title: Title }) {
borderRadius: 8,
textAlign: "center"
}}>
- {title.poster?.image_path && (
-

+ {title.posterUrl && (
+

)}
-
{title.title_names["en"]}
- {title.release_year} • {title.rating}
+ {title.name}
+ {title.year} • {title.rating}
);
diff --git a/modules/frontend/src/index.css b/modules/frontend/src/index.css
index e20de02..08a3ac9 100644
--- a/modules/frontend/src/index.css
+++ b/modules/frontend/src/index.css
@@ -1,8 +1,68 @@
-@import "tailwindcss";
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
-html, body, #root {
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
margin: 0;
- padding: 0;
- width: 100%;
- height: 100%;
-}
\ No newline at end of file
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/modules/frontend/src/pages/TitlesPage/TitlesPage.module.css b/modules/frontend/src/pages/TitlesPage/TitlesPage.module.css
index f1d8c73..9cc728b 100644
--- a/modules/frontend/src/pages/TitlesPage/TitlesPage.module.css
+++ b/modules/frontend/src/pages/TitlesPage/TitlesPage.module.css
@@ -1 +1,59 @@
-@import "tailwindcss";
+.container {
+ padding: 24px;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+
+.searchInput {
+ padding: 8px;
+ width: 240px;
+}
+
+.list {
+ display: grid;
+ gap: 12px;
+}
+
+.card {
+ display: flex;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 8px;
+ gap: 12px;
+}
+
+.poster {
+ width: 80px;
+ height: 120px;
+ object-fit: cover;
+ border-radius: 4px;
+}
+
+.posterPlaceholder {
+ width: 80px;
+ height: 120px;
+ background: #eee;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.cardInfo {
+ display: flex;
+ flex-direction: column;
+}
+
+.loadMore {
+ margin-top: 16px;
+ padding: 8px 16px;
+}
+
+.loader,
+.error {
+ padding: 20px;
+ text-align: center;
+}
diff --git a/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx b/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx
index b59f737..438d828 100644
--- a/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx
+++ b/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx
@@ -1,52 +1,114 @@
-import { ListView } from "../../components/ListView/ListView";
+import React, { useEffect, useState } from "react";
import { DefaultService } from "../../api/services/DefaultService";
-import { TitleCardSquare } from "../../components/cards/TitleCardSquare";
-import { TitleCardHorizontal } from "../../components/cards/TitleCardHorizontal";
-import type { Title } from "../../api";
-import { useState, useEffect } from "react";
+import type { Title } from "../../api/models/Title";
+import styles from "./TitlesPage.module.css";
-const PAGE_SIZE = 20;
-export default function TitlesPage() {
+const LIMIT = 20;
+
+const TitlesPage: React.FC = () => {
+ const [titles, setTitles] = useState([]);
const [search, setSearch] = useState("");
+ const [offset, setOffset] = useState(0);
- const loadTitles = async (cursor: string, limit: number) => {
- const result = await DefaultService.getTitles(
- cursor,
- undefined,
- true,
- search,
- undefined,
- undefined,
- undefined,
- undefined,
- limit,
- undefined,
- 'all'
- );
+ const [loading, setLoading] = useState(true);
+ const [loadingMore, setLoadingMore] = useState(false);
+ const [error, setError] = useState(null);
- return {
- items: result.data ?? [],
- cursor: result.cursor ?? null,
- };
+ const fetchTitles = async (reset: boolean) => {
+ try {
+ if (reset) {
+ setLoading(true);
+ setOffset(0);
+ } else {
+ setLoadingMore(true);
+ }
+
+ const result = await DefaultService.getTitles(
+ search || undefined,
+ undefined, // status
+ undefined, // rating
+ undefined, // release_year
+ undefined, // release_season
+ LIMIT,
+ reset ? 0 : offset,
+ "all"
+ );
+
+ if (reset) {
+ setTitles(result);
+ } else {
+ setTitles(prev => [...prev, ...result]);
+ }
+
+ if (result.length > 0) {
+ setOffset(prev => prev + LIMIT);
+ }
+
+ } catch (err) {
+ console.error(err);
+ setError("Failed to fetch titles.");
+ } finally {
+ setLoading(false);
+ setLoadingMore(false);
+ }
};
- return (
-
-
-
Titles
+ useEffect(() => {
+ fetchTitles(true);
+ }, [search]);
-
- pageSize={PAGE_SIZE}
- fetchItems={loadTitles}
- searchPlaceholder="Search titles..."
- renderItem={(title, layout) =>
- layout === "square"
- ?
- :
- }
- setSearch={setSearch}
- />
+ if (loading) return Loading...
;
+ if (error) return {error}
;
+
+ return (
+
+
+
Titles
+
+ setSearch(e.target.value)}
+ />
+
+
+
+ {titles.map((t) => (
+
+ {t.poster_id ? (
+

+ ) : (
+
No Image
+ )}
+
+
+
{t.name}
+
+ {t.release_year} • {t.release_season}
+
+
Rating: {t.rating}
+
{t.status}
+
+
+ ))}
+
+
+ {titles.length > 0 && (
+
+ )}
);
-}
+};
+export default TitlesPage;
diff --git a/modules/frontend/vite.config.ts b/modules/frontend/vite.config.ts
index 6c261e6..4cfbdd0 100644
--- a/modules/frontend/vite.config.ts
+++ b/modules/frontend/vite.config.ts
@@ -1,13 +1,9 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
-import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
- plugins: [
- react(),
- tailwindcss()
- ],
+ plugins: [react()],
server: {
host: '127.0.0.1',
port: 8083,