mirror of
https://github.com/C9Glax/tranga-website.git
synced 2025-09-10 11:58:20 +02:00
Reorganize Settings for faster access to individual settings (group them by category)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import {ReactNode, useContext, useState} from "react";
|
import {ReactNode, useContext, useState} from "react";
|
||||||
import { ApiContext } from "../../apiClient/ApiContext";
|
import { ApiContext } from "../../apiClient/ApiContext";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button, Card,
|
||||||
Input,
|
Input,
|
||||||
Modal,
|
Modal,
|
||||||
ModalDialog,
|
ModalDialog,
|
||||||
@@ -9,30 +9,36 @@ import {
|
|||||||
Tab,
|
Tab,
|
||||||
TabList,
|
TabList,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
Tabs
|
Tabs, Typography
|
||||||
} from "@mui/joy";
|
} from "@mui/joy";
|
||||||
import ModalClose from "@mui/joy/ModalClose";
|
import ModalClose from "@mui/joy/ModalClose";
|
||||||
import {GotifyRecord, NtfyRecord, PushoverRecord} from "../../apiClient/data-contracts.ts";
|
import {GotifyRecord, NtfyRecord, PushoverRecord} from "../../apiClient/data-contracts.ts";
|
||||||
import {LoadingState, StateColor, StateIndicator} from "../Loading.tsx";
|
import {LoadingState, StateColor, StateIndicator} from "../Loading.tsx";
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
export default function ({open, setOpen} : {open: boolean, setOpen: (open: boolean) => void}) {
|
export default function () {
|
||||||
|
const [notificationConnectorsOpen, setNotificationConnectorsOpen] = React.useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={open} onClose={() => setOpen(false)}>
|
<Card>
|
||||||
<ModalDialog>
|
<Typography>Notification Connectors</Typography>
|
||||||
<ModalClose />
|
<Button onClick={() => setNotificationConnectorsOpen(true)}>Add</Button>
|
||||||
<Tabs sx={{width:'95%'}} defaultValue={"gotify"}>
|
<Modal open={notificationConnectorsOpen} onClose={() => setNotificationConnectorsOpen(false)}>
|
||||||
<TabList>
|
<ModalDialog>
|
||||||
<Tab value={"gotify"}>Gotify</Tab>
|
<ModalClose />
|
||||||
<Tab value={"ntfy"}>Ntfy</Tab>
|
<Tabs sx={{width:'95%'}} defaultValue={"gotify"}>
|
||||||
<Tab value={"pushover"}>Pushover</Tab>
|
<TabList>
|
||||||
</TabList>
|
<Tab value={"gotify"}>Gotify</Tab>
|
||||||
<Gotify />
|
<Tab value={"ntfy"}>Ntfy</Tab>
|
||||||
<Ntfy />
|
<Tab value={"pushover"}>Pushover</Tab>
|
||||||
<Pushover />
|
</TabList>
|
||||||
</Tabs>
|
<Gotify />
|
||||||
</ModalDialog>
|
<Ntfy />
|
||||||
</Modal>
|
<Pushover />
|
||||||
|
</Tabs>
|
||||||
|
</ModalDialog>
|
||||||
|
</Modal>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import {ReactNode, useContext, useState} from "react";
|
import {ReactNode, useContext, useState} from "react";
|
||||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
import {SettingsContext} from "./Settings.tsx";
|
||||||
import {ApiContext} from "../../apiClient/ApiContext.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 * as React from "react";
|
||||||
import MarkdownPreview from "@uiw/react-markdown-preview";
|
import MarkdownPreview from "@uiw/react-markdown-preview";
|
||||||
|
|
||||||
@@ -27,9 +27,10 @@ export default function () : ReactNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsItem title={"Chapter Naming Scheme"}>
|
<Card>
|
||||||
<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"} />
|
<Typography>Chapter Naming Scheme</Typography>
|
||||||
<Input color={scheme} defaultValue={settings?.chapterNamingScheme as string} placeholder={"Scheme"} onChange={schemeChanged} />
|
<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 {ReactNode, useContext, useState} from "react";
|
||||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
import {SettingsContext} from "./Settings.tsx";
|
||||||
import {ApiContext} from "../../apiClient/ApiContext.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 * as React from "react";
|
||||||
|
|
||||||
export default function () : ReactNode {
|
export default function () : ReactNode {
|
||||||
@@ -26,8 +26,9 @@ export default function () : ReactNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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} />
|
<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 {ReactNode, useCallback, useContext, useState} from "react";
|
||||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
import {SettingsContext} from "./Settings.tsx";
|
||||||
import {ColorPaletteProp, Input} from "@mui/joy";
|
import {Button, Card, ColorPaletteProp, Input, Typography} from "@mui/joy";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||||
|
|
||||||
@@ -8,26 +8,34 @@ export default function () : ReactNode {
|
|||||||
const settings = useContext(SettingsContext);
|
const settings = useContext(SettingsContext);
|
||||||
const Api = useContext(ApiContext);
|
const Api = useContext(ApiContext);
|
||||||
|
|
||||||
|
const [uriValue, setUriValue] = React.useState<string>(settings?.flareSolverrUrl as string);
|
||||||
const [uriColor, setUriColor] = useState<ColorPaletteProp>("neutral");
|
const [uriColor, setUriColor] = useState<ColorPaletteProp>("neutral");
|
||||||
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||||
const uriChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
const uriChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||||
clearTimeout(timerRef.current);
|
clearTimeout(timerRef.current);
|
||||||
setUriColor("warning");
|
setUriColor("warning");
|
||||||
timerRef.current = setTimeout(() => {
|
timerRef.current = setTimeout(() => {
|
||||||
Api.settingsFlareSolverrUrlCreate(e.target.value)
|
setUriValue(e.target.value);
|
||||||
.then(response => {
|
|
||||||
if (response.ok)
|
|
||||||
setUriColor("success");
|
|
||||||
else
|
|
||||||
setUriColor("danger");
|
|
||||||
})
|
|
||||||
.catch(() => setUriColor("danger"));
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeUri = useCallback(() => {
|
||||||
|
Api.settingsFlareSolverrUrlCreate(uriValue)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok)
|
||||||
|
setUriColor("success");
|
||||||
|
else
|
||||||
|
setUriColor("danger");
|
||||||
|
})
|
||||||
|
.catch(() => setUriColor("danger"));
|
||||||
|
}, [uriValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsItem title={"FlareSolverr"}>
|
<Card>
|
||||||
<Input color={uriColor} defaultValue={settings?.flareSolverrUrl as string} type={"url"} placeholder={"URL"} onChange={uriChanged} />
|
<Typography>FlareSolverr</Typography>
|
||||||
</SettingsItem>
|
<Input color={uriColor} value={uriValue} type={"url"} placeholder={"URL"} onChange={uriChanged}
|
||||||
|
endDecorator={<Button onClick={changeUri}>Apply</Button>}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -1,13 +1,14 @@
|
|||||||
import {ReactNode, useContext} from "react";
|
import {ReactNode, useContext} from "react";
|
||||||
import {SettingsContext, SettingsItem} from "./Settings.tsx";
|
import {SettingsContext} from "./Settings.tsx";
|
||||||
import {Slider} from "@mui/joy";
|
import {Card, Slider, Typography} from "@mui/joy";
|
||||||
|
|
||||||
export default function () : ReactNode {
|
export default function () : ReactNode {
|
||||||
const settings = useContext(SettingsContext);
|
const settings = useContext(SettingsContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsItem title={"Image Compression"}>
|
<Card>
|
||||||
|
<Typography>Image Compression</Typography>
|
||||||
<Slider sx={{marginTop: "20px"}} valueLabelDisplay={"auto"} defaultValue={settings?.imageCompression}></Slider>
|
<Slider sx={{marginTop: "20px"}} valueLabelDisplay={"auto"} defaultValue={settings?.imageCompression}></Slider>
|
||||||
</SettingsItem>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -19,9 +19,7 @@ export default function () {
|
|||||||
}).catch(_ => setUnusedMangaState(LoadingState.failure));
|
}).catch(_ => setUnusedMangaState(LoadingState.failure));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<SettingsItem title={"Maintenance"}>
|
<SettingsItem title={"Maintenance"}>
|
||||||
<Button
|
<Button
|
||||||
disabled={unusedMangaState == LoadingState.loading}
|
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>
|
|
||||||
);
|
|
||||||
}
|
|
15
tranga-website/src/Components/Settings/Services.tsx
Normal file
15
tranga-website/src/Components/Settings/Services.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import {SettingsItem} from "./Settings.tsx";
|
||||||
|
import NotificationConnectors from "./AddNotificationConnector.tsx";
|
||||||
|
import FlareSolverr from "./FlareSolverr.tsx";
|
||||||
|
|
||||||
|
export default function(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsItem title={"Services"} direction={"row"}>
|
||||||
|
<FlareSolverr />
|
||||||
|
<NotificationConnectors />
|
||||||
|
</SettingsItem>
|
||||||
|
);
|
||||||
|
}
|
@@ -6,20 +6,17 @@ import {
|
|||||||
AccordionSummary, Button, ColorPaletteProp,
|
AccordionSummary, Button, ColorPaletteProp,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle, Input,
|
DialogTitle, Input,
|
||||||
Modal, ModalDialog
|
Modal, ModalDialog, Stack
|
||||||
} from "@mui/joy";
|
} from "@mui/joy";
|
||||||
import './Settings.css';
|
import './Settings.css';
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {createContext, Dispatch, ReactNode, useContext, useEffect, useState} from "react";
|
import {createContext, Dispatch, ReactNode, useContext, useEffect, useState} from "react";
|
||||||
import {TrangaSettings} from "../../apiClient/data-contracts.ts";
|
import {TrangaSettings} from "../../apiClient/data-contracts.ts";
|
||||||
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
import {ApiContext} from "../../apiClient/ApiContext.tsx";
|
||||||
import NotificationConnectors from "./NotificationConnectors.tsx";
|
|
||||||
import {SxProps} from "@mui/joy/styles/types";
|
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 Maintenance from "./Maintenance.tsx";
|
||||||
|
import Services from "./Services.tsx";
|
||||||
|
import Download from './Download.tsx';
|
||||||
|
|
||||||
export const SettingsContext = createContext<TrangaSettings|undefined>(undefined);
|
export const SettingsContext = createContext<TrangaSettings|undefined>(undefined);
|
||||||
|
|
||||||
@@ -69,11 +66,8 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
|
|||||||
defaultValue={Api.baseUrl}
|
defaultValue={Api.baseUrl}
|
||||||
onChange={apiUriChanged} />
|
onChange={apiUriChanged} />
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
<ImageCompression />
|
<Download />
|
||||||
<FlareSolverr />
|
<Services />
|
||||||
<DownloadLanguage />
|
|
||||||
<ChapterNamingScheme />
|
|
||||||
<NotificationConnectors />
|
|
||||||
<Maintenance />
|
<Maintenance />
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@@ -83,12 +77,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 (
|
return (
|
||||||
<Accordion>
|
<Accordion expanded={expanded} onChange={(_, expanded) => setExpanded(expanded)}>
|
||||||
<AccordionSummary>{title}</AccordionSummary>
|
<AccordionSummary>{title}</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
{children}
|
<Stack direction={stackDirection} spacing={1}>
|
||||||
|
{children}
|
||||||
|
</Stack>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user