mirror of
https://github.com/C9Glax/tranga-website.git
synced 2025-09-10 11:58:20 +02:00
Compare commits
7 Commits
9e388c8ac8
...
121b9ed7f6
Author | SHA1 | Date | |
---|---|---|---|
121b9ed7f6 | |||
34a34ef91e | |||
6ee200e482 | |||
1e2ef1d90c | |||
b98bd3a11d | |||
ee1b0536c7 | |||
d5975192e8 |
100
tranga-website/package-lock.json
generated
100
tranga-website/package-lock.json
generated
@@ -980,9 +980,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
|
||||
"integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
|
||||
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -995,9 +995,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz",
|
||||
"integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==",
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
|
||||
"integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@@ -1005,9 +1005,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
|
||||
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
|
||||
"version": "0.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
|
||||
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -1055,13 +1055,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
|
||||
"integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
|
||||
"version": "9.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz",
|
||||
"integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://eslint.org/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/object-schema": {
|
||||
@@ -1075,13 +1078,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
|
||||
"integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
|
||||
"integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/core": "^0.15.1",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2494,9 +2497,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -3193,20 +3196,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.23.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
|
||||
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
|
||||
"version": "9.32.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
|
||||
"integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.19.2",
|
||||
"@eslint/config-helpers": "^0.2.0",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/config-array": "^0.21.0",
|
||||
"@eslint/config-helpers": "^0.3.0",
|
||||
"@eslint/core": "^0.15.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.23.0",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"@eslint/js": "9.32.0",
|
||||
"@eslint/plugin-kit": "^0.3.4",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@@ -3217,9 +3220,9 @@
|
||||
"cross-spawn": "^7.0.6",
|
||||
"debug": "^4.3.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^8.3.0",
|
||||
"eslint-visitor-keys": "^4.2.0",
|
||||
"espree": "^10.3.0",
|
||||
"eslint-scope": "^8.4.0",
|
||||
"eslint-visitor-keys": "^4.2.1",
|
||||
"espree": "^10.4.0",
|
||||
"esquery": "^1.5.0",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@@ -3277,9 +3280,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
|
||||
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
|
||||
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
@@ -3294,9 +3297,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-visitor-keys": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
|
||||
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@@ -3307,15 +3310,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
|
||||
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
|
||||
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-jsx": "^5.3.2",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -7140,21 +7143,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
|
@@ -1,3 +1,3 @@
|
||||
.manga-list {
|
||||
margin: 15px;
|
||||
padding-top: calc(1em * var(--joy-lineHeight-md, 1.5) / 2);;
|
||||
}
|
@@ -100,10 +100,12 @@ function SearchDialog ({open, setOpen} : {open: boolean, setOpen: Dispatch<boole
|
||||
<Modal sx={{width: "100%", height: "100%"}} open={open} onClose={() => setOpen(false)}>
|
||||
<ModalDialog sx={{width: "80%"}}>
|
||||
<ModalClose />
|
||||
<Typography level={"h1"}>Search or add Manga</Typography>
|
||||
<Stepper orientation={"vertical"}>
|
||||
<Step indicator={<StepIndicator>1</StepIndicator>}>
|
||||
<Typography>Connector</Typography>
|
||||
<Select
|
||||
placeholder={"Select Connector 'Global' to search on all Connectors"}
|
||||
disabled={loadingState == LoadingState.loading}
|
||||
onChange={(_, v) => setSelectedMangaConnector(v as MangaConnector)}>
|
||||
{mangaConnectors?.map(con => (
|
||||
@@ -119,6 +121,7 @@ function SearchDialog ({open, setOpen} : {open: boolean, setOpen: Dispatch<boole
|
||||
disabled={loadingState == LoadingState.loading}
|
||||
onKeyDown={keyDownCheck}
|
||||
onChange={(e) => setSearchTerm(e.currentTarget.value)}
|
||||
placeholder={"Enter search-term or supported Connector-Url"}
|
||||
endDecorator={<Button
|
||||
disabled={loadingState == LoadingState.loading}
|
||||
onClick={doTheSearch}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import {ReactNode, useContext, useState} from "react";
|
||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
||||
import {SettingsContext} from "./Settings.tsx";
|
||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||
import {ColorPaletteProp, Input} from "@mui/joy";
|
||||
import {Card, ColorPaletteProp, Input, Typography} from "@mui/joy";
|
||||
import * as React from "react";
|
||||
import MarkdownPreview from "@uiw/react-markdown-preview";
|
||||
|
||||
@@ -27,9 +27,10 @@ export default function () : ReactNode {
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsItem title={"Chapter Naming Scheme"}>
|
||||
<MarkdownPreview style={{backgroundColor: "transparent"}} source={"Placeholders:\n * %M Obj Name\n * %V Volume\n * %C Chapter\n * %T Title\n * %A Author (first in list)\n * %I Chapter Internal ID\n * %i Obj Internal ID\n * %Y Year (Obj)\n *\n * ?_(...) replace _ with a value from above:\n * Everything inside the braces will only be added if the value of %_ is not null"} />
|
||||
<Card>
|
||||
<Typography>Chapter Naming Scheme</Typography>
|
||||
<Input color={scheme} defaultValue={settings?.chapterNamingScheme as string} placeholder={"Scheme"} onChange={schemeChanged} />
|
||||
</SettingsItem>
|
||||
<MarkdownPreview style={{backgroundColor: "transparent"}} source={"Placeholders:\n * %M Obj Name\n * %V Volume\n * %C Chapter\n * %T Title\n * %A Author (first in list)\n * %I Chapter Internal ID\n * %i Obj Internal ID\n * %Y Year (Obj)\n *\n * ?_(...) replace _ with a value from above:\n * Everything inside the braces will only be added if the value of %_ is not null"} />
|
||||
</Card>
|
||||
);
|
||||
}
|
14
tranga-website/src/Components/Settings/Download.tsx
Normal file
14
tranga-website/src/Components/Settings/Download.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import {SettingsItem} from "./Settings.tsx";
|
||||
import ImageCompression from "./ImageCompression.tsx";
|
||||
import DownloadLanguage from "./DownloadLanguage.tsx";
|
||||
import ChapterNamingScheme from "./ChapterNamingScheme.tsx";
|
||||
|
||||
export default function (){
|
||||
return (
|
||||
<SettingsItem title={"Download"}>
|
||||
<ImageCompression />
|
||||
<DownloadLanguage />
|
||||
<ChapterNamingScheme />
|
||||
</SettingsItem>
|
||||
)
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
import {ReactNode, useContext, useState} from "react";
|
||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
||||
import {SettingsContext} from "./Settings.tsx";
|
||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||
import {ColorPaletteProp, Input} from "@mui/joy";
|
||||
import {Card, ColorPaletteProp, Input, Typography} from "@mui/joy";
|
||||
import * as React from "react";
|
||||
|
||||
export default function () : ReactNode {
|
||||
@@ -26,8 +26,9 @@ export default function () : ReactNode {
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsItem title={"Download Language"}>
|
||||
<Card>
|
||||
<Typography>Download Language</Typography>
|
||||
<Input color={color} defaultValue={settings?.downloadLanguage as string} placeholder={"Language code (f.e. 'en')"} onChange={languageChanged} />
|
||||
</SettingsItem>
|
||||
</Card>
|
||||
);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import {ReactNode, useContext, useState} from "react";
|
||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
||||
import {ColorPaletteProp, Input} from "@mui/joy";
|
||||
import {ReactNode, useCallback, useContext, useState} from "react";
|
||||
import {SettingsContext} from "./Settings.tsx";
|
||||
import {Button, Card, ColorPaletteProp, Input, Typography} from "@mui/joy";
|
||||
import * as React from "react";
|
||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||
|
||||
@@ -8,26 +8,35 @@ export default function () : ReactNode {
|
||||
const settings = useContext(SettingsContext);
|
||||
const Api = useContext(ApiContext);
|
||||
|
||||
const [uriValue, setUriValue] = React.useState<string>(settings?.flareSolverrUrl as string);
|
||||
const [uriColor, setUriColor] = useState<ColorPaletteProp>("neutral");
|
||||
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
const uriChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||
clearTimeout(timerRef.current);
|
||||
setUriValue(e.target.value);
|
||||
setUriColor("warning");
|
||||
timerRef.current = setTimeout(() => {
|
||||
Api.settingsFlareSolverrUrlCreate(e.target.value)
|
||||
.then(response => {
|
||||
if (response.ok)
|
||||
setUriColor("success");
|
||||
else
|
||||
setUriColor("danger");
|
||||
})
|
||||
.catch(() => setUriColor("danger"));
|
||||
changeUri();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const changeUri = useCallback(() => {
|
||||
Api.settingsFlareSolverrUrlCreate(uriValue)
|
||||
.then(response => {
|
||||
if (response.ok)
|
||||
setUriColor("success");
|
||||
else
|
||||
setUriColor("danger");
|
||||
})
|
||||
.catch(() => setUriColor("danger"));
|
||||
}, [uriValue]);
|
||||
|
||||
return (
|
||||
<SettingsItem title={"FlareSolverr"}>
|
||||
<Input color={uriColor} defaultValue={settings?.flareSolverrUrl as string} type={"url"} placeholder={"URL"} onChange={uriChanged} />
|
||||
</SettingsItem>
|
||||
<Card>
|
||||
<Typography>FlareSolverr</Typography>
|
||||
<Input color={uriColor} value={uriValue} type={"url"} placeholder={"URL"} onChange={uriChanged}
|
||||
endDecorator={<Button onClick={changeUri}>Apply</Button>}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
@@ -1,13 +1,41 @@
|
||||
import {ReactNode, useContext} from "react";
|
||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
||||
import {Slider} from "@mui/joy";
|
||||
import {ReactNode, useCallback, useContext, useState} from "react";
|
||||
import {SettingsContext} from "./Settings.tsx";
|
||||
import {Button, Card, ColorPaletteProp, Slider, Typography} from "@mui/joy";
|
||||
import * as React from "react";
|
||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||
|
||||
export default function () : ReactNode {
|
||||
const settings = useContext(SettingsContext);
|
||||
const Api = useContext(ApiContext);
|
||||
|
||||
const [value, setValue] = useState(settings?.imageCompression??100);
|
||||
const [color, setColor] = useState<ColorPaletteProp>("neutral");
|
||||
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
const valueChanged = (_: Event, val: number | number[]) => {
|
||||
clearTimeout(timerRef.current);
|
||||
setColor("warning");
|
||||
setValue(val as number);
|
||||
timerRef.current = setTimeout(() => {
|
||||
changeUri();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const changeUri = useCallback(() => {
|
||||
Api.settingsImageCompressionLevelPartialUpdate(value)
|
||||
.then(response => {
|
||||
if (response.ok)
|
||||
setColor("success");
|
||||
else
|
||||
setColor("danger");
|
||||
})
|
||||
.catch(() => setColor("danger"));
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<SettingsItem title={"Image Compression"}>
|
||||
<Slider sx={{marginTop: "20px"}} valueLabelDisplay={"auto"} defaultValue={settings?.imageCompression}></Slider>
|
||||
</SettingsItem>
|
||||
<Card>
|
||||
<Typography>Image Compression</Typography>
|
||||
<Slider sx={{marginTop: "20px"}} valueLabelDisplay={"auto"} value={value} onChange={valueChanged} color={color} />
|
||||
<Button onClick={changeUri}>Set</Button>
|
||||
</Card>
|
||||
);
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
import {Modal, ModalDialog, Tab, TabList, Tabs} from "@mui/joy";
|
||||
import ModalClose from "@mui/joy/ModalClose";
|
||||
import * as React from "react";
|
||||
|
||||
export default function ({open, setOpen} : {open: boolean, setOpen: Dispatch<boolean>}) {
|
||||
return (
|
||||
<Modal open={open} onClose={() => setOpen(false)}>
|
||||
<ModalDialog>
|
||||
<ModalClose />
|
||||
<Tabs sx={{width:'95%'}} defaultValue={"komga"}>
|
||||
<TabList>
|
||||
<Tab value={"komga"}>Komga</Tab>
|
||||
<Tab value={"kavita"}>Kavita</Tab>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
import {Button, Card, Typography} from "@mui/joy";
|
||||
import {useState} from "react";
|
||||
import ListLibraryConnectors from "./ListLibraryConnectors.tsx";
|
||||
import AddLibraryConnector from "./AddLibraryConnector.tsx";
|
||||
|
||||
export default function (){
|
||||
|
||||
const [addDialogOpen, setAddDialogOpen] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Typography>Library Connectors</Typography>
|
||||
<Button onClick={() => setAddDialogOpen(true)}>Add</Button>
|
||||
<ListLibraryConnectors />
|
||||
<AddLibraryConnector open={addDialogOpen} setOpen={() => setAddDialogOpen(false)} />
|
||||
</Card>
|
||||
);
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import {ApiContext} from "../../../apiClient/ApiContext.tsx";
|
||||
import {LibraryConnector} from "../../../apiClient/data-contracts.ts";
|
||||
import {Card, Chip, Input, Stack} from "@mui/joy";
|
||||
|
||||
export default function (){
|
||||
const Api = useContext(ApiContext);
|
||||
const [libraryConnectors, setLibraryConnectors] = useState<LibraryConnector[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getConnectors();
|
||||
}, []);
|
||||
|
||||
const getConnectors = () => {
|
||||
Api.libraryConnectorList().then(r => {
|
||||
if(r.ok)
|
||||
setLibraryConnectors(r.data);
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction={"column"} spacing={1}>
|
||||
{libraryConnectors.map(c => <LibraryConnectorItem key={c.key} connector={c} />)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function LibraryConnectorItem({connector} : {connector: LibraryConnector}){
|
||||
return (
|
||||
<Card>
|
||||
<Chip>{connector.libraryType}</Chip>
|
||||
<Input disabled value={connector.baseUrl} />
|
||||
</Card>
|
||||
);
|
||||
}
|
@@ -19,9 +19,7 @@ export default function () {
|
||||
}).catch(_ => setUnusedMangaState(LoadingState.failure));
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<SettingsItem title={"Maintenance"}>
|
||||
<Button
|
||||
disabled={unusedMangaState == LoadingState.loading}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
import {ReactNode} from "react";
|
||||
import {SettingsItem} from "./Settings.tsx";
|
||||
import {Button} from "@mui/joy";
|
||||
import NotificationConnectors from "./AddNotificationConnector.tsx";
|
||||
import * as React from "react";
|
||||
|
||||
export default function () : ReactNode {
|
||||
const [notificationConnectorsOpen, setNotificationConnectorsOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<SettingsItem title={"Notification Connectors"}>
|
||||
<Button onClick={() => setNotificationConnectorsOpen(true)}>Add Notification Connector</Button>
|
||||
<NotificationConnectors open={notificationConnectorsOpen} setOpen={setNotificationConnectorsOpen} />
|
||||
</SettingsItem>
|
||||
);
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
import {ReactNode, useContext, useState} from "react";
|
||||
import { ApiContext } from "../../apiClient/ApiContext";
|
||||
import { ApiContext } from "../../../apiClient/ApiContext.tsx";
|
||||
import {
|
||||
Button,
|
||||
Button, Card,
|
||||
Input,
|
||||
Modal,
|
||||
ModalDialog,
|
||||
@@ -9,30 +9,38 @@ import {
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
Tabs
|
||||
Tabs, Typography
|
||||
} from "@mui/joy";
|
||||
import ModalClose from "@mui/joy/ModalClose";
|
||||
import {GotifyRecord, NtfyRecord, PushoverRecord} from "../../apiClient/data-contracts.ts";
|
||||
import {LoadingState, StateColor, StateIndicator} from "../Loading.tsx";
|
||||
import {GotifyRecord, NtfyRecord, PushoverRecord} from "../../../apiClient/data-contracts.ts";
|
||||
import {LoadingState, StateColor, StateIndicator} from "../../Loading.tsx";
|
||||
import * as React from "react";
|
||||
import ListNotificationConnector from "./ListNotificationConnector.tsx";
|
||||
|
||||
export default function ({open, setOpen} : {open: boolean, setOpen: (open: boolean) => void}) {
|
||||
export default function () {
|
||||
const [notificationConnectorsOpen, setNotificationConnectorsOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={() => setOpen(false)}>
|
||||
<ModalDialog>
|
||||
<ModalClose />
|
||||
<Tabs sx={{width:'95%'}} defaultValue={"gotify"}>
|
||||
<TabList>
|
||||
<Tab value={"gotify"}>Gotify</Tab>
|
||||
<Tab value={"ntfy"}>Ntfy</Tab>
|
||||
<Tab value={"pushover"}>Pushover</Tab>
|
||||
</TabList>
|
||||
<Gotify />
|
||||
<Ntfy />
|
||||
<Pushover />
|
||||
</Tabs>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
<Card>
|
||||
<Typography>Notification Connectors</Typography>
|
||||
<Button onClick={() => setNotificationConnectorsOpen(true)}>Add</Button>
|
||||
<ListNotificationConnector />
|
||||
<Modal open={notificationConnectorsOpen} onClose={() => setNotificationConnectorsOpen(false)}>
|
||||
<ModalDialog>
|
||||
<ModalClose />
|
||||
<Tabs sx={{width:'95%'}} defaultValue={"gotify"}>
|
||||
<TabList>
|
||||
<Tab value={"gotify"}>Gotify</Tab>
|
||||
<Tab value={"ntfy"}>Ntfy</Tab>
|
||||
<Tab value={"pushover"}>Pushover</Tab>
|
||||
</TabList>
|
||||
<Gotify />
|
||||
<Ntfy />
|
||||
<Pushover />
|
||||
</Tabs>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
@@ -0,0 +1,52 @@
|
||||
import {ApiContext} from "../../../apiClient/ApiContext.tsx";
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import { NotificationConnector } from "../../../apiClient/data-contracts.ts";
|
||||
import {Card, Chip, Input, Stack, Table, Textarea, Typography} from "@mui/joy";
|
||||
|
||||
export default function (){
|
||||
const Api = useContext(ApiContext);
|
||||
const [notificationConnectors, setNotificationConnectors] = useState<NotificationConnector[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getConnectors();
|
||||
}, []);
|
||||
|
||||
const getConnectors = () => {
|
||||
Api.notificationConnectorList().then(r => {
|
||||
if(r.ok)
|
||||
setNotificationConnectors(r.data);
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction={"column"} spacing={1}>
|
||||
{notificationConnectors.map(c => <NotificationConnectorItem key={c.name} connector={c} />)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function NotificationConnectorItem({connector} : {connector: NotificationConnector}){
|
||||
return (
|
||||
<Card>
|
||||
<Typography left={"h2"}>{connector.name}</Typography>
|
||||
<Input disabled startDecorator={<Chip>{connector.httpMethod}</Chip>} value={connector.url} />
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(connector.headers).map(x =>
|
||||
<tr key={x[0]}>
|
||||
<td>{x[0]}</td>
|
||||
<td>{[x[1]]}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
<Textarea disabled value={connector.body}/>
|
||||
</Card>
|
||||
);
|
||||
}
|
17
tranga-website/src/Components/Settings/Services.tsx
Normal file
17
tranga-website/src/Components/Settings/Services.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import {SettingsItem} from "./Settings.tsx";
|
||||
import NotificationConnectors from "./NotificationConnectors/AddNotificationConnector.tsx";
|
||||
import FlareSolverr from "./FlareSolverr.tsx";
|
||||
import LibraryConnectors from "./LibraryConnectors/LibraryConnectors.tsx";
|
||||
|
||||
export default function(){
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<SettingsItem title={"Services"} direction={"row"}>
|
||||
<FlareSolverr />
|
||||
<NotificationConnectors />
|
||||
<LibraryConnectors />
|
||||
</SettingsItem>
|
||||
);
|
||||
}
|
@@ -6,20 +6,17 @@ import {
|
||||
AccordionSummary, Button, ColorPaletteProp,
|
||||
DialogContent,
|
||||
DialogTitle, Input,
|
||||
Modal, ModalDialog
|
||||
Modal, ModalDialog, Stack
|
||||
} from "@mui/joy";
|
||||
import './Settings.css';
|
||||
import * as React from "react";
|
||||
import {createContext, Dispatch, ReactNode, useContext, useEffect, useState} from "react";
|
||||
import {TrangaSettings} from "../../apiClient/data-contracts.ts";
|
||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||
import NotificationConnectors from "./NotificationConnectors.tsx";
|
||||
import {SxProps} from "@mui/joy/styles/types";
|
||||
import ImageCompression from "./ImageCompression.tsx";
|
||||
import FlareSolverr from "./FlareSolverr.tsx";
|
||||
import DownloadLanguage from "./DownloadLanguage.tsx";
|
||||
import ChapterNamingScheme from "./ChapterNamingScheme.tsx";
|
||||
import Maintenance from "./Maintenance.tsx";
|
||||
import Services from "./Services.tsx";
|
||||
import Download from './Download.tsx';
|
||||
|
||||
export const SettingsContext = createContext<TrangaSettings|undefined>(undefined);
|
||||
|
||||
@@ -41,8 +38,12 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
|
||||
const apiUriChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||
clearTimeout(timerRef.current);
|
||||
setApiUriColor("warning");
|
||||
|
||||
const val = e.target.value;
|
||||
const value = val.endsWith("/") ? val.substring(0, val.length - 1) : val;
|
||||
|
||||
timerRef.current = setTimeout(() => {
|
||||
setApiUri(e.target.value);
|
||||
setApiUri(value);
|
||||
setApiUriColor("success");
|
||||
}, 1000);
|
||||
}
|
||||
@@ -69,11 +70,8 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
|
||||
defaultValue={Api.baseUrl}
|
||||
onChange={apiUriChanged} />
|
||||
</SettingsItem>
|
||||
<ImageCompression />
|
||||
<FlareSolverr />
|
||||
<DownloadLanguage />
|
||||
<ChapterNamingScheme />
|
||||
<NotificationConnectors />
|
||||
<Download />
|
||||
<Services />
|
||||
<Maintenance />
|
||||
</AccordionGroup>
|
||||
</DialogContent>
|
||||
@@ -83,12 +81,17 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
|
||||
);
|
||||
}
|
||||
|
||||
export function SettingsItem({title, children} : {title: string, children: ReactNode}) {
|
||||
export function SettingsItem({title, children, defaultExpanded, direction} : {title: string, children?: ReactNode, defaultExpanded?: boolean, direction?: "row" | "column"}) {
|
||||
const [expanded, setExpanded] = React.useState(defaultExpanded??false);
|
||||
const stackDirection = direction ?? "column";
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<Accordion expanded={expanded} onChange={(_, expanded) => setExpanded(expanded)}>
|
||||
<AccordionSummary>{title}</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{children}
|
||||
<Stack direction={stackDirection} spacing={1}>
|
||||
{children}
|
||||
</Stack>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
Reference in New Issue
Block a user