diff --git a/package-lock.json b/package-lock.json index 2926f70..fbf84d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", @@ -105,6 +106,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -176,6 +178,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1157,9 +1160,9 @@ } }, "node_modules/@ibm-cloud/openapi-ruleset": { - "version": "1.33.3", - "resolved": "https://registry.npmjs.org/@ibm-cloud/openapi-ruleset/-/openapi-ruleset-1.33.3.tgz", - "integrity": "sha512-lOxglXIzUZwsw5WsbgZraxxzAYMdXYyiMNOioxYJYTd55ZuN4XEERoPdV5v1oPTdKedHEUSQu5siiSHToENFdA==", + "version": "1.33.4", + "resolved": "https://registry.npmjs.org/@ibm-cloud/openapi-ruleset/-/openapi-ruleset-1.33.4.tgz", + "integrity": "sha512-fF1/Uk8jbQIAnWueUxHan6ywORXwQbMvZ6hGjpY77sXLrgcVjWsbJPndeakJxOsTy3pxYcw9cvmkEv+IcIK+nQ==", "license": "Apache-2.0", "dependencies": { "@ibm-cloud/openapi-ruleset-utilities": "1.9.0", @@ -1173,7 +1176,7 @@ "loglevel": "^1.9.2", "loglevel-plugin-prefix": "0.8.4", "minimatch": "^6.2.0", - "validator": "^13.11.0" + "validator": "^13.15.23" }, "engines": { "node": ">=16.0.0" @@ -1329,27 +1332,27 @@ } }, "node_modules/@orval/angular": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/angular/-/angular-7.15.0.tgz", - "integrity": "sha512-CVQfohMdl7lU0StRlGJ0RfuhmsV+DrAy0x2sU8Mo6+Jb0UYGPhh/lQ+umsAHrCn5sqmoR8AEBoRDSYiD83rw/w==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/angular/-/angular-7.17.0.tgz", + "integrity": "sha512-3DnUU/2vUhKC33bmM2xMGRbbJRs8Qvubvjg+0QGh3AxedOYWRcLTxsuZNyr6n4y2TomrsbB1rFMTveTotE2VuA==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0" + "@orval/core": "7.17.0" } }, "node_modules/@orval/axios": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/axios/-/axios-7.15.0.tgz", - "integrity": "sha512-NseXzeQpIfycvKaTr+g/pJoIGOqbR5IPWKwWtUGRal78k48NaVKlC5TIYR0PwyCUvxhli4rKPUsH5hsdUBZu/A==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/axios/-/axios-7.17.0.tgz", + "integrity": "sha512-qbTOOOGjtfFDgpY1pHJNEO71CybSQiPJxuspVJDnWeoR7cwxOuZuWLWX3QE0bZ/2NHUg11rcuHwzzvPHGYmb0w==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0" + "@orval/core": "7.17.0" } }, "node_modules/@orval/core": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/core/-/core-7.15.0.tgz", - "integrity": "sha512-jkEZQKvVZN6++ji6PicTbPHo/bzhvDqg2GIXL9u2t6rFgFwowZ92CP3v+S5bku+Y82d+wDYRUBCr1fVg/u9dzg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/core/-/core-7.17.0.tgz", + "integrity": "sha512-oLHJitYNUbPYCijKt77az9Q7PnQE8ga79hVUt46c5cL4dXjjbRMLXSCIxB4M5lbu4D9q4G6ZgjCAxv+Fr7wGRA==", "license": "MIT", "dependencies": { "@apidevtools/swagger-parser": "^12.1.0", @@ -1374,83 +1377,89 @@ } }, "node_modules/@orval/fetch": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/fetch/-/fetch-7.15.0.tgz", - "integrity": "sha512-GwQohzMmwuSSbyu68z0gShGJIeRRYFtBJzugZCmY7z99A1pksaWvierWIHc7tmp2jXeOrodat7h/Ju3Hsw6WAQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/fetch/-/fetch-7.17.0.tgz", + "integrity": "sha512-VZuSKa2tMhHyL4BKiPyXmNj0Z5jF92lisTgWdm24WdmFfrzeGCHDyBbim6ivOMnrcvf0v7o16EeaTdMeNiaGdQ==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", + "@orval/core": "7.17.0", "openapi3-ts": "4.5.0" } }, "node_modules/@orval/hono": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/hono/-/hono-7.15.0.tgz", - "integrity": "sha512-lw8UYTEjQYUq7Fswh0INXyWpOaj9V1wTfXh5KL2xMjI2qhuyAoQhCkG9M1a07X7cVrK0RxSG5ZkbBeWWBeCjDA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/hono/-/hono-7.17.0.tgz", + "integrity": "sha512-/78Gb346+I0jPLML/s/QK7O8sJoiCIQxKT0sJ3YVXPLSKQ4aBEYGcXthAW/LwJTZWz8Rm0vGGnz4svra3gafwg==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", - "@orval/zod": "7.15.0", + "@orval/core": "7.17.0", + "@orval/zod": "7.17.0", "fs-extra": "^11.3.2", "lodash.uniq": "^4.5.0", "openapi3-ts": "4.5.0" } }, "node_modules/@orval/mcp": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/mcp/-/mcp-7.15.0.tgz", - "integrity": "sha512-unq34kRmLNMCG/9q6Kl5bilHjWIapt2dQfdoeb+02N/Pt870hulgaH3ERFOl23JPOUBW9vj2itbE+Qin5z2/2w==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/mcp/-/mcp-7.17.0.tgz", + "integrity": "sha512-izVIA3XgBpdQ6u2VLD7lxXbksq2K0wf8ypmQAufd5SewSoyEPcu8jIlLovGLTI5WiBr0pJnHLoltJ4Pl/zkZ0A==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", - "@orval/fetch": "7.15.0", - "@orval/zod": "7.15.0", + "@orval/core": "7.17.0", + "@orval/fetch": "7.17.0", + "@orval/zod": "7.17.0", "openapi3-ts": "4.5.0" } }, "node_modules/@orval/mock": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/mock/-/mock-7.15.0.tgz", - "integrity": "sha512-3oe6jfYsOWQVm/3A7T2ABjfjkdXTrNwB+KYn/4IToWtzViQjPQxderrNhB8yH8PmEzqRxkz8vGfqpGSUIfRikQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/mock/-/mock-7.17.0.tgz", + "integrity": "sha512-A/A/50XXBgidhTlQDyPQp6nIUfrP27GagyEQ70ztXS5Z9y1WJe7K5ZjjCnISOc7cFTZJmsYKvuCUo4ptfTm1bA==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", + "@orval/core": "7.17.0", "openapi3-ts": "4.5.0" } }, "node_modules/@orval/query": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/query/-/query-7.15.0.tgz", - "integrity": "sha512-0SmpZNfisOU4piEWpfynl+O2EKRiH2FS6j5cGewDpsAjmZN7Pqg35h9EGvtYUgFisURH5z8AzB8P8hfVeNjxDA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/query/-/query-7.17.0.tgz", + "integrity": "sha512-h/XZRpOLOewIPa/3uSk68M6CSgylrGSUxHux9uEH3P2nYWjwYV+GxuOUptzuONA37LGd76mgytW6tK27VmYeCA==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", - "@orval/fetch": "7.15.0", + "@orval/core": "7.17.0", + "@orval/fetch": "7.17.0", "chalk": "^4.1.2", "lodash.omitby": "^4.6.0" } }, "node_modules/@orval/swr": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/swr/-/swr-7.15.0.tgz", - "integrity": "sha512-papSt3cuhxSwIj3xoHz1JYt8Btt+qGHMsjKFoDJK4EYaotZRp5OYSElojJl8aOTyKEYc+3elQNkncReF24azfQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/swr/-/swr-7.17.0.tgz", + "integrity": "sha512-ZC5ZjzILWt8WE3V4gWRIg9XWHjQBubUJzin67aKERlybPN+sqMdgMEl9/XL+emUItkAIdex9cCaDHZwqjrmbKg==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", - "@orval/fetch": "7.15.0" + "@orval/core": "7.17.0", + "@orval/fetch": "7.17.0" } }, "node_modules/@orval/zod": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@orval/zod/-/zod-7.15.0.tgz", - "integrity": "sha512-E86N+NeZI644zaHVDvDSrwkHTtreatBsnkWkktZVklE4C3eP99h0SmyiUm8l6miA2jXJIh6nF+/sbWdrbkmU7A==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@orval/zod/-/zod-7.17.0.tgz", + "integrity": "sha512-ldwGUR4K0YIay+4UGR7ykTYD99Cs+CHIAOAVVny8/h9dU0CD/6sxomHuCzrC0Z2QxVZ4rL7k8g10Kffmp+dHBw==", "license": "MIT", "dependencies": { - "@orval/core": "7.15.0", + "@orval/core": "7.17.0", "lodash.uniq": "^4.5.0", "openapi3-ts": "4.5.0" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", @@ -1955,6 +1964,49 @@ } } }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", @@ -2209,6 +2261,29 @@ } } }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/rect": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", @@ -2686,6 +2761,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2770,6 +2846,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2901,6 +2978,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3356,6 +3434,7 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3366,6 +3445,7 @@ "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3428,6 +3508,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -3691,6 +3772,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3969,6 +4051,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -4168,6 +4251,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=20" } @@ -4441,9 +4525,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", @@ -4667,6 +4751,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5893,9 +5978,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5909,6 +5994,7 @@ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -6832,23 +6918,23 @@ } }, "node_modules/orval": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/orval/-/orval-7.15.0.tgz", - "integrity": "sha512-uw03ULVDLX2coGbjZalq4sKQj2io6eyhJOqiIFcY76VqiJz9GUxrBvQwaFwyxOEUfy9EoI25c+clAjpYggNeNw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/orval/-/orval-7.17.0.tgz", + "integrity": "sha512-iBqZC7HpSSL1CJ9jRCD+5vCYpedd03Udh+izcyFnWyVUN0ywuzGonizJgl5iGTgoe+VXsMM7ndV5h+DkghreMg==", "license": "MIT", "dependencies": { "@apidevtools/swagger-parser": "^12.1.0", "@commander-js/extra-typings": "^14.0.0", - "@orval/angular": "7.15.0", - "@orval/axios": "7.15.0", - "@orval/core": "7.15.0", - "@orval/fetch": "7.15.0", - "@orval/hono": "7.15.0", - "@orval/mcp": "7.15.0", - "@orval/mock": "7.15.0", - "@orval/query": "7.15.0", - "@orval/swr": "7.15.0", - "@orval/zod": "7.15.0", + "@orval/angular": "7.17.0", + "@orval/axios": "7.17.0", + "@orval/core": "7.17.0", + "@orval/fetch": "7.17.0", + "@orval/hono": "7.17.0", + "@orval/mcp": "7.17.0", + "@orval/mock": "7.17.0", + "@orval/query": "7.17.0", + "@orval/swr": "7.17.0", + "@orval/zod": "7.17.0", "chalk": "^4.1.2", "chokidar": "^4.0.3", "commander": "^14.0.1", @@ -6856,7 +6942,8 @@ "execa": "^5.1.1", "find-up": "5.0.0", "fs-extra": "^11.3.2", - "js-yaml": "4.1.0", + "jiti": "^2.6.1", + "js-yaml": "4.1.1", "lodash.uniq": "^4.5.0", "openapi3-ts": "4.5.0", "string-argv": "^0.3.2", @@ -7083,6 +7170,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7092,6 +7180,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7104,6 +7193,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.65.0.tgz", "integrity": "sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -7942,6 +8032,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8108,6 +8199,7 @@ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.14.tgz", "integrity": "sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@gerrit0/mini-shiki": "^3.12.0", "lunr": "^2.3.9", @@ -8179,6 +8271,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8371,9 +8464,9 @@ } }, "node_modules/validator": { - "version": "13.15.20", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", - "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -8384,6 +8477,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -8475,6 +8569,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index 251e4a2..4781221 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", diff --git a/src/api/generated/api.schemas.ts b/src/api/generated/api.schemas.ts index 4152488..2e198e7 100644 --- a/src/api/generated/api.schemas.ts +++ b/src/api/generated/api.schemas.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 @@ -11,6 +11,7 @@ export interface DefaultResponseDTOVoid { } export interface ImportRequestDTO { + metadataId?: string; id: string; } @@ -62,6 +63,21 @@ export interface AuthenticationRequestDTO { password: string; } +export interface DefaultResponseDTOProviderListDTO { + timestamp?: string; + data?: ProviderListDTO; + message?: string; +} + +export interface ProviderDTO { + id: number; + name: string; +} + +export interface ProviderListDTO { + providers: ProviderDTO[]; +} + export interface DefaultResponseDTOPageMangaListDTO { timestamp?: string; data?: PageMangaListDTO; @@ -87,12 +103,12 @@ export interface PageMangaListDTO { totalPages?: number; totalElements?: number; pageable?: PageableObject; + first?: boolean; + last?: boolean; size?: number; content?: MangaListDTO[]; number?: number; sort?: SortObject; - first?: boolean; - last?: boolean; numberOfElements?: number; empty?: boolean; } @@ -101,15 +117,15 @@ export interface PageableObject { pageNumber?: number; pageSize?: number; paged?: boolean; - unpaged?: boolean; offset?: number; sort?: SortObject; + unpaged?: boolean; } export interface SortObject { sorted?: boolean; - unsorted?: boolean; empty?: boolean; + unsorted?: boolean; } export interface DefaultResponseDTOListMangaChapterDTO { @@ -245,6 +261,10 @@ export type UpdateProviderMangaListParams = { providerId: number; }; +export type GetProvidersParams = { +manualImport?: boolean; +}; + export type GetMangasParams = { searchQuery?: string; genreIds?: number[]; diff --git a/src/api/generated/auth/auth.ts b/src/api/generated/auth/auth.ts index 32f2dc0..9068bb8 100644 --- a/src/api/generated/auth/auth.ts +++ b/src/api/generated/auth/auth.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/favorite-mangas/favorite-mangas.ts b/src/api/generated/favorite-mangas/favorite-mangas.ts index ca9074a..668e002 100644 --- a/src/api/generated/favorite-mangas/favorite-mangas.ts +++ b/src/api/generated/favorite-mangas/favorite-mangas.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/genre/genre.ts b/src/api/generated/genre/genre.ts index 68e7f0c..7714f47 100644 --- a/src/api/generated/genre/genre.ts +++ b/src/api/generated/genre/genre.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/management/management.ts b/src/api/generated/management/management.ts index 1f54df5..549b14e 100644 --- a/src/api/generated/management/management.ts +++ b/src/api/generated/management/management.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/manga-chapter/manga-chapter.ts b/src/api/generated/manga-chapter/manga-chapter.ts index cc7e7d5..d1dcc5a 100644 --- a/src/api/generated/manga-chapter/manga-chapter.ts +++ b/src/api/generated/manga-chapter/manga-chapter.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/manga-import-review/manga-import-review.ts b/src/api/generated/manga-import-review/manga-import-review.ts index 722f4d8..dc07a9f 100644 --- a/src/api/generated/manga-import-review/manga-import-review.ts +++ b/src/api/generated/manga-import-review/manga-import-review.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/manga-import/manga-import.ts b/src/api/generated/manga-import/manga-import.ts index b78c625..6591619 100644 --- a/src/api/generated/manga-import/manga-import.ts +++ b/src/api/generated/manga-import/manga-import.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 @@ -97,17 +97,18 @@ export const useImportMultipleFiles = ,signal?: AbortSignal ) => { return customInstance( - {url: `/manga/import/manga-dex`, method: 'POST', + {url: `/manga/import/provider/${encodeURIComponent(String(providerId))}`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: importRequestDTO, signal }, @@ -116,11 +117,11 @@ export const importFromMangaDex = ( -export const getImportFromMangaDexMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ImportRequestDTO}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ImportRequestDTO}, TContext> => { +export const getImportFromProviderMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{providerId: number;data: ImportRequestDTO}, TContext>, request?: SecondParameter} +): UseMutationOptions>, TError,{providerId: number;data: ImportRequestDTO}, TContext> => { -const mutationKey = ['importFromMangaDex']; +const mutationKey = ['importFromProvider']; const {mutation: mutationOptions, request: requestOptions} = options ? options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? options @@ -130,10 +131,10 @@ const {mutation: mutationOptions, request: requestOptions} = options ? - const mutationFn: MutationFunction>, {data: ImportRequestDTO}> = (props) => { - const {data} = props ?? {}; + const mutationFn: MutationFunction>, {providerId: number;data: ImportRequestDTO}> = (props) => { + const {providerId,data} = props ?? {}; - return importFromMangaDex(data,requestOptions) + return importFromProvider(providerId,data,requestOptions) } @@ -141,88 +142,23 @@ const {mutation: mutationOptions, request: requestOptions} = options ? return { mutationFn, ...mutationOptions }} - export type ImportFromMangaDexMutationResult = NonNullable>> - export type ImportFromMangaDexMutationBody = ImportRequestDTO - export type ImportFromMangaDexMutationError = unknown + export type ImportFromProviderMutationResult = NonNullable>> + export type ImportFromProviderMutationBody = ImportRequestDTO + export type ImportFromProviderMutationError = unknown /** - * @summary Import manga from MangaDex + * @summary Import manga from content provider */ -export const useImportFromMangaDex = (options?: { mutation?:UseMutationOptions>, TError,{data: ImportRequestDTO}, TContext>, request?: SecondParameter} +export const useImportFromProvider = (options?: { mutation?:UseMutationOptions>, TError,{providerId: number;data: ImportRequestDTO}, TContext>, request?: SecondParameter} , queryClient?: QueryClient): UseMutationResult< - Awaited>, + Awaited>, TError, - {data: ImportRequestDTO}, + {providerId: number;data: ImportRequestDTO}, TContext > => { - const mutationOptions = getImportFromMangaDexMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** - * Imports manga data from Bato into the local database. - * @summary Import manga from Bato - */ -export const importFromBato = ( - importRequestDTO: ImportRequestDTO, - options?: SecondParameter,signal?: AbortSignal -) => { - - - return customInstance( - {url: `/manga/import/bato`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: importRequestDTO, signal - }, - options); - } - - - -export const getImportFromBatoMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ImportRequestDTO}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ImportRequestDTO}, TContext> => { - -const mutationKey = ['importFromBato']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; - - - - - const mutationFn: MutationFunction>, {data: ImportRequestDTO}> = (props) => { - const {data} = props ?? {}; - - return importFromBato(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type ImportFromBatoMutationResult = NonNullable>> - export type ImportFromBatoMutationBody = ImportRequestDTO - export type ImportFromBatoMutationError = unknown - - /** - * @summary Import manga from Bato - */ -export const useImportFromBato = (options?: { mutation?:UseMutationOptions>, TError,{data: ImportRequestDTO}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ImportRequestDTO}, - TContext - > => { - - const mutationOptions = getImportFromBatoMutationOptions(options); + const mutationOptions = getImportFromProviderMutationOptions(options); return useMutation(mutationOptions, queryClient); } diff --git a/src/api/generated/manga/manga.ts b/src/api/generated/manga/manga.ts index 7405d4d..62ba2b1 100644 --- a/src/api/generated/manga/manga.ts +++ b/src/api/generated/manga/manga.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v7.15.0 🍺 + * Generated by orval v7.17.0 🍺 * Do not edit manually. * OpenAPI definition * OpenAPI spec version: v0 diff --git a/src/api/generated/provider/provider.ts b/src/api/generated/provider/provider.ts new file mode 100644 index 0000000..c6cfba9 --- /dev/null +++ b/src/api/generated/provider/provider.ts @@ -0,0 +1,127 @@ +/** + * Generated by orval v7.17.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import { + useQuery +} from '@tanstack/react-query'; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseQueryOptions, + UseQueryResult +} from '@tanstack/react-query'; + +import type { + DefaultResponseDTOProviderListDTO, + GetProvidersParams +} from '../api.schemas'; + +import { customInstance } from '../../api'; + + +type SecondParameter unknown> = Parameters[1]; + + + +/** + * Retrieve a list of content providers + * @summary Get a list of providers + */ +export const getProviders = ( + params?: GetProvidersParams, + options?: SecondParameter,signal?: AbortSignal +) => { + + + return customInstance( + {url: `/providers`, method: 'GET', + params, signal + }, + options); + } + + + + +export const getGetProvidersQueryKey = (params?: GetProvidersParams,) => { + return [ + `/providers`, ...(params ? [params]: []) + ] as const; + } + + +export const getGetProvidersQueryOptions = >, TError = unknown>(params?: GetProvidersParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +) => { + +const {query: queryOptions, request: requestOptions} = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetProvidersQueryKey(params); + + + + const queryFn: QueryFunction>> = ({ signal }) => getProviders(params, requestOptions, signal); + + + + + + return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } +} + +export type GetProvidersQueryResult = NonNullable>> +export type GetProvidersQueryError = unknown + + +export function useGetProviders>, TError = unknown>( + params: undefined | GetProvidersParams, options: { query:Partial>, TError, TData>> & Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + > , 'initialData' + >, request?: SecondParameter} + , queryClient?: QueryClient + ): DefinedUseQueryResult & { queryKey: DataTag } +export function useGetProviders>, TError = unknown>( + params?: GetProvidersParams, options?: { query?:Partial>, TError, TData>> & Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + > , 'initialData' + >, request?: SecondParameter} + , queryClient?: QueryClient + ): UseQueryResult & { queryKey: DataTag } +export function useGetProviders>, TError = unknown>( + params?: GetProvidersParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} + , queryClient?: QueryClient + ): UseQueryResult & { queryKey: DataTag } +/** + * @summary Get a list of providers + */ + +export function useGetProviders>, TError = unknown>( + params?: GetProvidersParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} + , queryClient?: QueryClient + ): UseQueryResult & { queryKey: DataTag } { + + const queryOptions = getGetProvidersQueryOptions(params,options) + + const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey ; + + return query; +} + + + + diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx new file mode 100644 index 0000000..86a44e5 --- /dev/null +++ b/src/components/ui/select.tsx @@ -0,0 +1,188 @@ +import * as SelectPrimitive from "@radix-ui/react-select"; +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; +import type * as React from "react"; + +import { cn } from "@/lib/utils"; + +function Select({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectGroup({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectValue({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectTrigger({ + className, + size = "default", + children, + ...props +}: React.ComponentProps & { + size?: "sm" | "default"; +}) { + return ( + + {children} + + + + + ); +} + +function SelectContent({ + className, + children, + position = "item-aligned", + align = "center", + ...props +}: React.ComponentProps) { + return ( + + + + + {children} + + + + + ); +} + +function SelectLabel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +function SelectSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +function SelectScrollDownButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, +}; diff --git a/src/features/home/components/BatoImportDialog.tsx b/src/features/home/components/BatoImportDialog.tsx deleted file mode 100644 index de44bd5..0000000 --- a/src/features/home/components/BatoImportDialog.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { useCallback } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; -import { useImportFromBato } from "@/api/generated/manga-import/manga-import.ts"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog.tsx"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form.tsx"; -import { Input } from "@/components/ui/input"; - -interface BatoImportDialogProps { - batoDialogOpen: boolean; - onBatoDialogOpenChange: (open: boolean) => void; -} - -export const BatoImportDialog = ({ - batoDialogOpen, - onBatoDialogOpenChange, -}: BatoImportDialogProps) => { - const formSchema = z.object({ - value: z.string().min(1, "Please enter a Bato URL."), - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - value: "", - }, - }); - - const { mutate: importBato, isPending: isPendingImportBato } = - useImportFromBato({ - mutation: { - onSuccess: () => { - form.reset(); - onBatoDialogOpenChange(false); - toast.success("Manga imported successfully!"); - }, - }, - }); - - const handleSubmit = useCallback( - (data: z.infer) => { - const id = data.value; - - importBato({ data: { id } }); - }, - [formSchema, importBato], - ); - - return ( - - - - Import from Bato - - Enter a Bato manga URL to import it to your library. - - -
- - ( - - Bato URL - - - - - - )} - /> - - - - - - -
-
- ); -}; diff --git a/src/features/home/components/ImportDropdown.tsx b/src/features/home/components/ImportDropdown.tsx index ca5b18f..b966aff 100644 --- a/src/features/home/components/ImportDropdown.tsx +++ b/src/features/home/components/ImportDropdown.tsx @@ -10,15 +10,13 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { useAuth } from "@/contexts/AuthContext.tsx"; -import { BatoImportDialog } from "@/features/home/components/BatoImportDialog.tsx"; -import { MangaDexImportDialog } from "@/features/home/components/MangaDexImportDialog.tsx"; import { MangaManualImportDialog } from "@/features/home/components/MangaManualImportDialog.tsx"; +import { ProviderImportDialog } from "@/features/home/components/ProviderImportDialog.tsx"; export function ImportDropdown() { const { isAuthenticated } = useAuth(); - const [mangaDexDialogOpen, setMangaDexDialogOpen] = useState(false); - const [batoDialogOpen, setBatoDialogOpen] = useState(false); + const [providerDialogOpen, setProviderDialogOpen] = useState(false); const [fileImportDialogOpen, setFileImportDialogOpen] = useState(false); if (!isAuthenticated) { @@ -35,13 +33,9 @@ export function ImportDropdown() { - setMangaDexDialogOpen(true)}> + setProviderDialogOpen(true)}> - Import from MangaDex - - setBatoDialogOpen(true)}> - - Import from Bato + Import from Provider setFileImportDialogOpen(true)}> @@ -61,14 +55,9 @@ export function ImportDropdown() { - - - void; -} - -export const MangaDexImportDialog = ({ - mangaDexDialogOpen, - onMangaDexDialogOpenChange, -}: MangaDexImportDialogProps) => { - const formSchema = z.object({ - value: z.string().min(1, "Please enter a MangaDex ID or URL."), - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - value: "", - }, - }); - - const { mutate: importMangaDex, isPending: isPendingImportMangaDex } = - useImportFromMangaDex({ - mutation: { - onSuccess: () => { - form.reset(); - onMangaDexDialogOpenChange(false); - toast.success("Manga imported successfully!"); - }, - }, - }); - - const handleSubmit = useCallback( - (data: z.infer) => { - let id = data.value; - if (data.value.length > 36) { - const match = data.value.match(/title\/([0-9a-fA-F-]{36})/); - if (match) { - id = match[1]; - } else { - alert("Invalid MangaDex URL or ID"); - return; - } - } - - if (id.length !== 36) { - alert("Invalid MangaDex ID"); - return; - } - - importMangaDex({ data: { id } }); - }, - [formSchema, importMangaDex], - ); - - return ( - - - - Import from MangaDex - - Enter a MangaDex manga URL or ID to import it to your library. - - -
- - ( - - MangaDex URL or ID - - - - - - )} - /> - - - - - - -
-
- ); -}; diff --git a/src/features/home/components/ProviderImportDialog.tsx b/src/features/home/components/ProviderImportDialog.tsx new file mode 100644 index 0000000..7b080c6 --- /dev/null +++ b/src/features/home/components/ProviderImportDialog.tsx @@ -0,0 +1,190 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useCallback } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { useImportFromProvider } from "@/api/generated/manga-import/manga-import.ts"; +import { useGetProviders } from "@/api/generated/provider/provider.ts"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog.tsx"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form.tsx"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select.tsx"; + +interface ProviderImportDialogProps { + dialogOpen: boolean; + onDialogOpenChange: (open: boolean) => void; +} + +export const ProviderImportDialog = ({ + dialogOpen, + onDialogOpenChange, +}: ProviderImportDialogProps) => { + const formSchema = z.object({ + value: z.string().min(1, "Please enter an ID or URL."), + providerId: z.string(), + myAnimeListId: z.string().optional(), + }); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + value: "", + providerId: "", + myAnimeListId: undefined, + }, + }); + + const { data: providerData, isFetching: isFetchingProviders } = + useGetProviders({ manualImport: true }); + + const { mutate: importFromProvider, isPending: isPendingImportFromProvider } = + useImportFromProvider({ + mutation: { + onSuccess: () => { + form.reset(); + onDialogOpenChange(false); + toast.success("Manga imported successfully!"); + }, + }, + }); + + const handleSubmit = useCallback( + (data: z.output) => { + importFromProvider({ + providerId: Number(data.providerId), + data: { id: data.value, metadataId: data.myAnimeListId }, + }); + }, + [importFromProvider], + ); + + return ( + + + + Import from Provider + + Enter a Provider manga URL or ID to import it to your library. + + +
+ + ( + + Provider + + + + + + )} + /> + ( + + URL or ID + + + + + + )} + /> + ( + + MyAnimeList ID (Optional) + + + + + Optionally link this manga to a MyAnimeList entry for better + precision on metadata fetching. + + + + )} + /> + + + + + + +
+
+ ); +};