Compare commits
No commits in common. "main" and "fix/manga-dex" have entirely different histories.
main
...
fix/manga-
@ -1,2 +0,0 @@
|
|||||||
VITE_API_BASE_URL=http://localhost:8080
|
|
||||||
VITE_OMV_BASE_URL=http://omv2.badger-pirarucu.ts.net:9000/mangamochi
|
|
||||||
61
.woodpecker/pipeline.yaml
Normal file
61
.woodpecker/pipeline.yaml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# .pipeline.yml
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
when:
|
||||||
|
event: [ push, pull_request ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: publish-image
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
platforms: linux/amd64
|
||||||
|
repo: git.badger-pirarucu.ts.net/mangamochi/frontend
|
||||||
|
registry: git.badger-pirarucu.ts.net
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
username:
|
||||||
|
from_secret: DOCKER_USER
|
||||||
|
password:
|
||||||
|
from_secret: DOCKER_PASSWORD
|
||||||
|
build_args:
|
||||||
|
VITE_API_BASE_URL:
|
||||||
|
from_secret: NEXT_PUBLIC_API_BASE_URL
|
||||||
|
VITE_OMV_BASE_URL:
|
||||||
|
from_secret: NEXT_PUBLIC_OMV_BASE_URL
|
||||||
|
tags:
|
||||||
|
- latest
|
||||||
|
- ${CI_COMMIT_SHA}
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
branch: [ main ]
|
||||||
|
|
||||||
|
- name: deploy
|
||||||
|
depends_on: [ publish-image ]
|
||||||
|
image: alpine:3.20
|
||||||
|
environment:
|
||||||
|
DEPLOY_USER: rov
|
||||||
|
DEPLOY_HOST: mangamochi.badger-pirarucu.ts.net
|
||||||
|
DEPLOY_PORT: 22
|
||||||
|
IMAGE: git.badger-pirarucu.ts.net/mangamochi/frontend:${CI_COMMIT_SHA}
|
||||||
|
DEPLOY_SSH_KEY:
|
||||||
|
from_secret: DEPLOY_SSH_KEY
|
||||||
|
commands:
|
||||||
|
- echo "🚀 Deploying Next.js app to $DEPLOY_HOST...."
|
||||||
|
- apk add --no-cache openssh-client docker-cli
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_rsa
|
||||||
|
- chmod 600 ~/.ssh/id_rsa
|
||||||
|
- ssh-keyscan -p $DEPLOY_PORT $DEPLOY_HOST >> ~/.ssh/known_hosts
|
||||||
|
- >
|
||||||
|
ssh -p $DEPLOY_PORT $DEPLOY_USER@$DEPLOY_HOST "
|
||||||
|
docker pull $IMAGE &&
|
||||||
|
docker stop mangamochi-frontend 2>/dev/null || true &&
|
||||||
|
docker rm mangamochi-frontend 2>/dev/null || true &&
|
||||||
|
docker run -d --name mangamochi-frontend \
|
||||||
|
--restart always \
|
||||||
|
-p 80:80 \
|
||||||
|
$IMAGE
|
||||||
|
"
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
branch: [ main ]
|
||||||
237
package-lock.json
generated
237
package-lock.json
generated
@ -14,7 +14,6 @@
|
|||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
@ -106,7 +105,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
@ -178,7 +176,6 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@ -1160,9 +1157,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ibm-cloud/openapi-ruleset": {
|
"node_modules/@ibm-cloud/openapi-ruleset": {
|
||||||
"version": "1.33.4",
|
"version": "1.33.3",
|
||||||
"resolved": "https://registry.npmjs.org/@ibm-cloud/openapi-ruleset/-/openapi-ruleset-1.33.4.tgz",
|
"resolved": "https://registry.npmjs.org/@ibm-cloud/openapi-ruleset/-/openapi-ruleset-1.33.3.tgz",
|
||||||
"integrity": "sha512-fF1/Uk8jbQIAnWueUxHan6ywORXwQbMvZ6hGjpY77sXLrgcVjWsbJPndeakJxOsTy3pxYcw9cvmkEv+IcIK+nQ==",
|
"integrity": "sha512-lOxglXIzUZwsw5WsbgZraxxzAYMdXYyiMNOioxYJYTd55ZuN4XEERoPdV5v1oPTdKedHEUSQu5siiSHToENFdA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ibm-cloud/openapi-ruleset-utilities": "1.9.0",
|
"@ibm-cloud/openapi-ruleset-utilities": "1.9.0",
|
||||||
@ -1176,7 +1173,7 @@
|
|||||||
"loglevel": "^1.9.2",
|
"loglevel": "^1.9.2",
|
||||||
"loglevel-plugin-prefix": "0.8.4",
|
"loglevel-plugin-prefix": "0.8.4",
|
||||||
"minimatch": "^6.2.0",
|
"minimatch": "^6.2.0",
|
||||||
"validator": "^13.15.23"
|
"validator": "^13.11.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
@ -1332,27 +1329,27 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/angular": {
|
"node_modules/@orval/angular": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/angular/-/angular-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/angular/-/angular-7.15.0.tgz",
|
||||||
"integrity": "sha512-3DnUU/2vUhKC33bmM2xMGRbbJRs8Qvubvjg+0QGh3AxedOYWRcLTxsuZNyr6n4y2TomrsbB1rFMTveTotE2VuA==",
|
"integrity": "sha512-CVQfohMdl7lU0StRlGJ0RfuhmsV+DrAy0x2sU8Mo6+Jb0UYGPhh/lQ+umsAHrCn5sqmoR8AEBoRDSYiD83rw/w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0"
|
"@orval/core": "7.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/axios": {
|
"node_modules/@orval/axios": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/axios/-/axios-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/axios/-/axios-7.15.0.tgz",
|
||||||
"integrity": "sha512-qbTOOOGjtfFDgpY1pHJNEO71CybSQiPJxuspVJDnWeoR7cwxOuZuWLWX3QE0bZ/2NHUg11rcuHwzzvPHGYmb0w==",
|
"integrity": "sha512-NseXzeQpIfycvKaTr+g/pJoIGOqbR5IPWKwWtUGRal78k48NaVKlC5TIYR0PwyCUvxhli4rKPUsH5hsdUBZu/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0"
|
"@orval/core": "7.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/core": {
|
"node_modules/@orval/core": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/core/-/core-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/core/-/core-7.15.0.tgz",
|
||||||
"integrity": "sha512-oLHJitYNUbPYCijKt77az9Q7PnQE8ga79hVUt46c5cL4dXjjbRMLXSCIxB4M5lbu4D9q4G6ZgjCAxv+Fr7wGRA==",
|
"integrity": "sha512-jkEZQKvVZN6++ji6PicTbPHo/bzhvDqg2GIXL9u2t6rFgFwowZ92CP3v+S5bku+Y82d+wDYRUBCr1fVg/u9dzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^12.1.0",
|
"@apidevtools/swagger-parser": "^12.1.0",
|
||||||
@ -1377,89 +1374,83 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/fetch": {
|
"node_modules/@orval/fetch": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/fetch/-/fetch-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/fetch/-/fetch-7.15.0.tgz",
|
||||||
"integrity": "sha512-VZuSKa2tMhHyL4BKiPyXmNj0Z5jF92lisTgWdm24WdmFfrzeGCHDyBbim6ivOMnrcvf0v7o16EeaTdMeNiaGdQ==",
|
"integrity": "sha512-GwQohzMmwuSSbyu68z0gShGJIeRRYFtBJzugZCmY7z99A1pksaWvierWIHc7tmp2jXeOrodat7h/Ju3Hsw6WAQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"openapi3-ts": "4.5.0"
|
"openapi3-ts": "4.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/hono": {
|
"node_modules/@orval/hono": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/hono/-/hono-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/hono/-/hono-7.15.0.tgz",
|
||||||
"integrity": "sha512-/78Gb346+I0jPLML/s/QK7O8sJoiCIQxKT0sJ3YVXPLSKQ4aBEYGcXthAW/LwJTZWz8Rm0vGGnz4svra3gafwg==",
|
"integrity": "sha512-lw8UYTEjQYUq7Fswh0INXyWpOaj9V1wTfXh5KL2xMjI2qhuyAoQhCkG9M1a07X7cVrK0RxSG5ZkbBeWWBeCjDA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"@orval/zod": "7.17.0",
|
"@orval/zod": "7.15.0",
|
||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"lodash.uniq": "^4.5.0",
|
"lodash.uniq": "^4.5.0",
|
||||||
"openapi3-ts": "4.5.0"
|
"openapi3-ts": "4.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/mcp": {
|
"node_modules/@orval/mcp": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/mcp/-/mcp-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/mcp/-/mcp-7.15.0.tgz",
|
||||||
"integrity": "sha512-izVIA3XgBpdQ6u2VLD7lxXbksq2K0wf8ypmQAufd5SewSoyEPcu8jIlLovGLTI5WiBr0pJnHLoltJ4Pl/zkZ0A==",
|
"integrity": "sha512-unq34kRmLNMCG/9q6Kl5bilHjWIapt2dQfdoeb+02N/Pt870hulgaH3ERFOl23JPOUBW9vj2itbE+Qin5z2/2w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"@orval/fetch": "7.17.0",
|
"@orval/fetch": "7.15.0",
|
||||||
"@orval/zod": "7.17.0",
|
"@orval/zod": "7.15.0",
|
||||||
"openapi3-ts": "4.5.0"
|
"openapi3-ts": "4.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/mock": {
|
"node_modules/@orval/mock": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/mock/-/mock-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/mock/-/mock-7.15.0.tgz",
|
||||||
"integrity": "sha512-A/A/50XXBgidhTlQDyPQp6nIUfrP27GagyEQ70ztXS5Z9y1WJe7K5ZjjCnISOc7cFTZJmsYKvuCUo4ptfTm1bA==",
|
"integrity": "sha512-3oe6jfYsOWQVm/3A7T2ABjfjkdXTrNwB+KYn/4IToWtzViQjPQxderrNhB8yH8PmEzqRxkz8vGfqpGSUIfRikQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"openapi3-ts": "4.5.0"
|
"openapi3-ts": "4.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/query": {
|
"node_modules/@orval/query": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/query/-/query-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/query/-/query-7.15.0.tgz",
|
||||||
"integrity": "sha512-h/XZRpOLOewIPa/3uSk68M6CSgylrGSUxHux9uEH3P2nYWjwYV+GxuOUptzuONA37LGd76mgytW6tK27VmYeCA==",
|
"integrity": "sha512-0SmpZNfisOU4piEWpfynl+O2EKRiH2FS6j5cGewDpsAjmZN7Pqg35h9EGvtYUgFisURH5z8AzB8P8hfVeNjxDA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"@orval/fetch": "7.17.0",
|
"@orval/fetch": "7.15.0",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"lodash.omitby": "^4.6.0"
|
"lodash.omitby": "^4.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/swr": {
|
"node_modules/@orval/swr": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/swr/-/swr-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/swr/-/swr-7.15.0.tgz",
|
||||||
"integrity": "sha512-ZC5ZjzILWt8WE3V4gWRIg9XWHjQBubUJzin67aKERlybPN+sqMdgMEl9/XL+emUItkAIdex9cCaDHZwqjrmbKg==",
|
"integrity": "sha512-papSt3cuhxSwIj3xoHz1JYt8Btt+qGHMsjKFoDJK4EYaotZRp5OYSElojJl8aOTyKEYc+3elQNkncReF24azfQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"@orval/fetch": "7.17.0"
|
"@orval/fetch": "7.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@orval/zod": {
|
"node_modules/@orval/zod": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@orval/zod/-/zod-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@orval/zod/-/zod-7.15.0.tgz",
|
||||||
"integrity": "sha512-ldwGUR4K0YIay+4UGR7ykTYD99Cs+CHIAOAVVny8/h9dU0CD/6sxomHuCzrC0Z2QxVZ4rL7k8g10Kffmp+dHBw==",
|
"integrity": "sha512-E86N+NeZI644zaHVDvDSrwkHTtreatBsnkWkktZVklE4C3eP99h0SmyiUm8l6miA2jXJIh6nF+/sbWdrbkmU7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"lodash.uniq": "^4.5.0",
|
"lodash.uniq": "^4.5.0",
|
||||||
"openapi3-ts": "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": {
|
"node_modules/@radix-ui/primitive": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
@ -1964,49 +1955,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-separator": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
|
||||||
@ -2261,29 +2209,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/rect": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||||
@ -2761,7 +2686,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
@ -2846,7 +2770,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
@ -2978,7 +2901,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
@ -3434,7 +3356,6 @@
|
|||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@ -3445,7 +3366,6 @@
|
|||||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@ -3508,7 +3428,6 @@
|
|||||||
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
|
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.46.2",
|
"@typescript-eslint/scope-manager": "8.46.2",
|
||||||
"@typescript-eslint/types": "8.46.2",
|
"@typescript-eslint/types": "8.46.2",
|
||||||
@ -3772,7 +3691,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@ -4051,7 +3969,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.19",
|
"baseline-browser-mapping": "^2.8.19",
|
||||||
"caniuse-lite": "^1.0.30001751",
|
"caniuse-lite": "^1.0.30001751",
|
||||||
@ -4251,7 +4168,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
|
||||||
"integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==",
|
"integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
}
|
}
|
||||||
@ -4525,9 +4441,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.24.1",
|
"version": "1.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
|
||||||
"integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
|
"integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-buffer-byte-length": "^1.0.2",
|
"array-buffer-byte-length": "^1.0.2",
|
||||||
@ -4751,7 +4667,6 @@
|
|||||||
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@ -5978,9 +5893,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
@ -5994,7 +5909,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
|
||||||
"integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
|
"integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.16.0"
|
"node": ">= 10.16.0"
|
||||||
}
|
}
|
||||||
@ -6918,23 +6832,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/orval": {
|
"node_modules/orval": {
|
||||||
"version": "7.17.0",
|
"version": "7.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/orval/-/orval-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/orval/-/orval-7.15.0.tgz",
|
||||||
"integrity": "sha512-iBqZC7HpSSL1CJ9jRCD+5vCYpedd03Udh+izcyFnWyVUN0ywuzGonizJgl5iGTgoe+VXsMM7ndV5h+DkghreMg==",
|
"integrity": "sha512-uw03ULVDLX2coGbjZalq4sKQj2io6eyhJOqiIFcY76VqiJz9GUxrBvQwaFwyxOEUfy9EoI25c+clAjpYggNeNw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^12.1.0",
|
"@apidevtools/swagger-parser": "^12.1.0",
|
||||||
"@commander-js/extra-typings": "^14.0.0",
|
"@commander-js/extra-typings": "^14.0.0",
|
||||||
"@orval/angular": "7.17.0",
|
"@orval/angular": "7.15.0",
|
||||||
"@orval/axios": "7.17.0",
|
"@orval/axios": "7.15.0",
|
||||||
"@orval/core": "7.17.0",
|
"@orval/core": "7.15.0",
|
||||||
"@orval/fetch": "7.17.0",
|
"@orval/fetch": "7.15.0",
|
||||||
"@orval/hono": "7.17.0",
|
"@orval/hono": "7.15.0",
|
||||||
"@orval/mcp": "7.17.0",
|
"@orval/mcp": "7.15.0",
|
||||||
"@orval/mock": "7.17.0",
|
"@orval/mock": "7.15.0",
|
||||||
"@orval/query": "7.17.0",
|
"@orval/query": "7.15.0",
|
||||||
"@orval/swr": "7.17.0",
|
"@orval/swr": "7.15.0",
|
||||||
"@orval/zod": "7.17.0",
|
"@orval/zod": "7.15.0",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^4.0.3",
|
||||||
"commander": "^14.0.1",
|
"commander": "^14.0.1",
|
||||||
@ -6942,8 +6856,7 @@
|
|||||||
"execa": "^5.1.1",
|
"execa": "^5.1.1",
|
||||||
"find-up": "5.0.0",
|
"find-up": "5.0.0",
|
||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"jiti": "^2.6.1",
|
"js-yaml": "4.1.0",
|
||||||
"js-yaml": "4.1.1",
|
|
||||||
"lodash.uniq": "^4.5.0",
|
"lodash.uniq": "^4.5.0",
|
||||||
"openapi3-ts": "4.5.0",
|
"openapi3-ts": "4.5.0",
|
||||||
"string-argv": "^0.3.2",
|
"string-argv": "^0.3.2",
|
||||||
@ -7170,7 +7083,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -7180,7 +7092,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@ -7193,7 +7104,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.65.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.65.0.tgz",
|
||||||
"integrity": "sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==",
|
"integrity": "sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
@ -8032,7 +7942,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@ -8199,7 +8108,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.14.tgz",
|
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.14.tgz",
|
||||||
"integrity": "sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==",
|
"integrity": "sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gerrit0/mini-shiki": "^3.12.0",
|
"@gerrit0/mini-shiki": "^3.12.0",
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
@ -8271,7 +8179,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@ -8464,9 +8371,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
"version": "13.15.23",
|
"version": "13.15.20",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
|
||||||
"integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==",
|
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
@ -8477,7 +8384,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
|
||||||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@ -8569,7 +8475,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
|
|||||||
@ -1,27 +1,30 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
*/
|
*/
|
||||||
|
export interface UpdateMangaDataCommand {
|
||||||
|
mangaId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DefaultResponseDTOVoid {
|
export interface DefaultResponseDTOVoid {
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
data?: unknown;
|
data?: unknown;
|
||||||
message?: string;
|
message?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImportRequestDTO {
|
export interface ImportMangaDexRequestDTO {
|
||||||
metadataId?: string;
|
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DefaultResponseDTOImportMangaResponseDTO {
|
export interface DefaultResponseDTOImportMangaDexResponseDTO {
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
data?: ImportMangaResponseDTO;
|
data?: ImportMangaDexResponseDTO;
|
||||||
message?: string;
|
message?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImportMangaResponseDTO {
|
export interface ImportMangaDexResponseDTO {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,21 +66,6 @@ export interface AuthenticationRequestDTO {
|
|||||||
password: string;
|
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 {
|
export interface DefaultResponseDTOPageMangaListDTO {
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
data?: PageMangaListDTO;
|
data?: PageMangaListDTO;
|
||||||
@ -102,29 +90,29 @@ export interface MangaListDTO {
|
|||||||
export interface PageMangaListDTO {
|
export interface PageMangaListDTO {
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
totalElements?: number;
|
totalElements?: number;
|
||||||
pageable?: PageableObject;
|
|
||||||
first?: boolean;
|
|
||||||
last?: boolean;
|
|
||||||
size?: number;
|
size?: number;
|
||||||
content?: MangaListDTO[];
|
content?: MangaListDTO[];
|
||||||
number?: number;
|
number?: number;
|
||||||
|
pageable?: PageableObject;
|
||||||
|
first?: boolean;
|
||||||
|
last?: boolean;
|
||||||
sort?: SortObject;
|
sort?: SortObject;
|
||||||
numberOfElements?: number;
|
numberOfElements?: number;
|
||||||
empty?: boolean;
|
empty?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageableObject {
|
export interface PageableObject {
|
||||||
|
offset?: number;
|
||||||
pageNumber?: number;
|
pageNumber?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
paged?: boolean;
|
paged?: boolean;
|
||||||
offset?: number;
|
|
||||||
sort?: SortObject;
|
sort?: SortObject;
|
||||||
unpaged?: boolean;
|
unpaged?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SortObject {
|
export interface SortObject {
|
||||||
sorted?: boolean;
|
|
||||||
empty?: boolean;
|
empty?: boolean;
|
||||||
|
sorted?: boolean;
|
||||||
unsorted?: boolean;
|
unsorted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,24 +152,12 @@ export interface MangaDTO {
|
|||||||
score: number;
|
score: number;
|
||||||
providers: MangaProviderDTO[];
|
providers: MangaProviderDTO[];
|
||||||
chapterCount: number;
|
chapterCount: number;
|
||||||
favorite: boolean;
|
|
||||||
following: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MangaProviderDTOProviderStatus = typeof MangaProviderDTOProviderStatus[keyof typeof MangaProviderDTOProviderStatus];
|
|
||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
||||||
export const MangaProviderDTOProviderStatus = {
|
|
||||||
ACTIVE: 'ACTIVE',
|
|
||||||
INACTIVE: 'INACTIVE',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export interface MangaProviderDTO {
|
export interface MangaProviderDTO {
|
||||||
id: number;
|
id: number;
|
||||||
/** @minLength 1 */
|
/** @minLength 1 */
|
||||||
providerName: string;
|
providerName: string;
|
||||||
providerStatus: MangaProviderDTOProviderStatus;
|
|
||||||
chaptersAvailable: number;
|
chaptersAvailable: number;
|
||||||
chaptersDownloaded: number;
|
chaptersDownloaded: number;
|
||||||
supportsChapterFetch: boolean;
|
supportsChapterFetch: boolean;
|
||||||
@ -197,8 +173,6 @@ export interface MangaChapterImagesDTO {
|
|||||||
id: number;
|
id: number;
|
||||||
/** @minLength 1 */
|
/** @minLength 1 */
|
||||||
mangaTitle: string;
|
mangaTitle: string;
|
||||||
previousChapterId?: number;
|
|
||||||
nextChapterId?: number;
|
|
||||||
chapterImageKeys: string[];
|
chapterImageKeys: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,14 +231,6 @@ importReviewId: number;
|
|||||||
malId: string;
|
malId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateProviderMangaListParams = {
|
|
||||||
providerId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetProvidersParams = {
|
|
||||||
manualImport?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetMangasParams = {
|
export type GetMangasParams = {
|
||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
genreIds?: number[];
|
genreIds?: number[];
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
|
|||||||
86
src/api/generated/dev-controller/dev-controller.ts
Normal file
86
src/api/generated/dev-controller/dev-controller.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.15.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
useMutation
|
||||||
|
} from '@tanstack/react-query';
|
||||||
|
import type {
|
||||||
|
MutationFunction,
|
||||||
|
QueryClient,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseMutationResult
|
||||||
|
} from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
UpdateMangaDataCommand
|
||||||
|
} from '../api.schemas';
|
||||||
|
|
||||||
|
import { customInstance } from '../../api';
|
||||||
|
|
||||||
|
|
||||||
|
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const sendRecord = (
|
||||||
|
updateMangaDataCommand: UpdateMangaDataCommand,
|
||||||
|
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
|
||||||
|
|
||||||
|
return customInstance<string>(
|
||||||
|
{url: `/records`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: updateMangaDataCommand, signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getSendRecordMutationOptions = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof sendRecord>>, TError,{data: UpdateMangaDataCommand}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
|
): UseMutationOptions<Awaited<ReturnType<typeof sendRecord>>, TError,{data: UpdateMangaDataCommand}, TContext> => {
|
||||||
|
|
||||||
|
const mutationKey = ['sendRecord'];
|
||||||
|
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<Awaited<ReturnType<typeof sendRecord>>, {data: UpdateMangaDataCommand}> = (props) => {
|
||||||
|
const {data} = props ?? {};
|
||||||
|
|
||||||
|
return sendRecord(data,requestOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions }}
|
||||||
|
|
||||||
|
export type SendRecordMutationResult = NonNullable<Awaited<ReturnType<typeof sendRecord>>>
|
||||||
|
export type SendRecordMutationBody = UpdateMangaDataCommand
|
||||||
|
export type SendRecordMutationError = unknown
|
||||||
|
|
||||||
|
export const useSendRecord = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof sendRecord>>, TError,{data: UpdateMangaDataCommand}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
|
, queryClient?: QueryClient): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof sendRecord>>,
|
||||||
|
TError,
|
||||||
|
{data: UpdateMangaDataCommand},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
|
||||||
|
const mutationOptions = getSendRecordMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions, queryClient);
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
|
|||||||
@ -1,345 +0,0 @@
|
|||||||
/**
|
|
||||||
* Generated by orval v7.17.0 🍺
|
|
||||||
* Do not edit manually.
|
|
||||||
* OpenAPI definition
|
|
||||||
* OpenAPI spec version: v0
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
useMutation
|
|
||||||
} from '@tanstack/react-query';
|
|
||||||
import type {
|
|
||||||
MutationFunction,
|
|
||||||
QueryClient,
|
|
||||||
UseMutationOptions,
|
|
||||||
UseMutationResult
|
|
||||||
} from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
DefaultResponseDTOVoid,
|
|
||||||
UpdateProviderMangaListParams
|
|
||||||
} from '../api.schemas';
|
|
||||||
|
|
||||||
import { customInstance } from '../../api';
|
|
||||||
|
|
||||||
|
|
||||||
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger user follow update
|
|
||||||
* @summary Trigger user follow update
|
|
||||||
*/
|
|
||||||
export const userFollowUpdate = (
|
|
||||||
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/management/user-follow`, method: 'POST', signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getUserFollowUpdateMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof userFollowUpdate>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof userFollowUpdate>>, TError,void, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['userFollowUpdate'];
|
|
||||||
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<Awaited<ReturnType<typeof userFollowUpdate>>, void> = () => {
|
|
||||||
|
|
||||||
|
|
||||||
return userFollowUpdate(requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type UserFollowUpdateMutationResult = NonNullable<Awaited<ReturnType<typeof userFollowUpdate>>>
|
|
||||||
|
|
||||||
export type UserFollowUpdateMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Trigger user follow update
|
|
||||||
*/
|
|
||||||
export const useUserFollowUpdate = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof userFollowUpdate>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof userFollowUpdate>>,
|
|
||||||
TError,
|
|
||||||
void,
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getUserFollowUpdateMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Queue the retrieval of the manga list for a specific provider
|
|
||||||
* @summary Queue update provider manga list
|
|
||||||
*/
|
|
||||||
export const updateProviderMangaList = (
|
|
||||||
params: UpdateProviderMangaListParams,
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/management/update-provider-manga-list`, method: 'POST',
|
|
||||||
params, signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getUpdateProviderMangaListMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateProviderMangaList>>, TError,{params: UpdateProviderMangaListParams}, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof updateProviderMangaList>>, TError,{params: UpdateProviderMangaListParams}, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['updateProviderMangaList'];
|
|
||||||
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<Awaited<ReturnType<typeof updateProviderMangaList>>, {params: UpdateProviderMangaListParams}> = (props) => {
|
|
||||||
const {params} = props ?? {};
|
|
||||||
|
|
||||||
return updateProviderMangaList(params,requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type UpdateProviderMangaListMutationResult = NonNullable<Awaited<ReturnType<typeof updateProviderMangaList>>>
|
|
||||||
|
|
||||||
export type UpdateProviderMangaListMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Queue update provider manga list
|
|
||||||
*/
|
|
||||||
export const useUpdateProviderMangaList = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateProviderMangaList>>, TError,{params: UpdateProviderMangaListParams}, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof updateProviderMangaList>>,
|
|
||||||
TError,
|
|
||||||
{params: UpdateProviderMangaListParams},
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getUpdateProviderMangaListMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Queue the retrieval of the manga lists from the content providers
|
|
||||||
* @summary Queue update manga list
|
|
||||||
*/
|
|
||||||
export const updateMangaList = (
|
|
||||||
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/management/update-manga-list`, method: 'POST', signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getUpdateMangaListMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateMangaList>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof updateMangaList>>, TError,void, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['updateMangaList'];
|
|
||||||
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<Awaited<ReturnType<typeof updateMangaList>>, void> = () => {
|
|
||||||
|
|
||||||
|
|
||||||
return updateMangaList(requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type UpdateMangaListMutationResult = NonNullable<Awaited<ReturnType<typeof updateMangaList>>>
|
|
||||||
|
|
||||||
export type UpdateMangaListMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Queue update manga list
|
|
||||||
*/
|
|
||||||
export const useUpdateMangaList = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateMangaList>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof updateMangaList>>,
|
|
||||||
TError,
|
|
||||||
void,
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getUpdateMangaListMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Sends a test notification to all users
|
|
||||||
* @summary Test notification
|
|
||||||
*/
|
|
||||||
export const testNotification = (
|
|
||||||
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/management/test-notification`, method: 'POST', signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getTestNotificationMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof testNotification>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof testNotification>>, TError,void, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['testNotification'];
|
|
||||||
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<Awaited<ReturnType<typeof testNotification>>, void> = () => {
|
|
||||||
|
|
||||||
|
|
||||||
return testNotification(requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type TestNotificationMutationResult = NonNullable<Awaited<ReturnType<typeof testNotification>>>
|
|
||||||
|
|
||||||
export type TestNotificationMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Test notification
|
|
||||||
*/
|
|
||||||
export const useTestNotification = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof testNotification>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof testNotification>>,
|
|
||||||
TError,
|
|
||||||
void,
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getTestNotificationMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Triggers the cleanup of untracked S3 images
|
|
||||||
* @summary Cleanup unused S3 images
|
|
||||||
*/
|
|
||||||
export const imageCleanup = (
|
|
||||||
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/management/image-cleanup`, method: 'POST', signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getImageCleanupMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof imageCleanup>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof imageCleanup>>, TError,void, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['imageCleanup'];
|
|
||||||
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<Awaited<ReturnType<typeof imageCleanup>>, void> = () => {
|
|
||||||
|
|
||||||
|
|
||||||
return imageCleanup(requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type ImageCleanupMutationResult = NonNullable<Awaited<ReturnType<typeof imageCleanup>>>
|
|
||||||
|
|
||||||
export type ImageCleanupMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Cleanup unused S3 images
|
|
||||||
*/
|
|
||||||
export const useImageCleanup = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof imageCleanup>>, TError,void, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof imageCleanup>>,
|
|
||||||
TError,
|
|
||||||
void,
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getImageCleanupMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
@ -15,10 +15,10 @@ import type {
|
|||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
DefaultResponseDTOImportMangaResponseDTO,
|
DefaultResponseDTOImportMangaDexResponseDTO,
|
||||||
DefaultResponseDTOVoid,
|
DefaultResponseDTOVoid,
|
||||||
ImportMultipleFilesBody,
|
ImportMangaDexRequestDTO,
|
||||||
ImportRequestDTO
|
ImportMultipleFilesBody
|
||||||
} from '../api.schemas';
|
} from '../api.schemas';
|
||||||
|
|
||||||
import { customInstance } from '../../api';
|
import { customInstance } from '../../api';
|
||||||
@ -97,31 +97,30 @@ export const useImportMultipleFiles = <TError = unknown,
|
|||||||
return useMutation(mutationOptions, queryClient);
|
return useMutation(mutationOptions, queryClient);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Imports manga data from content provider into the local database.
|
* Imports manga data from MangaDex into the local database.
|
||||||
* @summary Import manga from content provider
|
* @summary Import manga from MangaDex
|
||||||
*/
|
*/
|
||||||
export const importFromProvider = (
|
export const importFromMangaDex = (
|
||||||
providerId: number,
|
importMangaDexRequestDTO: ImportMangaDexRequestDTO,
|
||||||
importRequestDTO: ImportRequestDTO,
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOImportMangaResponseDTO>(
|
return customInstance<DefaultResponseDTOImportMangaDexResponseDTO>(
|
||||||
{url: `/manga/import/provider/${encodeURIComponent(String(providerId))}`, method: 'POST',
|
{url: `/manga/import/manga-dex`, method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json', },
|
headers: {'Content-Type': 'application/json', },
|
||||||
data: importRequestDTO, signal
|
data: importMangaDexRequestDTO, signal
|
||||||
},
|
},
|
||||||
options);
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getImportFromProviderMutationOptions = <TError = unknown,
|
export const getImportFromMangaDexMutationOptions = <TError = unknown,
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof importFromProvider>>, TError,{providerId: number;data: ImportRequestDTO}, TContext>, request?: SecondParameter<typeof customInstance>}
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof importFromMangaDex>>, TError,{data: ImportMangaDexRequestDTO}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof importFromProvider>>, TError,{providerId: number;data: ImportRequestDTO}, TContext> => {
|
): UseMutationOptions<Awaited<ReturnType<typeof importFromMangaDex>>, TError,{data: ImportMangaDexRequestDTO}, TContext> => {
|
||||||
|
|
||||||
const mutationKey = ['importFromProvider'];
|
const mutationKey = ['importFromMangaDex'];
|
||||||
const {mutation: mutationOptions, request: requestOptions} = options ?
|
const {mutation: mutationOptions, request: requestOptions} = options ?
|
||||||
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
|
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
|
||||||
options
|
options
|
||||||
@ -131,10 +130,10 @@ const {mutation: mutationOptions, request: requestOptions} = options ?
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mutationFn: MutationFunction<Awaited<ReturnType<typeof importFromProvider>>, {providerId: number;data: ImportRequestDTO}> = (props) => {
|
const mutationFn: MutationFunction<Awaited<ReturnType<typeof importFromMangaDex>>, {data: ImportMangaDexRequestDTO}> = (props) => {
|
||||||
const {providerId,data} = props ?? {};
|
const {data} = props ?? {};
|
||||||
|
|
||||||
return importFromProvider(providerId,data,requestOptions)
|
return importFromMangaDex(data,requestOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -142,23 +141,23 @@ const {mutation: mutationOptions, request: requestOptions} = options ?
|
|||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
return { mutationFn, ...mutationOptions }}
|
||||||
|
|
||||||
export type ImportFromProviderMutationResult = NonNullable<Awaited<ReturnType<typeof importFromProvider>>>
|
export type ImportFromMangaDexMutationResult = NonNullable<Awaited<ReturnType<typeof importFromMangaDex>>>
|
||||||
export type ImportFromProviderMutationBody = ImportRequestDTO
|
export type ImportFromMangaDexMutationBody = ImportMangaDexRequestDTO
|
||||||
export type ImportFromProviderMutationError = unknown
|
export type ImportFromMangaDexMutationError = unknown
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Import manga from content provider
|
* @summary Import manga from MangaDex
|
||||||
*/
|
*/
|
||||||
export const useImportFromProvider = <TError = unknown,
|
export const useImportFromMangaDex = <TError = unknown,
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof importFromProvider>>, TError,{providerId: number;data: ImportRequestDTO}, TContext>, request?: SecondParameter<typeof customInstance>}
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof importFromMangaDex>>, TError,{data: ImportMangaDexRequestDTO}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
, queryClient?: QueryClient): UseMutationResult<
|
||||||
Awaited<ReturnType<typeof importFromProvider>>,
|
Awaited<ReturnType<typeof importFromMangaDex>>,
|
||||||
TError,
|
TError,
|
||||||
{providerId: number;data: ImportRequestDTO},
|
{data: ImportMangaDexRequestDTO},
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
|
|
||||||
const mutationOptions = getImportFromProviderMutationOptions(options);
|
const mutationOptions = getImportFromMangaDexMutationOptions(options);
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
return useMutation(mutationOptions, queryClient);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Generated by orval v7.17.0 🍺
|
* Generated by orval v7.15.0 🍺
|
||||||
* Do not edit manually.
|
* Do not edit manually.
|
||||||
* OpenAPI definition
|
* OpenAPI definition
|
||||||
* OpenAPI spec version: v0
|
* OpenAPI spec version: v0
|
||||||
@ -102,132 +102,6 @@ export const useFetchMangaChapters = <TError = unknown,
|
|||||||
return useMutation(mutationOptions, queryClient);
|
return useMutation(mutationOptions, queryClient);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Unfollow the manga specified by its ID.
|
|
||||||
* @summary Unfollow the manga specified by its ID
|
|
||||||
*/
|
|
||||||
export const unfollowManga = (
|
|
||||||
mangaId: number,
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/mangas/${encodeURIComponent(String(mangaId))}/unfollowManga`, method: 'POST', signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getUnfollowMangaMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof unfollowManga>>, TError,{mangaId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof unfollowManga>>, TError,{mangaId: number}, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['unfollowManga'];
|
|
||||||
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<Awaited<ReturnType<typeof unfollowManga>>, {mangaId: number}> = (props) => {
|
|
||||||
const {mangaId} = props ?? {};
|
|
||||||
|
|
||||||
return unfollowManga(mangaId,requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type UnfollowMangaMutationResult = NonNullable<Awaited<ReturnType<typeof unfollowManga>>>
|
|
||||||
|
|
||||||
export type UnfollowMangaMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Unfollow the manga specified by its ID
|
|
||||||
*/
|
|
||||||
export const useUnfollowManga = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof unfollowManga>>, TError,{mangaId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof unfollowManga>>,
|
|
||||||
TError,
|
|
||||||
{mangaId: number},
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getUnfollowMangaMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Follow the manga specified by its ID.
|
|
||||||
* @summary Follow the manga specified by its ID
|
|
||||||
*/
|
|
||||||
export const followManga = (
|
|
||||||
mangaId: number,
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOVoid>(
|
|
||||||
{url: `/mangas/${encodeURIComponent(String(mangaId))}/followManga`, method: 'POST', signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getFollowMangaMutationOptions = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof followManga>>, TError,{mangaId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
): UseMutationOptions<Awaited<ReturnType<typeof followManga>>, TError,{mangaId: number}, TContext> => {
|
|
||||||
|
|
||||||
const mutationKey = ['followManga'];
|
|
||||||
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<Awaited<ReturnType<typeof followManga>>, {mangaId: number}> = (props) => {
|
|
||||||
const {mangaId} = props ?? {};
|
|
||||||
|
|
||||||
return followManga(mangaId,requestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions }}
|
|
||||||
|
|
||||||
export type FollowMangaMutationResult = NonNullable<Awaited<ReturnType<typeof followManga>>>
|
|
||||||
|
|
||||||
export type FollowMangaMutationError = unknown
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Follow the manga specified by its ID
|
|
||||||
*/
|
|
||||||
export const useFollowManga = <TError = unknown,
|
|
||||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof followManga>>, TError,{mangaId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient): UseMutationResult<
|
|
||||||
Awaited<ReturnType<typeof followManga>>,
|
|
||||||
TError,
|
|
||||||
{mangaId: number},
|
|
||||||
TContext
|
|
||||||
> => {
|
|
||||||
|
|
||||||
const mutationOptions = getFollowMangaMutationOptions(options);
|
|
||||||
|
|
||||||
return useMutation(mutationOptions, queryClient);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Retrieve a list of mangas with their details.
|
* Retrieve a list of mangas with their details.
|
||||||
* @summary Get a list of mangas
|
* @summary Get a list of mangas
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,127 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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<T extends (...args: never) => unknown> = Parameters<T>[1];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a list of content providers
|
|
||||||
* @summary Get a list of providers
|
|
||||||
*/
|
|
||||||
export const getProviders = (
|
|
||||||
params?: GetProvidersParams,
|
|
||||||
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
|
|
||||||
|
|
||||||
return customInstance<DefaultResponseDTOProviderListDTO>(
|
|
||||||
{url: `/providers`, method: 'GET',
|
|
||||||
params, signal
|
|
||||||
},
|
|
||||||
options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getGetProvidersQueryKey = (params?: GetProvidersParams,) => {
|
|
||||||
return [
|
|
||||||
`/providers`, ...(params ? [params]: [])
|
|
||||||
] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const getGetProvidersQueryOptions = <TData = Awaited<ReturnType<typeof getProviders>>, TError = unknown>(params?: GetProvidersParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof getProviders>>, TError, TData>>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
) => {
|
|
||||||
|
|
||||||
const {query: queryOptions, request: requestOptions} = options ?? {};
|
|
||||||
|
|
||||||
const queryKey = queryOptions?.queryKey ?? getGetProvidersQueryKey(params);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getProviders>>> = ({ signal }) => getProviders(params, requestOptions, signal);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { queryKey, queryFn, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof getProviders>>, TError, TData> & { queryKey: DataTag<QueryKey, TData, TError> }
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GetProvidersQueryResult = NonNullable<Awaited<ReturnType<typeof getProviders>>>
|
|
||||||
export type GetProvidersQueryError = unknown
|
|
||||||
|
|
||||||
|
|
||||||
export function useGetProviders<TData = Awaited<ReturnType<typeof getProviders>>, TError = unknown>(
|
|
||||||
params: undefined | GetProvidersParams, options: { query:Partial<UseQueryOptions<Awaited<ReturnType<typeof getProviders>>, TError, TData>> & Pick<
|
|
||||||
DefinedInitialDataOptions<
|
|
||||||
Awaited<ReturnType<typeof getProviders>>,
|
|
||||||
TError,
|
|
||||||
Awaited<ReturnType<typeof getProviders>>
|
|
||||||
> , 'initialData'
|
|
||||||
>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient
|
|
||||||
): DefinedUseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> }
|
|
||||||
export function useGetProviders<TData = Awaited<ReturnType<typeof getProviders>>, TError = unknown>(
|
|
||||||
params?: GetProvidersParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof getProviders>>, TError, TData>> & Pick<
|
|
||||||
UndefinedInitialDataOptions<
|
|
||||||
Awaited<ReturnType<typeof getProviders>>,
|
|
||||||
TError,
|
|
||||||
Awaited<ReturnType<typeof getProviders>>
|
|
||||||
> , 'initialData'
|
|
||||||
>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> }
|
|
||||||
export function useGetProviders<TData = Awaited<ReturnType<typeof getProviders>>, TError = unknown>(
|
|
||||||
params?: GetProvidersParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof getProviders>>, TError, TData>>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> }
|
|
||||||
/**
|
|
||||||
* @summary Get a list of providers
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function useGetProviders<TData = Awaited<ReturnType<typeof getProviders>>, TError = unknown>(
|
|
||||||
params?: GetProvidersParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof getProviders>>, TError, TData>>, request?: SecondParameter<typeof customInstance>}
|
|
||||||
, queryClient?: QueryClient
|
|
||||||
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> } {
|
|
||||||
|
|
||||||
const queryOptions = getGetProvidersQueryOptions(params,options)
|
|
||||||
|
|
||||||
const query = useQuery(queryOptions, queryClient) as UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
|
|
||||||
|
|
||||||
query.queryKey = queryOptions.queryKey ;
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,188 +0,0 @@
|
|||||||
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<typeof SelectPrimitive.Root>) {
|
|
||||||
return <SelectPrimitive.Root data-slot="select" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectGroup({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
|
||||||
return <SelectPrimitive.Group data-slot="select-group" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectValue({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
|
||||||
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectTrigger({
|
|
||||||
className,
|
|
||||||
size = "default",
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
||||||
size?: "sm" | "default";
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.Trigger
|
|
||||||
data-slot="select-trigger"
|
|
||||||
data-size={size}
|
|
||||||
className={cn(
|
|
||||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<SelectPrimitive.Icon asChild>
|
|
||||||
<ChevronDownIcon className="size-4 opacity-50" />
|
|
||||||
</SelectPrimitive.Icon>
|
|
||||||
</SelectPrimitive.Trigger>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectContent({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
position = "item-aligned",
|
|
||||||
align = "center",
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.Portal>
|
|
||||||
<SelectPrimitive.Content
|
|
||||||
data-slot="select-content"
|
|
||||||
className={cn(
|
|
||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
||||||
position === "popper" &&
|
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
position={position}
|
|
||||||
align={align}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<SelectScrollUpButton />
|
|
||||||
<SelectPrimitive.Viewport
|
|
||||||
className={cn(
|
|
||||||
"p-1",
|
|
||||||
position === "popper" &&
|
|
||||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</SelectPrimitive.Viewport>
|
|
||||||
<SelectScrollDownButton />
|
|
||||||
</SelectPrimitive.Content>
|
|
||||||
</SelectPrimitive.Portal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectLabel({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.Label
|
|
||||||
data-slot="select-label"
|
|
||||||
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectItem({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.Item
|
|
||||||
data-slot="select-item"
|
|
||||||
className={cn(
|
|
||||||
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
data-slot="select-item-indicator"
|
|
||||||
className="absolute right-2 flex size-3.5 items-center justify-center"
|
|
||||||
>
|
|
||||||
<SelectPrimitive.ItemIndicator>
|
|
||||||
<CheckIcon className="size-4" />
|
|
||||||
</SelectPrimitive.ItemIndicator>
|
|
||||||
</span>
|
|
||||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
||||||
</SelectPrimitive.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectSeparator({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.Separator
|
|
||||||
data-slot="select-separator"
|
|
||||||
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectScrollUpButton({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.ScrollUpButton
|
|
||||||
data-slot="select-scroll-up-button"
|
|
||||||
className={cn(
|
|
||||||
"flex cursor-default items-center justify-center py-1",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<ChevronUpIcon className="size-4" />
|
|
||||||
</SelectPrimitive.ScrollUpButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectScrollDownButton({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
|
||||||
return (
|
|
||||||
<SelectPrimitive.ScrollDownButton
|
|
||||||
data-slot="select-scroll-down-button"
|
|
||||||
className={cn(
|
|
||||||
"flex cursor-default items-center justify-center py-1",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<ChevronDownIcon className="size-4" />
|
|
||||||
</SelectPrimitive.ScrollDownButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectGroup,
|
|
||||||
SelectItem,
|
|
||||||
SelectLabel,
|
|
||||||
SelectScrollDownButton,
|
|
||||||
SelectScrollUpButton,
|
|
||||||
SelectSeparator,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
};
|
|
||||||
@ -10,13 +10,13 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { useAuth } from "@/contexts/AuthContext.tsx";
|
import { useAuth } from "@/contexts/AuthContext.tsx";
|
||||||
|
import { MangaDexImportDialog } from "@/features/home/components/MangaDexImportDialog.tsx";
|
||||||
import { MangaManualImportDialog } from "@/features/home/components/MangaManualImportDialog.tsx";
|
import { MangaManualImportDialog } from "@/features/home/components/MangaManualImportDialog.tsx";
|
||||||
import { ProviderImportDialog } from "@/features/home/components/ProviderImportDialog.tsx";
|
|
||||||
|
|
||||||
export function ImportDropdown() {
|
export function ImportDropdown() {
|
||||||
const { isAuthenticated } = useAuth();
|
const { isAuthenticated } = useAuth();
|
||||||
|
|
||||||
const [providerDialogOpen, setProviderDialogOpen] = useState(false);
|
const [mangaDexDialogOpen, setMangaDexDialogOpen] = useState(false);
|
||||||
const [fileImportDialogOpen, setFileImportDialogOpen] = useState(false);
|
const [fileImportDialogOpen, setFileImportDialogOpen] = useState(false);
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
@ -33,9 +33,9 @@ export function ImportDropdown() {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="start" className="">
|
<DropdownMenuContent align="start" className="">
|
||||||
<DropdownMenuItem onClick={() => setProviderDialogOpen(true)}>
|
<DropdownMenuItem onClick={() => setMangaDexDialogOpen(true)}>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
<Download className="mr-2 h-4 w-4" />
|
||||||
Import from Provider
|
Import from MangaDex
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setFileImportDialogOpen(true)}>
|
<DropdownMenuItem onClick={() => setFileImportDialogOpen(true)}>
|
||||||
<FileUp className="mr-2 h-4 w-4" />
|
<FileUp className="mr-2 h-4 w-4" />
|
||||||
@ -55,9 +55,9 @@ export function ImportDropdown() {
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
<ProviderImportDialog
|
<MangaDexImportDialog
|
||||||
dialogOpen={providerDialogOpen}
|
mangaDexDialogOpen={mangaDexDialogOpen}
|
||||||
onDialogOpenChange={setProviderDialogOpen}
|
onMangaDexDialogOpenChange={setMangaDexDialogOpen}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MangaManualImportDialog
|
<MangaManualImportDialog
|
||||||
|
|||||||
134
src/features/home/components/MangaDexImportDialog.tsx
Normal file
134
src/features/home/components/MangaDexImportDialog.tsx
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
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 { useImportFromMangaDex } 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 MangaDexImportDialogProps {
|
||||||
|
mangaDexDialogOpen: boolean;
|
||||||
|
onMangaDexDialogOpenChange: (open: boolean) => 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<z.infer<typeof formSchema>>({
|
||||||
|
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<typeof formSchema>) => {
|
||||||
|
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 (
|
||||||
|
<Dialog open={mangaDexDialogOpen} onOpenChange={onMangaDexDialogOpenChange}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Import from MangaDex</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Enter a MangaDex manga URL or ID to import it to your library.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
id="importForm"
|
||||||
|
onSubmit={form.handleSubmit(handleSubmit)}
|
||||||
|
className="space-y-4"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="value"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>MangaDex URL or ID</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="e.g., https://mangadex.org/title/..."
|
||||||
|
disabled={isPendingImportMangaDex}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => onMangaDexDialogOpenChange(false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={isPendingImportMangaDex}
|
||||||
|
form="importForm"
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,190 +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 { 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<z.infer<typeof formSchema>>({
|
|
||||||
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<typeof formSchema>) => {
|
|
||||||
importFromProvider({
|
|
||||||
providerId: Number(data.providerId),
|
|
||||||
data: { id: data.value, metadataId: data.myAnimeListId },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[importFromProvider],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={dialogOpen} onOpenChange={onDialogOpenChange}>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Import from Provider</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Enter a Provider manga URL or ID to import it to your library.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
id="importForm"
|
|
||||||
onSubmit={form.handleSubmit(handleSubmit)}
|
|
||||||
className="space-y-4"
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="providerId"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Provider</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
|
||||||
disabled={isFetchingProviders}
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-[180px]">
|
|
||||||
<SelectValue placeholder="Select a provider" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{providerData?.data?.providers?.map((provider) => (
|
|
||||||
<SelectItem
|
|
||||||
key={provider.id}
|
|
||||||
value={provider.id.toString()}
|
|
||||||
>
|
|
||||||
{provider.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="value"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>URL or ID</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="e.g., https://mangadex.org/title/..."
|
|
||||||
disabled={isPendingImportFromProvider}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="myAnimeListId"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>MyAnimeList ID (Optional)</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="e.g., 13 (for One Piece)"
|
|
||||||
disabled={isPendingImportFromProvider}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
|
||||||
Optionally link this manga to a MyAnimeList entry for better
|
|
||||||
precision on metadata fetching.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => onDialogOpenChange(false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
disabled={isPendingImportFromProvider || isFetchingProviders}
|
|
||||||
form="importForm"
|
|
||||||
>
|
|
||||||
Import
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { ArrowLeft, ChevronLeft, ChevronRight, Home } from "lucide-react";
|
import { ArrowLeft, ChevronLeft, ChevronRight, Home } from "lucide-react";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router";
|
import { useNavigate, useParams } from "react-router";
|
||||||
import {
|
import {
|
||||||
useGetMangaChapterImages,
|
useGetMangaChapterImages,
|
||||||
@ -21,95 +21,34 @@ const Chapter = () => {
|
|||||||
getCurrentChapterPage(chapterNumber) ?? 1,
|
getCurrentChapterPage(chapterNumber) ?? 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [infiniteScroll, setInfiniteScroll] = useState(true);
|
|
||||||
|
|
||||||
const { data, isLoading } = useGetMangaChapterImages(chapterNumber);
|
const { data, isLoading } = useGetMangaChapterImages(chapterNumber);
|
||||||
|
|
||||||
const { mutate } = useMarkAsRead();
|
const { mutate } = useMarkAsRead();
|
||||||
|
|
||||||
// For infinite scroll mode
|
|
||||||
const [visibleCount, setVisibleCount] = useState(1);
|
|
||||||
const loadMoreRef = useRef(null);
|
|
||||||
|
|
||||||
/** Mark chapter as read when last page reached */
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data || isLoading) return;
|
if (!data || isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentPage === data.data?.chapterImageKeys.length) {
|
if (currentPage === data.data?.chapterImageKeys.length) {
|
||||||
mutate({ chapterId: chapterNumber });
|
mutate({ chapterId: chapterNumber });
|
||||||
}
|
}
|
||||||
}, [data, mutate, currentPage]);
|
}, [data, mutate, currentPage]);
|
||||||
|
|
||||||
/** Persist reading progress */
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentChapterPage(chapterNumber, currentPage);
|
setCurrentChapterPage(chapterNumber, currentPage);
|
||||||
}, [chapterNumber, currentPage]);
|
}, [chapterNumber, currentPage]);
|
||||||
|
|
||||||
/** Restore stored page */
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading && data?.data) {
|
if (!isLoading && !data?.data) {
|
||||||
const stored = getCurrentChapterPage(chapterNumber);
|
return;
|
||||||
if (stored) {
|
|
||||||
setCurrentPage(stored);
|
|
||||||
setVisibleCount(stored); // for infinite scroll
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [isLoading, data?.data]);
|
|
||||||
|
|
||||||
/** Infinite scroll observer */
|
const storedChapterPage = getCurrentChapterPage(chapterNumber);
|
||||||
useEffect(() => {
|
if (storedChapterPage) {
|
||||||
if (!infiniteScroll) return;
|
setCurrentPage(storedChapterPage);
|
||||||
if (!loadMoreRef.current) return;
|
|
||||||
|
|
||||||
const obs = new IntersectionObserver((entries) => {
|
|
||||||
if (entries[0].isIntersecting) {
|
|
||||||
setVisibleCount((count) =>
|
|
||||||
Math.min(count + 2, data?.data?.chapterImageKeys.length ?? 0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
obs.observe(loadMoreRef.current);
|
|
||||||
return () => obs.disconnect();
|
|
||||||
}, [infiniteScroll, data?.data]);
|
|
||||||
|
|
||||||
/** Track which image is currently visible (for progress update) */
|
|
||||||
useEffect(() => {
|
|
||||||
if (!infiniteScroll) return;
|
|
||||||
|
|
||||||
const imgs = document.querySelectorAll("[data-page]");
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
entries.forEach((entry) => {
|
|
||||||
const el = entry.target as HTMLElement; // <-- FIX
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
const pageNum = Number(el.dataset.page); // <-- SAFE
|
|
||||||
setCurrentPage(pageNum);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{ threshold: 0.5 },
|
|
||||||
);
|
|
||||||
|
|
||||||
imgs.forEach((img) => observer.observe(img));
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, [infiniteScroll, visibleCount]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!data?.data) return;
|
|
||||||
|
|
||||||
// When switching modes:
|
|
||||||
if (infiniteScroll) {
|
|
||||||
// Scroll mode → show saved progress
|
|
||||||
setVisibleCount(currentPage);
|
|
||||||
setTimeout(() => {
|
|
||||||
const el = document.querySelector(`[data-page="${currentPage}"]`);
|
|
||||||
el?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
||||||
}, 50);
|
|
||||||
} else {
|
|
||||||
// Single page mode → scroll to top
|
|
||||||
window.scrollTo({ top: 0 });
|
|
||||||
}
|
}
|
||||||
}, [infiniteScroll]);
|
}, [getCurrentChapterPage, isLoading, data?.data]);
|
||||||
|
|
||||||
if (!data?.data) {
|
if (!data?.data) {
|
||||||
return (
|
return (
|
||||||
@ -126,25 +65,27 @@ const Chapter = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const images = data.data.chapterImageKeys;
|
|
||||||
|
|
||||||
/** Standard navigation (non-infinite mode) */
|
|
||||||
const goToNextPage = () => {
|
const goToNextPage = () => {
|
||||||
if (currentPage < images.length) {
|
if (!data?.data) {
|
||||||
setCurrentPage((p) => p + 1);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPage < data.data.chapterImageKeys.length) {
|
||||||
|
setCurrentPage(currentPage + 1);
|
||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const goToPreviousPage = () => {
|
const goToPreviousPage = () => {
|
||||||
if (currentPage > 1) {
|
if (currentPage > 1) {
|
||||||
setCurrentPage((p) => p - 1);
|
setCurrentPage(currentPage - 1);
|
||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
{/* HEADER */}
|
{/* Header */}
|
||||||
<header className="sticky top-0 z-50 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
<header className="sticky top-0 z-50 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||||
<div className="px-4 py-4 sm:px-8">
|
<div className="px-4 py-4 sm:px-8">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@ -158,7 +99,6 @@ const Chapter = () => {
|
|||||||
<ArrowLeft className="h-4 w-4" />
|
<ArrowLeft className="h-4 w-4" />
|
||||||
<span className="hidden sm:inline">Back</span>
|
<span className="hidden sm:inline">Back</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -168,7 +108,6 @@ const Chapter = () => {
|
|||||||
<Home className="h-4 w-4" />
|
<Home className="h-4 w-4" />
|
||||||
<span className="hidden sm:inline">Home</span>
|
<span className="hidden sm:inline">Home</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="hidden sm:block">
|
<div className="hidden sm:block">
|
||||||
<h1 className="text-sm font-semibold text-foreground">
|
<h1 className="text-sm font-semibold text-foreground">
|
||||||
{data.data.mangaTitle}
|
{data.data.mangaTitle}
|
||||||
@ -178,27 +117,19 @@ const Chapter = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
Page {currentPage} / {images.length}
|
Page {currentPage} / {data.data.chapterImageKeys.length}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setInfiniteScroll((v) => !v)}
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
{infiniteScroll ? "Single Page Mode" : "Scroll Mode"}
|
|
||||||
</Button>
|
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* MAIN */}
|
{/* Reader Content */}
|
||||||
<main className="mx-auto max-w-4xl px-4 py-8">
|
<main className="mx-auto max-w-4xl px-4 py-8">
|
||||||
|
{/* Mobile title */}
|
||||||
<div className="mb-4 sm:hidden">
|
<div className="mb-4 sm:hidden">
|
||||||
<h1 className="text-lg font-semibold text-foreground">
|
<h1 className="text-lg font-semibold text-foreground">
|
||||||
{data.data.mangaTitle}
|
{data.data.mangaTitle}
|
||||||
@ -208,74 +139,74 @@ const Chapter = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ------------------------------------------------------------------ */}
|
{/* Manga Page */}
|
||||||
{/* MODE 1 --- INFINITE SCROLL MODE */}
|
<div className="relative mx-auto mb-8 overflow-hidden rounded-lg border border-border bg-muted">
|
||||||
{/* ------------------------------------------------------------------ */}
|
<img
|
||||||
{infiniteScroll ? (
|
src={
|
||||||
<div className="flex flex-col space-y-0">
|
import.meta.env.VITE_OMV_BASE_URL +
|
||||||
{images.slice(0, visibleCount).map((key, idx) => (
|
"/" +
|
||||||
<img
|
data.data.chapterImageKeys[currentPage - 1] ||
|
||||||
key={idx}
|
"/placeholder.svg"
|
||||||
data-page={idx + 1}
|
}
|
||||||
src={`${import.meta.env.VITE_OMV_BASE_URL}/${key}`}
|
alt={`Page ${currentPage}`}
|
||||||
className="w-full h-auto block"
|
width={1000}
|
||||||
alt={`Page ${idx + 1}`}
|
height={1400}
|
||||||
loading="lazy"
|
className="h-auto w-full"
|
||||||
/>
|
// priority
|
||||||
))}
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* LOAD MORE SENTINEL */}
|
{/* Navigation Controls */}
|
||||||
<div ref={loadMoreRef} className="h-10" />
|
<div className="space-y-4">
|
||||||
|
{/* Page Navigation */}
|
||||||
|
<div className="flex items-center justify-center gap-4">
|
||||||
|
<Button
|
||||||
|
onClick={goToPreviousPage}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
variant="outline"
|
||||||
|
className="gap-2 bg-transparent"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
Previous Page
|
||||||
|
</Button>
|
||||||
|
<span className="text-sm font-medium text-foreground">
|
||||||
|
{currentPage} / {data.data.chapterImageKeys.length}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
onClick={goToNextPage}
|
||||||
|
disabled={currentPage === data.data.chapterImageKeys.length}
|
||||||
|
variant="outline"
|
||||||
|
className="gap-2 bg-transparent"
|
||||||
|
>
|
||||||
|
Next Page
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
/* MODE 2 --- STANDARD SINGLE-PAGE MODE */
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
<>
|
|
||||||
<div className="relative mx-auto mb-8 overflow-hidden rounded-lg border border-border bg-muted">
|
|
||||||
<img
|
|
||||||
src={
|
|
||||||
import.meta.env.VITE_OMV_BASE_URL +
|
|
||||||
"/" +
|
|
||||||
images[currentPage - 1] || "/placeholder.svg"
|
|
||||||
}
|
|
||||||
alt={`Page ${currentPage}`}
|
|
||||||
width={1000}
|
|
||||||
height={1400}
|
|
||||||
className="h-auto w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* NAVIGATION BUTTONS */}
|
{/*/!* Chapter Navigation *!/*/}
|
||||||
<div className="space-y-4">
|
{/*{(currentPage === data.chapterImageKeys.length || currentPage === 1) && (*/}
|
||||||
<div className="flex items-center justify-center gap-4">
|
{/* <div className="flex items-center justify-center gap-4 border-t border-border pt-4">*/}
|
||||||
<Button
|
{/* <Button*/}
|
||||||
onClick={goToPreviousPage}
|
{/* onClick={goToPreviousChapter}*/}
|
||||||
disabled={currentPage === 1}
|
{/* disabled={chapterNumber === 1}*/}
|
||||||
variant="outline"
|
{/* variant="secondary"*/}
|
||||||
className="gap-2 bg-transparent"
|
{/* className="gap-2"*/}
|
||||||
>
|
{/* >*/}
|
||||||
<ChevronLeft className="h-4 w-4" />
|
{/* <ChevronLeft className="h-4 w-4" />*/}
|
||||||
Previous Page
|
{/* Previous Chapter*/}
|
||||||
</Button>
|
{/* </Button>*/}
|
||||||
|
{/* <Button*/}
|
||||||
<span className="text-sm font-medium text-foreground">
|
{/* onClick={goToNextChapter}*/}
|
||||||
{currentPage} / {images.length}
|
{/* disabled={chapterNumber === manga.chapters}*/}
|
||||||
</span>
|
{/* variant="secondary"*/}
|
||||||
|
{/* className="gap-2"*/}
|
||||||
<Button
|
{/* >*/}
|
||||||
onClick={goToNextPage}
|
{/* Next Chapter*/}
|
||||||
disabled={currentPage === images.length}
|
{/* <ChevronRight className="h-4 w-4" />*/}
|
||||||
variant="outline"
|
{/* </Button>*/}
|
||||||
className="gap-2 bg-transparent"
|
{/* </div>*/}
|
||||||
>
|
{/*)}*/}
|
||||||
Next Page
|
</div>
|
||||||
<ChevronRight className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,27 +1,18 @@
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
Bell,
|
|
||||||
BellOff,
|
|
||||||
BookOpen,
|
BookOpen,
|
||||||
Calendar,
|
Calendar,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Database,
|
Database,
|
||||||
Heart,
|
|
||||||
Star,
|
Star,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useCallback, useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router";
|
import { useNavigate, useParams } from "react-router";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
|
||||||
useSetFavorite,
|
|
||||||
useSetUnfavorite,
|
|
||||||
} from "@/api/generated/favorite-mangas/favorite-mangas.ts";
|
|
||||||
import {
|
import {
|
||||||
useFetchMangaChapters,
|
useFetchMangaChapters,
|
||||||
useFollowManga,
|
|
||||||
useGetManga,
|
useGetManga,
|
||||||
useUnfollowManga,
|
|
||||||
} from "@/api/generated/manga/manga.ts";
|
} from "@/api/generated/manga/manga.ts";
|
||||||
import { useFetchAllChapters } from "@/api/generated/manga-chapter/manga-chapter.ts";
|
import { useFetchAllChapters } from "@/api/generated/manga-chapter/manga-chapter.ts";
|
||||||
import { ThemeToggle } from "@/components/ThemeToggle.tsx";
|
import { ThemeToggle } from "@/components/ThemeToggle.tsx";
|
||||||
@ -33,12 +24,10 @@ import {
|
|||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible.tsx";
|
} from "@/components/ui/collapsible.tsx";
|
||||||
import { useAuth } from "@/contexts/AuthContext.tsx";
|
|
||||||
import { MangaChapter } from "@/features/manga/MangaChapter.tsx";
|
import { MangaChapter } from "@/features/manga/MangaChapter.tsx";
|
||||||
import { formatToTwoDigitsDateRange } from "@/utils/dateFormatter.ts";
|
import { formatToTwoDigitsDateRange } from "@/utils/dateFormatter.ts";
|
||||||
|
|
||||||
const Manga = () => {
|
const Manga = () => {
|
||||||
const { isAuthenticated } = useAuth();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const mangaId = Number(params.mangaId);
|
const mangaId = Number(params.mangaId);
|
||||||
@ -64,63 +53,6 @@ const Manga = () => {
|
|||||||
|
|
||||||
const [openProviders, setOpenProviders] = useState<Set<number>>(new Set());
|
const [openProviders, setOpenProviders] = useState<Set<number>>(new Set());
|
||||||
|
|
||||||
const { mutate: mutateFavorite, isPending: isPendingFavorite } =
|
|
||||||
useSetFavorite({
|
|
||||||
mutation: {
|
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutate: mutateUnfavorite, isPending: isPendingUnfavorite } =
|
|
||||||
useSetUnfavorite({
|
|
||||||
mutation: {
|
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const isPendingFavoriteChange = isPendingFavorite || isPendingUnfavorite;
|
|
||||||
|
|
||||||
const handleFavoriteClick = useCallback(
|
|
||||||
(isFavorite: boolean) =>
|
|
||||||
isFavorite
|
|
||||||
? mutateUnfavorite({ id: mangaData?.data?.id ?? -1 })
|
|
||||||
: mutateFavorite({ id: mangaData?.data?.id ?? -1 }),
|
|
||||||
[mutateUnfavorite, mutateFavorite, mangaData?.data?.id],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { mutate: mutateFollow, isPending: isPendingFollow } = useFollowManga({
|
|
||||||
mutation: {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey });
|
|
||||||
toast.success(
|
|
||||||
"We will notify you when new content if available for this manga.",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutate: mutateUnfollow, isPending: isPendingUnfollow } =
|
|
||||||
useUnfollowManga({
|
|
||||||
mutation: {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey });
|
|
||||||
toast.success(
|
|
||||||
"You will no longer received notifications for this manga.",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const isPendingFollowChange = isPendingFollow || isPendingUnfollow;
|
|
||||||
|
|
||||||
const handleFollowClick = useCallback(
|
|
||||||
(isFollowing: boolean) =>
|
|
||||||
isFollowing
|
|
||||||
? mutateUnfollow({ mangaId: mangaData?.data?.id ?? -1 })
|
|
||||||
: mutateFollow({ mangaId: mangaData?.data?.id ?? -1 }),
|
|
||||||
[mangaData?.data?.id, mutateUnfollow, mutateFollow],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!mangaData) {
|
if (!mangaData) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background">
|
<div className="flex min-h-screen items-center justify-center bg-background">
|
||||||
@ -193,57 +125,16 @@ const Manga = () => {
|
|||||||
{/* Details */}
|
{/* Details */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-3 flex items-center justify-between gap-4">
|
<div className="mb-3 flex items-start justify-between gap-4">
|
||||||
<h1 className="text-balance text-4xl font-bold tracking-tight text-foreground">
|
<h1 className="text-balance text-4xl font-bold tracking-tight text-foreground">
|
||||||
{mangaData.data?.title}
|
{mangaData.data?.title}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="flex gap-4 items-center">
|
<Badge
|
||||||
<Badge
|
variant="secondary"
|
||||||
variant="secondary"
|
className="border border-border bg-card text-foreground"
|
||||||
className="border border-border bg-card text-foreground max-h-6"
|
>
|
||||||
>
|
{mangaData.data?.status}
|
||||||
{mangaData.data?.status}
|
</Badge>
|
||||||
</Badge>
|
|
||||||
|
|
||||||
{isAuthenticated && (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="outline"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
handleFollowClick(
|
|
||||||
mangaData?.data?.following || false,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
disabled={isPendingFollowChange}
|
|
||||||
>
|
|
||||||
{mangaData?.data?.following ? <BellOff /> : <Bell />}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="outline"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
handleFavoriteClick(
|
|
||||||
mangaData?.data?.favorite || false,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
disabled={isPendingFavoriteChange}
|
|
||||||
>
|
|
||||||
<Heart
|
|
||||||
className={`h-4 w-4 transition-colors ${
|
|
||||||
mangaData?.data?.favorite
|
|
||||||
? "fill-red-500 text-red-500"
|
|
||||||
: "text-muted-foreground hover:text-red-500"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-lg text-muted-foreground">
|
<p className="text-lg text-muted-foreground">
|
||||||
{mangaData.data?.authors.join(", ")}
|
{mangaData.data?.authors.join(", ")}
|
||||||
@ -345,13 +236,6 @@ const Manga = () => {
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{mangaData.data?.providers.length === 0 && (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<p className="text-foreground">
|
|
||||||
No providers available for this manga.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{mangaData.data?.providers.map((provider) => (
|
{mangaData.data?.providers.map((provider) => (
|
||||||
<Card key={provider.id} className="border-border bg-card">
|
<Card key={provider.id} className="border-border bg-card">
|
||||||
<Collapsible
|
<Collapsible
|
||||||
@ -373,37 +257,36 @@ const Manga = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{provider.supportsChapterFetch &&
|
{provider.supportsChapterFetch && (
|
||||||
provider.providerStatus === "ACTIVE" && (
|
<div className={"flex gap-4 pr-4"}>
|
||||||
<div className={"flex gap-4 pr-4"}>
|
<Button
|
||||||
<Button
|
size="sm"
|
||||||
size="sm"
|
variant="outline"
|
||||||
variant="outline"
|
disabled={isPending}
|
||||||
disabled={isPending}
|
onClick={() =>
|
||||||
onClick={() =>
|
fetchAllMutate({
|
||||||
fetchAllMutate({
|
mangaProviderId: provider.id,
|
||||||
mangaProviderId: provider.id,
|
})
|
||||||
})
|
}
|
||||||
}
|
className="gap-2"
|
||||||
className="gap-2"
|
>
|
||||||
>
|
<Database className="h-4 w-4" />
|
||||||
<Database className="h-4 w-4" />
|
Fetch all from Provider
|
||||||
Fetch all from Provider
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
<Button
|
size="sm"
|
||||||
size="sm"
|
variant="outline"
|
||||||
variant="outline"
|
disabled={isPending}
|
||||||
disabled={isPending}
|
onClick={() =>
|
||||||
onClick={() =>
|
mutate({ mangaProviderId: provider.id })
|
||||||
mutate({ mangaProviderId: provider.id })
|
}
|
||||||
}
|
className="gap-2"
|
||||||
className="gap-2"
|
>
|
||||||
>
|
<Database className="h-4 w-4" />
|
||||||
<Database className="h-4 w-4" />
|
Fetch from Provider
|
||||||
Fetch from Provider
|
</Button>
|
||||||
</Button>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
className={`h-5 w-5 text-muted-foreground transition-transform ${
|
className={`h-5 w-5 text-muted-foreground transition-transform ${
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user