diff --git a/tranga-website/README.md b/tranga-website/README.md index 40ede56..bee7491 100644 --- a/tranga-website/README.md +++ b/tranga-website/README.md @@ -24,31 +24,31 @@ export default tseslint.config({ languageOptions: { // other options... parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], + project: ["./tsconfig.node.json", "./tsconfig.app.json"], tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: ```js // eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' +import reactX from "eslint-plugin-react-x"; +import reactDom from "eslint-plugin-react-dom"; export default tseslint.config({ plugins: { // Add the react-x and react-dom plugins - 'react-x': reactX, - 'react-dom': reactDom, + "react-x": reactX, + "react-dom": reactDom, }, rules: { // other rules... // Enable its recommended typescript rules - ...reactX.configs['recommended-typescript'].rules, + ...reactX.configs["recommended-typescript"].rules, ...reactDom.configs.recommended.rules, }, -}) +}); ``` diff --git a/tranga-website/eslint.config.js b/tranga-website/eslint.config.js index 092408a..79a552e 100644 --- a/tranga-website/eslint.config.js +++ b/tranga-website/eslint.config.js @@ -1,28 +1,28 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ["dist"] }, { extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, }, -) +); diff --git a/tranga-website/package-lock.json b/tranga-website/package-lock.json index d647d92..f7b7a8e 100644 --- a/tranga-website/package-lock.json +++ b/tranga-website/package-lock.json @@ -26,6 +26,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "prettier": "3.6.2", "swagger-typescript-api": "^13.2.7", "typescript": "~5.7.2", "typescript-eslint": "^8.24.1", @@ -980,9 +981,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 +996,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.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1005,9 +1006,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.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1055,13 +1056,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.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "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 +1079,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.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.12.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -2494,9 +2498,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 +3197,20 @@ } }, "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "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.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -3217,9 +3221,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 +3281,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 +3298,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 +3311,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" @@ -5739,6 +5743,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -7140,21 +7160,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", diff --git a/tranga-website/package.json b/tranga-website/package.json index bec82c8..f51ba0c 100644 --- a/tranga-website/package.json +++ b/tranga-website/package.json @@ -6,8 +6,10 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", - "lint": "eslint .", - "preview": "vite preview" + "lint": "eslint . --fix", + "swagger-api": "swagger-typescript-api generate -p http://127.0.0.1:6531/swagger/v2/swagger.json -o ./src/apiClient --modular", + "prettier:check": "prettier . --check", + "prettier": "prettier . --write" }, "dependencies": { "@emotion/react": "^11.14.0", @@ -28,6 +30,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "prettier": "3.6.2", "swagger-typescript-api": "^13.2.7", "typescript": "~5.7.2", "typescript-eslint": "^8.24.1", diff --git a/tranga-website/src/App.css b/tranga-website/src/App.css index 8e2ae63..03e3a91 100644 --- a/tranga-website/src/App.css +++ b/tranga-website/src/App.css @@ -10,4 +10,4 @@ position: absolute; width: 100%; left: 0; -} \ No newline at end of file +} diff --git a/tranga-website/src/App.tsx b/tranga-website/src/App.tsx index 8f9297f..5c75f70 100644 --- a/tranga-website/src/App.tsx +++ b/tranga-website/src/App.tsx @@ -1,85 +1,89 @@ -import Sheet from '@mui/joy/Sheet'; -import './App.css' +import Sheet from "@mui/joy/Sheet"; +import "./App.css"; import Settings from "./Components/Settings/Settings.tsx"; import Header from "./Header.tsx"; -import {createContext, ReactNode, useEffect, useState} from "react"; -import {V2} from "./apiClient/V2.ts"; -import { ApiContext } from './apiClient/ApiContext.tsx'; +import { createContext, ReactNode, useEffect, useState } from "react"; +import { V2 } from "./apiClient/V2.ts"; +import { ApiContext } from "./apiClient/ApiContext.tsx"; import MangaList from "./Components/Mangas/MangaList.tsx"; -import {FileLibrary, Manga, MangaConnector} from "./apiClient/data-contracts.ts"; +import { + FileLibrary, + Manga, + MangaConnector, +} from "./apiClient/data-contracts.ts"; import Search from "./Components/Search.tsx"; -import {Typography} from "@mui/joy"; +import { Typography } from "@mui/joy"; export const MangaConnectorContext = createContext([]); export const MangaContext = createContext([]); export const FileLibraryContext = createContext([]); -export default function App () { - const apiUriStr = localStorage.getItem("apiUri") ?? window.location.href.substring(0, window.location.href.lastIndexOf("/")) + "/api"; - const [apiUri, setApiUri] = useState(apiUriStr); - const [Api, setApi] = useState(new V2({ - baseUrl: apiUri - })); +export default function App() { + const apiUriStr = + localStorage.getItem("apiUri") ?? + window.location.href.substring(0, window.location.href.lastIndexOf("/")) + + "/api"; + const [apiUri, setApiUri] = useState(apiUriStr); + const [Api, setApi] = useState( + new V2({ + baseUrl: apiUri, + }), + ); - const [mangaConnectors, setMangaConnectors] = useState([]); - const [manga, setManga] = useState([]); - const [fileLibraries, setFileLibraries] = useState([]); - - useEffect(() => { - Api.mangaConnectorList().then(response => { - if (response.ok) - setMangaConnectors(response.data); - }); - - Api.fileLibraryList().then(response => { - if (response.ok) - setFileLibraries(response.data); - }) - - Api.queryMangaDownloadingList() - .then(response => { - if (response.ok) - setManga(response.data); - }); - }, []); - - - useEffect(() => { - localStorage.setItem("apiUri", apiUri); - if (Api.baseUrl != apiUri) - setApi(new V2({ - baseUrl: apiUri - })); - }, [apiUri]); + const [mangaConnectors, setMangaConnectors] = useState([]); + const [manga, setManga] = useState([]); + const [fileLibraries, setFileLibraries] = useState([]); - return ( - - - - - { - Api ? - -
- -
- - - - - -
- : - } -
-
-
-
- ); + useEffect(() => { + Api.mangaConnectorList().then((response) => { + if (response.ok) setMangaConnectors(response.data); + }); + + Api.fileLibraryList().then((response) => { + if (response.ok) setFileLibraries(response.data); + }); + + Api.queryMangaDownloadingList().then((response) => { + if (response.ok) setManga(response.data); + }); + }, []); + + useEffect(() => { + localStorage.setItem("apiUri", apiUri); + if (Api.baseUrl != apiUri) + setApi( + new V2({ + baseUrl: apiUri, + }), + ); + }, [apiUri]); + + return ( + + + + + {Api ? ( + +
+ +
+ + + + + +
+ ) : ( + + )} +
+
+
+
+ ); } -function Loading () : ReactNode{ - return ( - Loading - ); -} \ No newline at end of file +function Loading(): ReactNode { + return Loading; +} diff --git a/tranga-website/src/Components/Loading.tsx b/tranga-website/src/Components/Loading.tsx index 8f0c072..6813c61 100644 --- a/tranga-website/src/Components/Loading.tsx +++ b/tranga-website/src/Components/Loading.tsx @@ -1,32 +1,34 @@ -import {Close, Done} from "@mui/icons-material"; -import {CircularProgress, ColorPaletteProp} from "@mui/joy"; -import {ReactNode} from "react"; +import { Close, Done } from "@mui/icons-material"; +import { CircularProgress, ColorPaletteProp } from "@mui/joy"; +import { ReactNode } from "react"; export enum LoadingState { - none, - loading, - success, - failure + none, + loading, + success, + failure, } -export function StateIndicator(state : LoadingState) : ReactNode { - switch (state) { - case LoadingState.loading: - return (); - case LoadingState.failure: - return (); - case LoadingState.success: - return (); - default: return null; - } +export function StateIndicator(state: LoadingState): ReactNode { + switch (state) { + case LoadingState.loading: + return ; + case LoadingState.failure: + return ; + case LoadingState.success: + return ; + default: + return null; + } } -export function StateColor(state : LoadingState) : ColorPaletteProp | undefined { - switch (state) { - case LoadingState.failure: - return "danger"; - case LoadingState.success: - return "success"; - default: return undefined; - } -} \ No newline at end of file +export function StateColor(state: LoadingState): ColorPaletteProp | undefined { + switch (state) { + case LoadingState.failure: + return "danger"; + case LoadingState.success: + return "success"; + default: + return undefined; + } +} diff --git a/tranga-website/src/Components/MangaConnectorLink.tsx b/tranga-website/src/Components/MangaConnectorLink.tsx index 4f38533..ea837b1 100644 --- a/tranga-website/src/Components/MangaConnectorLink.tsx +++ b/tranga-website/src/Components/MangaConnectorLink.tsx @@ -1,42 +1,97 @@ -import {CSSProperties, ReactNode, useContext, useEffect, useRef, useState} from "react"; -import {ChapterMangaConnectorId, MangaConnector, MangaMangaConnectorId} from "../apiClient/data-contracts.ts"; -import {Link, Tooltip, Typography} from "@mui/joy"; -import {MangaConnectorContext} from "../App.tsx"; -import {ApiContext} from "../apiClient/ApiContext.tsx"; +import { + CSSProperties, + ReactNode, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { + ChapterMangaConnectorId, + MangaConnector, + MangaMangaConnectorId, +} from "../apiClient/data-contracts.ts"; +import { Link, Tooltip, Typography } from "@mui/joy"; +import { MangaConnectorContext } from "../App.tsx"; +import { ApiContext } from "../apiClient/ApiContext.tsx"; -export default function MangaConnectorLink({MangaConnectorId, imageStyle, printName} : {MangaConnectorId : MangaMangaConnectorId | ChapterMangaConnectorId, imageStyle? : CSSProperties, printName?: boolean}) : ReactNode{ - const mangaConnectorContext = useContext(MangaConnectorContext); - const [mangaConnector, setMangaConnector] = useState(mangaConnectorContext?.find(c => c.name == MangaConnectorId.mangaConnectorName)); - const imageRef = useRef(null); - - useEffect(() => { - const connector = mangaConnectorContext?.find(c => c.name == MangaConnectorId.mangaConnectorName); - setMangaConnector(connector); - if (imageRef?.current != null) - imageRef.current.setHTMLUnsafe(``); - }, []); - - return ( - {MangaConnectorId.mangaConnectorName}: {MangaConnectorId.websiteUrl}}> - - - {printName ? {mangaConnector?.name} : null} - - +export default function MangaConnectorLink({ + MangaConnectorId, + imageStyle, + printName, +}: { + MangaConnectorId: MangaMangaConnectorId | ChapterMangaConnectorId; + imageStyle?: CSSProperties; + printName?: boolean; +}): ReactNode { + const mangaConnectorContext = useContext(MangaConnectorContext); + const [mangaConnector, setMangaConnector] = useState< + MangaConnector | undefined + >( + mangaConnectorContext?.find( + (c) => c.name == MangaConnectorId.mangaConnectorName, + ), + ); + const imageRef = useRef(null); + + useEffect(() => { + const connector = mangaConnectorContext?.find( + (c) => c.name == MangaConnectorId.mangaConnectorName, ); + setMangaConnector(connector); + if (imageRef?.current != null) + imageRef.current.setHTMLUnsafe( + ``, + ); + }, []); + + return ( + + {MangaConnectorId.mangaConnectorName}:{" "} + + {MangaConnectorId.websiteUrl} + + + } + > + + + {printName ? {mangaConnector?.name} : null} + + + ); } -export function MangaConnectorLinkFromId({MangaConnectorIdId, imageStyle, printName} : {MangaConnectorIdId: string, imageStyle? : CSSProperties, printName?: boolean}) : ReactNode { - const Api = useContext(ApiContext); - - const [node, setNode] = useState(null); +export function MangaConnectorLinkFromId({ + MangaConnectorIdId, + imageStyle, + printName, +}: { + MangaConnectorIdId: string; + imageStyle?: CSSProperties; + printName?: boolean; +}): ReactNode { + const Api = useContext(ApiContext); - useEffect(() => { - Api.queryMangaMangaConnectorIdDetail(MangaConnectorIdId).then(response => { - if (response.ok) - setNode(); - }); - }, []); - - return node; -} \ No newline at end of file + const [node, setNode] = useState(null); + + useEffect(() => { + Api.queryMangaMangaConnectorIdDetail(MangaConnectorIdId).then( + (response) => { + if (response.ok) + setNode( + , + ); + }, + ); + }, []); + + return node; +} diff --git a/tranga-website/src/Components/Mangas/MangaCard.css b/tranga-website/src/Components/Mangas/MangaCard.css index 87959c3..f7635c3 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.css +++ b/tranga-website/src/Components/Mangas/MangaCard.css @@ -1,21 +1,25 @@ .manga-card { - width: 220px; - height: 300px; + width: 220px; + height: 300px; } .manga-cover-blur { - background: linear-gradient(135deg, rgba(245, 169, 184, 0.85) 40%, rgba(91, 206, 250, 0.3)); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);) - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); + background: linear-gradient( + 135deg, + rgba(245, 169, 184, 0.85) 40%, + rgba(91, 206, 250, 0.3) + ); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + )backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); } .manga-card-badge-icon { - width: 25px; - height: 25px; + width: 25px; + height: 25px; } .manga-modal { - width: 90%; - margin: auto; -} \ No newline at end of file + width: 90%; + margin: auto; +} diff --git a/tranga-website/src/Components/Mangas/MangaCard.tsx b/tranga-website/src/Components/Mangas/MangaCard.tsx index df11ee7..b18a8cb 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.tsx +++ b/tranga-website/src/Components/Mangas/MangaCard.tsx @@ -1,105 +1,176 @@ import { - Badge, - Box, - Card, - CardContent, - CardCover, - Chip, - Link, - Modal, - ModalDialog, - Stack, Tooltip, - Typography + Badge, + Box, + Card, + CardContent, + CardCover, + Chip, + Link, + Modal, + ModalDialog, + Stack, + Tooltip, + Typography, } from "@mui/joy"; -import {Manga} from "../../apiClient/data-contracts.ts"; -import {Dispatch, ReactNode, SetStateAction, useContext, useState} from "react"; +import { Manga } from "../../apiClient/data-contracts.ts"; +import { + Dispatch, + ReactNode, + SetStateAction, + useContext, + useState, +} from "react"; import "./MangaCard.css"; import MangaConnectorBadge from "./MangaConnectorBadge.tsx"; import ModalClose from "@mui/joy/ModalClose"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import MarkdownPreview from '@uiw/react-markdown-preview'; -import {MangaContext} from "../../App.tsx"; -import {MangaConnectorLinkFromId} from "../MangaConnectorLink.tsx"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; +import MarkdownPreview from "@uiw/react-markdown-preview"; +import { MangaContext } from "../../App.tsx"; +import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx"; -export function MangaCardFromId({mangaId} : {mangaId: string}) { - const mangas = useContext(MangaContext); - const manga = mangas.find(manga => manga.key === mangaId); - - return +export function MangaCardFromId({ mangaId }: { mangaId: string }) { + const mangas = useContext(MangaContext); + const manga = mangas.find((manga) => manga.key === mangaId); + + return ; } -export function MangaCard({manga, children} : {manga: Manga | undefined, children? : ReactNode}) { - if (manga === undefined) - return PlaceHolderCard(); +export function MangaCard({ + manga, + children, +}: { + manga: Manga | undefined; + children?: ReactNode; +}) { + if (manga === undefined) return PlaceHolderCard(); - const [open, setOpen] = useState(false); - - return ( - - setOpen(true)}> - - - - - - {manga?.name} - - - - {children} - - - ); + const [open, setOpen] = useState(false); + + return ( + + setOpen(true)}> + + + + + + {manga?.name} + + + + {children} + + + ); } -export function MangaModal({manga, open, setOpen, children}: {manga: Manga | undefined, open: boolean, setOpen: Dispatch>, children?: ReactNode}) { - - return ( - setOpen(false)} className={"manga-modal"}> - - - {manga?.altTitles?.map(title => {title.title})}}> - {manga?.name} - - - - - - - - {manga?.mangaConnectorIdsIds?.map((idid) => )} - {manga?.mangaTags?.map((tag) => {tag.tag})} - {manga?.links?.map((link) => {link.linkProvider})} - - - - - {children} - - - - - ); +export function MangaModal({ + manga, + open, + setOpen, + children, +}: { + manga: Manga | undefined; + open: boolean; + setOpen: Dispatch>; + children?: ReactNode; +}) { + return ( + setOpen(false)} className={"manga-modal"}> + + + + {manga?.altTitles?.map((title) => ( + {title.title} + ))} + + } + > + + {manga?.name} + + + + + + + + + {manga?.mangaConnectorIdsIds?.map((idid) => ( + + ))} + {manga?.mangaTags?.map((tag) => ( + {tag.tag} + ))} + {manga?.links?.map((link) => ( + + {link.linkProvider} + + ))} + + + + + + {children} + + + + + + ); } -function PlaceHolderCard(){ - return ( - - - - - - - - - ); +function PlaceHolderCard() { + return ( + + + + + + + + + ); } -function MangaCover({manga}: {manga: Manga | undefined}) { - const api = useContext(ApiContext); - const uri = manga ? `${api.baseUrl}/v2/Manga/${manga?.key}/Cover` : "blahaj.png"; - - return ( - - ); -} \ No newline at end of file +function MangaCover({ manga }: { manga: Manga | undefined }) { + const api = useContext(ApiContext); + const uri = manga + ? `${api.baseUrl}/v2/Manga/${manga?.key}/Cover` + : "blahaj.png"; + + return ( + + ); +} diff --git a/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx b/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx index f56fe7b..85c17b8 100644 --- a/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx +++ b/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx @@ -1,14 +1,23 @@ import { Badge } from "@mui/joy"; -import {Manga} from "../../apiClient/data-contracts.ts"; -import {ReactElement} from "react"; -import "./MangaCard.css" -import {MangaConnectorLinkFromId} from "../MangaConnectorLink.tsx"; +import { Manga } from "../../apiClient/data-contracts.ts"; +import { ReactElement } from "react"; +import "./MangaCard.css"; +import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx"; -export default function MangaConnectorBadge ({manga, children} : {manga: Manga, children? : ReactElement | ReactElement[] | undefined}) { - - return ( - )}> - {children} - - ); -} \ No newline at end of file +export default function MangaConnectorBadge({ + manga, + children, +}: { + manga: Manga; + children?: ReactElement | ReactElement[] | undefined; +}) { + return ( + ( + + ))} + > + {children} + + ); +} diff --git a/tranga-website/src/Components/Mangas/MangaDownloadDialog.tsx b/tranga-website/src/Components/Mangas/MangaDownloadDialog.tsx index d4644ed..f2127c6 100644 --- a/tranga-website/src/Components/Mangas/MangaDownloadDialog.tsx +++ b/tranga-website/src/Components/Mangas/MangaDownloadDialog.tsx @@ -1,100 +1,138 @@ -import {Manga} from "../../apiClient/data-contracts.ts"; -import {ChangeEvent, Dispatch, ReactNode, useContext, useEffect, useState} from "react"; -import {Button, Checkbox, Option, Select, Stack, Typography} from "@mui/joy"; +import { Manga } from "../../apiClient/data-contracts.ts"; +import { + ChangeEvent, + Dispatch, + ReactNode, + useContext, + useEffect, + useState, +} from "react"; +import { Button, Checkbox, Option, Select, Stack, Typography } from "@mui/joy"; import Drawer from "@mui/joy/Drawer"; import ModalClose from "@mui/joy/ModalClose"; -import {MangaConnectorLinkFromId} from "../MangaConnectorLink.tsx"; +import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx"; import Sheet from "@mui/joy/Sheet"; -import {FileLibraryContext} from "../../App.tsx"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import {LoadingState, StateIndicator} from "../Loading.tsx"; +import { FileLibraryContext } from "../../App.tsx"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; +import { LoadingState, StateIndicator } from "../Loading.tsx"; -export default function ({manga} : {manga: Manga}) : ReactNode{ - const [open, setOpen] = useState(false); - - return ( - <> - - - - ); +export default function ({ manga }: { manga: Manga }): ReactNode { + const [open, setOpen] = useState(false); + + return ( + <> + + + + ); } -function DownloadDrawer({manga, open, setOpen}: {manga: Manga, open: boolean, setOpen: Dispatch}): ReactNode { - const fileLibraries = useContext(FileLibraryContext); - const Api = useContext(ApiContext); - - const onLibraryChange = (_ :any, value: ({} | null)) => { - if (value === undefined) - return; - Api.mangaChangeLibraryCreate(manga.key as string, value as string); - } - - return ( - setOpen(false)}> - - - Download to Library: - - Download from: - - {manga.mangaConnectorIdsIds?.map(id => )} - - - - ); +function DownloadDrawer({ + manga, + open, + setOpen, +}: { + manga: Manga; + open: boolean; + setOpen: Dispatch; +}): ReactNode { + const fileLibraries = useContext(FileLibraryContext); + const Api = useContext(ApiContext); + + const onLibraryChange = (_: any, value: {} | null) => { + if (value === undefined) return; + Api.mangaChangeLibraryCreate(manga.key as string, value as string); + }; + + return ( + setOpen(false)}> + + + Download to Library: + + Download from: + + {manga.mangaConnectorIdsIds?.map((id) => ( + + ))} + + + + ); } -function DownloadCheckBox({mangaConnectorIdId} : {mangaConnectorIdId : string}) : ReactNode { - const Api = useContext(ApiContext); - const [useForDownloading, setUseForDownloading] = useState(false); - const [loading, setLoading] = useState(LoadingState.none); - - useEffect(() => { - setLoading(LoadingState.loading); - Api.queryMangaMangaConnectorIdDetail(mangaConnectorIdId).then(response => { - if (response.ok){ - setUseForDownloading(response.data.useForDownload as boolean); - setLoading(LoadingState.none); - }else - setLoading(LoadingState.failure); - }).catch(_ => setLoading(LoadingState.failure)); - }, []); - - const onSelected = (event: ChangeEvent) => { - setLoading(LoadingState.loading); - const val = event.currentTarget.checked; - Api.queryMangaMangaConnectorIdDetail(mangaConnectorIdId).then(response => { - if (!response.ok){ - setLoading(LoadingState.failure); - return; - } - Api.mangaSetAsDownloadFromCreate(response.data.objId, response.data.mangaConnectorName, val) - .then(response => { - if (response.ok){ - setUseForDownloading(val); - setLoading(LoadingState.success); - }else - setLoading(LoadingState.failure); - }).catch(_ => setLoading(LoadingState.failure)); - }).catch(_ => setLoading(LoadingState.failure)); - } - - return ( - - - - } /> - ); -} \ No newline at end of file +function DownloadCheckBox({ + mangaConnectorIdId, +}: { + mangaConnectorIdId: string; +}): ReactNode { + const Api = useContext(ApiContext); + const [useForDownloading, setUseForDownloading] = useState(false); + const [loading, setLoading] = useState(LoadingState.none); + + useEffect(() => { + setLoading(LoadingState.loading); + Api.queryMangaMangaConnectorIdDetail(mangaConnectorIdId) + .then((response) => { + if (response.ok) { + setUseForDownloading(response.data.useForDownload as boolean); + setLoading(LoadingState.none); + } else setLoading(LoadingState.failure); + }) + .catch((_) => setLoading(LoadingState.failure)); + }, []); + + const onSelected = (event: ChangeEvent) => { + setLoading(LoadingState.loading); + const val = event.currentTarget.checked; + Api.queryMangaMangaConnectorIdDetail(mangaConnectorIdId) + .then((response) => { + if (!response.ok) { + setLoading(LoadingState.failure); + return; + } + Api.mangaSetAsDownloadFromCreate( + response.data.objId, + response.data.mangaConnectorName, + val, + ) + .then((response) => { + if (response.ok) { + setUseForDownloading(val); + setLoading(LoadingState.success); + } else setLoading(LoadingState.failure); + }) + .catch((_) => setLoading(LoadingState.failure)); + }) + .catch((_) => setLoading(LoadingState.failure)); + }; + + return ( + + + + } + /> + ); +} diff --git a/tranga-website/src/Components/Mangas/MangaList.css b/tranga-website/src/Components/Mangas/MangaList.css index 94bdfd6..698ae82 100644 --- a/tranga-website/src/Components/Mangas/MangaList.css +++ b/tranga-website/src/Components/Mangas/MangaList.css @@ -1,3 +1,3 @@ .manga-list { - padding-top: 12px; -} \ No newline at end of file + padding-top: 12px; +} diff --git a/tranga-website/src/Components/Mangas/MangaList.tsx b/tranga-website/src/Components/Mangas/MangaList.tsx index bd347f4..8a0f050 100644 --- a/tranga-website/src/Components/Mangas/MangaList.tsx +++ b/tranga-website/src/Components/Mangas/MangaList.tsx @@ -1,28 +1,38 @@ -import {ReactNode} from "react"; -import {MangaCard} from "./MangaCard.tsx"; -import {Stack} from "@mui/joy"; +import { ReactNode } from "react"; +import { MangaCard } from "./MangaCard.tsx"; +import { Stack } from "@mui/joy"; import "./MangaList.css"; -import {Manga} from "../../apiClient/data-contracts.ts"; +import { Manga } from "../../apiClient/data-contracts.ts"; import MangaDownloadDialog from "./MangaDownloadDialog.tsx"; import MangaMerge from "./MangaMerge.tsx"; -export default function MangaList ({mangas, children} : {mangas: Manga[], children?: ReactNode}) { - - return ( - - {children} - {mangas?.map(manga => ( - - - - - ))} - - ); - -} \ No newline at end of file +export default function MangaList({ + mangas, + children, +}: { + mangas: Manga[]; + children?: ReactNode; +}) { + return ( + + {children} + {mangas?.map((manga) => ( + + + + + ))} + + ); +} diff --git a/tranga-website/src/Components/Mangas/MangaMerge.tsx b/tranga-website/src/Components/Mangas/MangaMerge.tsx index 660d980..ea8e367 100644 --- a/tranga-website/src/Components/Mangas/MangaMerge.tsx +++ b/tranga-website/src/Components/Mangas/MangaMerge.tsx @@ -1,105 +1,136 @@ -import {ReactNode, useContext, useEffect, useState} from "react"; -import {Manga} from "../../apiClient/data-contracts.ts"; +import { ReactNode, useContext, useEffect, useState } from "react"; +import { Manga } from "../../apiClient/data-contracts.ts"; import Drawer from "@mui/joy/Drawer"; import ModalClose from "@mui/joy/ModalClose"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import {MangaCard} from "./MangaCard.tsx"; -import {Alert, Button, Modal, ModalDialog, Stack, Tooltip, Typography} from "@mui/joy"; -import {KeyboardDoubleArrowRight, Warning} from "@mui/icons-material"; -import {LoadingState, StateIndicator} from "../Loading.tsx"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; +import { MangaCard } from "./MangaCard.tsx"; +import { + Alert, + Button, + Modal, + ModalDialog, + Stack, + Tooltip, + Typography, +} from "@mui/joy"; +import { KeyboardDoubleArrowRight, Warning } from "@mui/icons-material"; +import { LoadingState, StateIndicator } from "../Loading.tsx"; -export default function ({manga} : {manga : Manga | undefined}) : ReactNode { - const Api = useContext(ApiContext); - - const [similar, setSimilar] = useState([]); - const [open, setOpen] = useState(false); - - useEffect(()=> { - if (manga === undefined || !open) - return; - Api.queryMangaSimilarNameList(manga.key as string).then(response => { - if (response.ok) - Api.mangaWithIDsCreate(response.data).then(response => { - if (response.ok) - setSimilar(response.data); - }); +export default function ({ manga }: { manga: Manga | undefined }): ReactNode { + const Api = useContext(ApiContext); + + const [similar, setSimilar] = useState([]); + const [open, setOpen] = useState(false); + + useEffect(() => { + if (manga === undefined || !open) return; + Api.queryMangaSimilarNameList(manga.key as string).then((response) => { + if (response.ok) + Api.mangaWithIDsCreate(response.data).then((response) => { + if (response.ok) setSimilar(response.data); }); - }, [open]); - - const exit = (manga : Manga) => { - setOpen(false); - setSimilar(similar.filter(m => m.key != manga.key)); - } - - return ( - <> - - setOpen(false)} anchor={"bottom"}> - - - {similar.map(similarManga => - exit(similarManga)} /> - )} - - - - ); + }); + }, [open]); + + const exit = (manga: Manga) => { + setOpen(false); + setSimilar(similar.filter((m) => m.key != manga.key)); + }; + + return ( + <> + + setOpen(false)} + anchor={"bottom"} + > + + + {similar.map((similarManga) => ( + + exit(similarManga)} + /> + + ))} + + + + ); } -function ConfirmationModal({manga, similarManga, exit} : {manga : Manga, similarManga : Manga, exit: ()=>void}) : ReactNode { - const [open, setOpen] = useState(false); - const Api = useContext(ApiContext); +function ConfirmationModal({ + manga, + similarManga, + exit, +}: { + manga: Manga; + similarManga: Manga; + exit: () => void; +}): ReactNode { + const [open, setOpen] = useState(false); + const Api = useContext(ApiContext); - const [loadingState, setLoadingState] = useState(LoadingState.none); - - const merge = () => { - setLoadingState(LoadingState.loading); - Api.mangaMergeIntoPartialUpdate(manga.key as string, similarManga.key as string).then(response => { - if (response.ok){ - setLoadingState(LoadingState.success); - setOpen(false); - exit(); - }else - setLoadingState(LoadingState.failure); - }).catch(_ => setLoadingState(LoadingState.failure)); - } - - return <> - { - loadingState == LoadingState.success ? - - Merged Successfully! - - : - - } - setOpen(false)}> - - - Confirm Merge - - - - - - - - - - - ; -} \ No newline at end of file + const [loadingState, setLoadingState] = useState( + LoadingState.none, + ); + + const merge = () => { + setLoadingState(LoadingState.loading); + Api.mangaMergeIntoPartialUpdate( + manga.key as string, + similarManga.key as string, + ) + .then((response) => { + if (response.ok) { + setLoadingState(LoadingState.success); + setOpen(false); + exit(); + } else setLoadingState(LoadingState.failure); + }) + .catch((_) => setLoadingState(LoadingState.failure)); + }; + + return ( + <> + {loadingState == LoadingState.success ? ( + + Merged Successfully! + + ) : ( + + )} + setOpen(false)}> + + + Confirm Merge + + + + + + + + + + + + + + ); +} diff --git a/tranga-website/src/Components/Search.tsx b/tranga-website/src/Components/Search.tsx index 32107fe..5086c88 100644 --- a/tranga-website/src/Components/Search.tsx +++ b/tranga-website/src/Components/Search.tsx @@ -1,138 +1,175 @@ -import {Dispatch, KeyboardEventHandler, ReactNode, useContext, useState} from "react"; import { - Badge, - Button, - Card, - CardContent, - CardCover, Chip, - Input, - Modal, - ModalDialog, - Option, - Select, - Step, - StepIndicator, - Stepper, - Typography + Dispatch, + KeyboardEventHandler, + ReactNode, + useContext, + useState, +} from "react"; +import { + Badge, + Button, + Card, + CardContent, + CardCover, + Chip, + Input, + Modal, + ModalDialog, + Option, + Select, + Step, + StepIndicator, + Stepper, + Typography, } from "@mui/joy"; import ModalClose from "@mui/joy/ModalClose"; -import {MangaConnectorContext} from "../App.tsx"; -import {Manga, MangaConnector} from "../apiClient/data-contracts.ts"; +import { MangaConnectorContext } from "../App.tsx"; +import { Manga, MangaConnector } from "../apiClient/data-contracts.ts"; import MangaList from "./Mangas/MangaList.tsx"; -import {ApiContext} from "../apiClient/ApiContext.tsx"; -import {LoadingState, StateColor, StateIndicator} from "./Loading.tsx"; +import { ApiContext } from "../apiClient/ApiContext.tsx"; +import { LoadingState, StateColor, StateIndicator } from "./Loading.tsx"; -export default function () : ReactNode { - const [open, setOpen] = useState(false); - - return ( - - {if (!open) setOpen(true)}} className={"manga-card"}> - - - - - - Add - - - - - - - ); +export default function (): ReactNode { + const [open, setOpen] = useState(false); + + return ( + + { + if (!open) setOpen(true); + }} + className={"manga-card"} + > + + + + + Add + + + + + + ); } -function SearchDialog ({open, setOpen} : {open: boolean, setOpen: Dispatch}) : ReactNode { - const mangaConnectors = useContext(MangaConnectorContext); - const Api = useContext(ApiContext); - - const [selectedMangaConnector, setSelectedMangaConnector] = useState(undefined); - const [searchTerm, setSearchTerm] = useState(); - const [searchResults, setSearchResults] = useState([]); +function SearchDialog({ + open, + setOpen, +}: { + open: boolean; + setOpen: Dispatch; +}): ReactNode { + const mangaConnectors = useContext(MangaConnectorContext); + const Api = useContext(ApiContext); - const [loadingState, setLoadingState] = useState(LoadingState.none); - - const doTheSearch = () => { - if (searchTerm === undefined || searchTerm.length < 1) - return; - if (!isUrl(searchTerm) && selectedMangaConnector === undefined) - return; - - setLoadingState(LoadingState.loading); - - if (isUrl(searchTerm)) - Api.searchUrlCreate(searchTerm) - .then(response => { - if (response.ok){ - setSearchResults([response.data]); - setLoadingState(LoadingState.success); - }else - setLoadingState(LoadingState.failure); - }).catch(() => setLoadingState(LoadingState.failure)); - else - Api.searchDetail(selectedMangaConnector!.name, searchTerm) - .then(response => { - if(response.ok){ - setSearchResults(response.data); - setLoadingState(LoadingState.success); - }else - setLoadingState(LoadingState.failure); - }).catch(() => setLoadingState(LoadingState.failure)); + const [selectedMangaConnector, setSelectedMangaConnector] = useState< + MangaConnector | undefined + >(undefined); + const [searchTerm, setSearchTerm] = useState(); + const [searchResults, setSearchResults] = useState([]); + + const [loadingState, setLoadingState] = useState( + LoadingState.none, + ); + + const doTheSearch = () => { + if (searchTerm === undefined || searchTerm.length < 1) return; + if (!isUrl(searchTerm) && selectedMangaConnector === undefined) return; + + setLoadingState(LoadingState.loading); + + if (isUrl(searchTerm)) + Api.searchUrlCreate(searchTerm) + .then((response) => { + if (response.ok) { + setSearchResults([response.data]); + setLoadingState(LoadingState.success); + } else setLoadingState(LoadingState.failure); + }) + .catch(() => setLoadingState(LoadingState.failure)); + else + Api.searchDetail(selectedMangaConnector!.name, searchTerm) + .then((response) => { + if (response.ok) { + setSearchResults(response.data); + setLoadingState(LoadingState.success); + } else setLoadingState(LoadingState.failure); + }) + .catch(() => setLoadingState(LoadingState.failure)); + }; + + const isUrl = (url: string) => { + try { + new URL(url); + return true; + } catch (Error) { + return false; } - - const isUrl = (url: string) => { - try{ - new URL(url); - return true; - }catch (Error){ - return false; - } + }; + + const keyDownCheck: KeyboardEventHandler = (e) => { + if (e.key === "Enter") { + doTheSearch(); } - - const keyDownCheck : KeyboardEventHandler = (e) => { - if (e.key === "Enter") { - doTheSearch(); - } - } - - return ( - setOpen(false)}> - - - - 1}> - Connector - - - 2}> - Search - setSearchTerm(e.currentTarget.value)} - endDecorator={} - /> - - 3}> - Result {searchResults.length} - - - - - - ); -} \ No newline at end of file + }; + + return ( + setOpen(false)} + > + + + + 1}> + Connector + + + 2}> + Search + setSearchTerm(e.currentTarget.value)} + endDecorator={ + + } + /> + + 3}> + + Result {searchResults.length} + + + + + + + ); +} diff --git a/tranga-website/src/Components/Settings/AddNotificationConnector.tsx b/tranga-website/src/Components/Settings/AddNotificationConnector.tsx index 46165db..99d437d 100644 --- a/tranga-website/src/Components/Settings/AddNotificationConnector.tsx +++ b/tranga-website/src/Components/Settings/AddNotificationConnector.tsx @@ -1,133 +1,231 @@ -import {ReactNode, useContext, useState} from "react"; +import { ReactNode, useContext, useState } from "react"; import { ApiContext } from "../../apiClient/ApiContext"; import { - Button, - Input, - Modal, - ModalDialog, - Stack, - Tab, - TabList, - TabPanel, - Tabs + Button, + Input, + Modal, + ModalDialog, + Stack, + Tab, + TabList, + TabPanel, + Tabs, } 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"; -export default function ({open, setOpen} : {open: boolean, setOpen: (open: boolean) => void}) { - - return ( - setOpen(false)}> - - - - - Gotify - Ntfy - Pushover - - - - - - - - ); +export default function ({ + open, + setOpen, +}: { + open: boolean; + setOpen: (open: boolean) => void; +}) { + return ( + setOpen(false)}> + + + + + Gotify + Ntfy + Pushover + + + + + + + + ); } -function NotificationConnectorTab({ value, children, add, state }: { value: string, children: ReactNode, add: (data: any) => void, state: LoadingState }) { - - const IsLoading = (state : LoadingState) : boolean => state === LoadingState.loading; - - return ( - - - {children} - - - - ); +function NotificationConnectorTab({ + value, + children, + add, + state, +}: { + value: string; + children: ReactNode; + add: (data: any) => void; + state: LoadingState; +}) { + const IsLoading = (state: LoadingState): boolean => + state === LoadingState.loading; + + return ( + + + {children} + + + + ); } function Gotify() { - const Api = useContext(ApiContext); - const [gotifyData, setGotifyData] = useState({}); - const [loadingState, setLoadingState] = useState(LoadingState.none); + const Api = useContext(ApiContext); + const [gotifyData, setGotifyData] = useState({}); + const [loadingState, setLoadingState] = useState( + LoadingState.none, + ); - const Add = () => { - setLoadingState(LoadingState.loading); - Api.notificationConnectorGotifyUpdate(gotifyData) - .then((response) => { - if (response.ok) - setLoadingState(LoadingState.success); - else - setLoadingState(LoadingState.failure); - }) - .catch(_ => setLoadingState(LoadingState.failure)); - } - - return ( - - setGotifyData({...gotifyData, name: e.target.value})} /> - setGotifyData({...gotifyData, endpoint: e.target.value})} /> - setGotifyData({...gotifyData, appToken: e.target.value})} /> - setGotifyData({...gotifyData, priority: e.target.valueAsNumber})} /> - - ); + const Add = () => { + setLoadingState(LoadingState.loading); + Api.notificationConnectorGotifyUpdate(gotifyData) + .then((response) => { + if (response.ok) setLoadingState(LoadingState.success); + else setLoadingState(LoadingState.failure); + }) + .catch((_) => setLoadingState(LoadingState.failure)); + }; + + return ( + + setGotifyData({ ...gotifyData, name: e.target.value })} + /> + + setGotifyData({ ...gotifyData, endpoint: e.target.value }) + } + /> + + setGotifyData({ ...gotifyData, appToken: e.target.value }) + } + /> + + setGotifyData({ ...gotifyData, priority: e.target.valueAsNumber }) + } + /> + + ); } function Ntfy() { - const Api = useContext(ApiContext); - const [ntfyData, setNtfyData] = useState({}); - const [loadingState, setLoadingState] = useState(LoadingState.none); + const Api = useContext(ApiContext); + const [ntfyData, setNtfyData] = useState({}); + const [loadingState, setLoadingState] = useState( + LoadingState.none, + ); - const Add = () => { - setLoadingState(LoadingState.loading); - Api.notificationConnectorNtfyUpdate(ntfyData) - .then((response) => { - if (response.ok) - setLoadingState(LoadingState.success); - else - setLoadingState(LoadingState.failure); - }) - .catch(_ => setLoadingState(LoadingState.failure)); - } + const Add = () => { + setLoadingState(LoadingState.loading); + Api.notificationConnectorNtfyUpdate(ntfyData) + .then((response) => { + if (response.ok) setLoadingState(LoadingState.success); + else setLoadingState(LoadingState.failure); + }) + .catch((_) => setLoadingState(LoadingState.failure)); + }; - return ( - - setNtfyData({...ntfyData, name: e.target.value})} /> - setNtfyData({...ntfyData, endpoint: e.target.value})} /> - setNtfyData({...ntfyData, topic: e.target.value})} /> - setNtfyData({...ntfyData, username: e.target.value})} /> - setNtfyData({...ntfyData, password: e.target.value})} /> - setNtfyData({...ntfyData, priority: e.target.valueAsNumber})} /> - - ); + return ( + + setNtfyData({ ...ntfyData, name: e.target.value })} + /> + setNtfyData({ ...ntfyData, endpoint: e.target.value })} + /> + setNtfyData({ ...ntfyData, topic: e.target.value })} + /> + setNtfyData({ ...ntfyData, username: e.target.value })} + /> + setNtfyData({ ...ntfyData, password: e.target.value })} + /> + + setNtfyData({ ...ntfyData, priority: e.target.valueAsNumber }) + } + /> + + ); } function Pushover() { - const Api = useContext(ApiContext); - const [pushoverData, setPushoverData] = useState({}); - const [loadingState, setLoadingState] = useState(LoadingState.none); - - const Add = () => { - setLoadingState(LoadingState.loading); - Api.notificationConnectorPushoverUpdate(pushoverData) - .then((response) => { - if (response.ok) - setLoadingState(LoadingState.success); - else - setLoadingState(LoadingState.failure); - }) - .catch(_ => setLoadingState(LoadingState.failure)); - } + const Api = useContext(ApiContext); + const [pushoverData, setPushoverData] = useState({}); + const [loadingState, setLoadingState] = useState( + LoadingState.none, + ); - return ( - - setPushoverData({...pushoverData, name: e.target.value})} /> - setPushoverData({...pushoverData, user: e.target.value})} /> - setPushoverData({...pushoverData, appToken: e.target.value})} /> - - ); -} \ No newline at end of file + const Add = () => { + setLoadingState(LoadingState.loading); + Api.notificationConnectorPushoverUpdate(pushoverData) + .then((response) => { + if (response.ok) setLoadingState(LoadingState.success); + else setLoadingState(LoadingState.failure); + }) + .catch((_) => setLoadingState(LoadingState.failure)); + }; + + return ( + + + setPushoverData({ ...pushoverData, name: e.target.value }) + } + /> + + setPushoverData({ ...pushoverData, user: e.target.value }) + } + /> + + setPushoverData({ ...pushoverData, appToken: e.target.value }) + } + /> + + ); +} diff --git a/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx b/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx index 2109c90..2c36240 100644 --- a/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx +++ b/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx @@ -1,35 +1,43 @@ -import {ReactNode, useContext, useState} from "react"; -import {SettingsContext, SettingsItem} from "./Settings.tsx"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import {ColorPaletteProp, Input} from "@mui/joy"; +import { ReactNode, useContext, useState } from "react"; +import { SettingsContext, SettingsItem } from "./Settings.tsx"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; +import { ColorPaletteProp, Input } from "@mui/joy"; import * as React from "react"; import MarkdownPreview from "@uiw/react-markdown-preview"; -export default function () : ReactNode { - const settings = useContext(SettingsContext); - const Api = useContext(ApiContext); +export default function (): ReactNode { + const settings = useContext(SettingsContext); + const Api = useContext(ApiContext); - const [scheme, setScheme] = useState("neutral"); - const timerRef = React.useRef>(undefined); - const schemeChanged = (e : React.ChangeEvent) => { - clearTimeout(timerRef.current); - setScheme("warning"); - timerRef.current = setTimeout(() => { - Api.settingsChapterNamingSchemePartialUpdate(e.target.value) - .then(response => { - if (response.ok) - setScheme("success"); - else - setScheme("danger"); - }) - .catch(() => setScheme("danger")); - }, 1000); - } + const [scheme, setScheme] = useState("neutral"); + const timerRef = React.useRef>(undefined); + const schemeChanged = (e: React.ChangeEvent) => { + clearTimeout(timerRef.current); + setScheme("warning"); + timerRef.current = setTimeout(() => { + Api.settingsChapterNamingSchemePartialUpdate(e.target.value) + .then((response) => { + if (response.ok) setScheme("success"); + else setScheme("danger"); + }) + .catch(() => setScheme("danger")); + }, 1000); + }; - return ( - - - - - ); -} \ No newline at end of file + return ( + + + + + ); +} diff --git a/tranga-website/src/Components/Settings/DownloadLanguage.tsx b/tranga-website/src/Components/Settings/DownloadLanguage.tsx index 1a81994..bd5dd0b 100644 --- a/tranga-website/src/Components/Settings/DownloadLanguage.tsx +++ b/tranga-website/src/Components/Settings/DownloadLanguage.tsx @@ -1,33 +1,36 @@ -import {ReactNode, useContext, useState} from "react"; -import {SettingsContext, SettingsItem} from "./Settings.tsx"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import {ColorPaletteProp, Input} from "@mui/joy"; +import { ReactNode, useContext, useState } from "react"; +import { SettingsContext, SettingsItem } from "./Settings.tsx"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; +import { ColorPaletteProp, Input } from "@mui/joy"; import * as React from "react"; -export default function () : ReactNode { - const settings = useContext(SettingsContext); - const Api = useContext(ApiContext); +export default function (): ReactNode { + const settings = useContext(SettingsContext); + const Api = useContext(ApiContext); - const [color, setColor] = useState("neutral"); - const timerRef = React.useRef>(undefined); - const languageChanged = (e : React.ChangeEvent) => { - clearTimeout(timerRef.current); - setColor("warning"); - timerRef.current = setTimeout(() => { - Api.settingsDownloadLanguagePartialUpdate(e.target.value) - .then(response => { - if (response.ok) - setColor("success"); - else - setColor("danger"); - }) - .catch(() => setColor("danger")); - }, 1000); - } + const [color, setColor] = useState("neutral"); + const timerRef = React.useRef>(undefined); + const languageChanged = (e: React.ChangeEvent) => { + clearTimeout(timerRef.current); + setColor("warning"); + timerRef.current = setTimeout(() => { + Api.settingsDownloadLanguagePartialUpdate(e.target.value) + .then((response) => { + if (response.ok) setColor("success"); + else setColor("danger"); + }) + .catch(() => setColor("danger")); + }, 1000); + }; - return ( - - - - ); -} \ No newline at end of file + return ( + + + + ); +} diff --git a/tranga-website/src/Components/Settings/FlareSolverr.tsx b/tranga-website/src/Components/Settings/FlareSolverr.tsx index 01392b7..7adf9a5 100644 --- a/tranga-website/src/Components/Settings/FlareSolverr.tsx +++ b/tranga-website/src/Components/Settings/FlareSolverr.tsx @@ -1,33 +1,37 @@ -import {ReactNode, useContext, useState} from "react"; -import {SettingsContext, SettingsItem} from "./Settings.tsx"; -import {ColorPaletteProp, Input} from "@mui/joy"; +import { ReactNode, useContext, useState } from "react"; +import { SettingsContext, SettingsItem } from "./Settings.tsx"; +import { ColorPaletteProp, Input } from "@mui/joy"; import * as React from "react"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; -export default function () : ReactNode { - const settings = useContext(SettingsContext); - const Api = useContext(ApiContext); +export default function (): ReactNode { + const settings = useContext(SettingsContext); + const Api = useContext(ApiContext); - const [uriColor, setUriColor] = useState("neutral"); - const timerRef = React.useRef>(undefined); - const uriChanged = (e : React.ChangeEvent) => { - clearTimeout(timerRef.current); - setUriColor("warning"); - timerRef.current = setTimeout(() => { - Api.settingsFlareSolverrUrlCreate(e.target.value) - .then(response => { - if (response.ok) - setUriColor("success"); - else - setUriColor("danger"); - }) - .catch(() => setUriColor("danger")); - }, 1000); - } - - return ( - - - - ); -} \ No newline at end of file + const [uriColor, setUriColor] = useState("neutral"); + const timerRef = React.useRef>(undefined); + const uriChanged = (e: React.ChangeEvent) => { + clearTimeout(timerRef.current); + setUriColor("warning"); + timerRef.current = setTimeout(() => { + Api.settingsFlareSolverrUrlCreate(e.target.value) + .then((response) => { + if (response.ok) setUriColor("success"); + else setUriColor("danger"); + }) + .catch(() => setUriColor("danger")); + }, 1000); + }; + + return ( + + + + ); +} diff --git a/tranga-website/src/Components/Settings/ImageCompression.tsx b/tranga-website/src/Components/Settings/ImageCompression.tsx index 01e9d85..0732171 100644 --- a/tranga-website/src/Components/Settings/ImageCompression.tsx +++ b/tranga-website/src/Components/Settings/ImageCompression.tsx @@ -1,13 +1,17 @@ -import {ReactNode, useContext} from "react"; -import {SettingsContext, SettingsItem} from "./Settings.tsx"; -import {Slider} from "@mui/joy"; +import { ReactNode, useContext } from "react"; +import { SettingsContext, SettingsItem } from "./Settings.tsx"; +import { Slider } from "@mui/joy"; -export default function () : ReactNode { - const settings = useContext(SettingsContext); - - return ( - - - - ); -} \ No newline at end of file +export default function (): ReactNode { + const settings = useContext(SettingsContext); + + return ( + + + + ); +} diff --git a/tranga-website/src/Components/Settings/Maintenance.tsx b/tranga-website/src/Components/Settings/Maintenance.tsx index 729dd2b..525e8b0 100644 --- a/tranga-website/src/Components/Settings/Maintenance.tsx +++ b/tranga-website/src/Components/Settings/Maintenance.tsx @@ -1,33 +1,33 @@ -import {Button} from "@mui/joy"; -import {SettingsItem} from "./Settings.tsx"; -import {useContext, useState} from "react"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import {LoadingState, StateColor, StateIndicator} from "../Loading.tsx"; +import { Button } from "@mui/joy"; +import { SettingsItem } from "./Settings.tsx"; +import { useContext, useState } from "react"; +import { ApiContext } from "../../apiClient/ApiContext.tsx"; +import { LoadingState, StateColor, StateIndicator } from "../Loading.tsx"; export default function () { - const Api = useContext(ApiContext); - - const [unusedMangaState, setUnusedMangaState] = useState(LoadingState.none); - const cleanUnusedManga = () => { - setUnusedMangaState(LoadingState.loading); - Api.maintenanceCleanupNoDownloadMangaCreate() - .then(r => { - if (r.ok) - setUnusedMangaState(LoadingState.success); - else - setUnusedMangaState(LoadingState.failure); - }).catch(_ => setUnusedMangaState(LoadingState.failure)); - } - - - return ( + const Api = useContext(ApiContext); - - - - ); -} \ No newline at end of file + const [unusedMangaState, setUnusedMangaState] = useState(LoadingState.none); + const cleanUnusedManga = () => { + setUnusedMangaState(LoadingState.loading); + Api.maintenanceCleanupNoDownloadMangaCreate() + .then((r) => { + if (r.ok) setUnusedMangaState(LoadingState.success); + else setUnusedMangaState(LoadingState.failure); + }) + .catch((_) => setUnusedMangaState(LoadingState.failure)); + }; + + return ( + + + + ); +} diff --git a/tranga-website/src/Components/Settings/NotificationConnectors.tsx b/tranga-website/src/Components/Settings/NotificationConnectors.tsx index fc030a4..c363fba 100644 --- a/tranga-website/src/Components/Settings/NotificationConnectors.tsx +++ b/tranga-website/src/Components/Settings/NotificationConnectors.tsx @@ -1,16 +1,22 @@ -import {ReactNode} from "react"; -import {SettingsItem} from "./Settings.tsx"; -import {Button} from "@mui/joy"; +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 ( - - - - - ); -} \ No newline at end of file +export default function (): ReactNode { + const [notificationConnectorsOpen, setNotificationConnectorsOpen] = + React.useState(false); + + return ( + + + + + ); +} diff --git a/tranga-website/src/Components/Settings/Settings.tsx b/tranga-website/src/Components/Settings/Settings.tsx index 2ee4ffa..2e2ce81 100644 --- a/tranga-website/src/Components/Settings/Settings.tsx +++ b/tranga-website/src/Components/Settings/Settings.tsx @@ -1,95 +1,117 @@ -import ModalClose from '@mui/joy/ModalClose'; +import ModalClose from "@mui/joy/ModalClose"; import { - Accordion, - AccordionDetails, - AccordionGroup, - AccordionSummary, Button, ColorPaletteProp, - DialogContent, - DialogTitle, Input, - Modal, ModalDialog + Accordion, + AccordionDetails, + AccordionGroup, + AccordionSummary, + Button, + ColorPaletteProp, + DialogContent, + DialogTitle, + Input, + Modal, + ModalDialog, } from "@mui/joy"; -import './Settings.css'; +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 { + 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 { 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"; -export const SettingsContext = createContext(undefined); +export const SettingsContext = createContext( + undefined, +); -export default function Settings({setApiUri} : {setApiUri: Dispatch>}) { - const Api = useContext(ApiContext); - const [settings, setSettings] = useState(); +export default function Settings({ + setApiUri, +}: { + setApiUri: Dispatch>; +}) { + const Api = useContext(ApiContext); + const [settings, setSettings] = useState(); - const [open, setOpen] = React.useState(false); - - const [apiUriColor, setApiUriColor] = useState("neutral"); - const timerRef = React.useRef>(undefined); + const [open, setOpen] = React.useState(false); - useEffect(() => { - Api.settingsList().then((response) => { - setSettings(response.data); - }); - }, []); + const [apiUriColor, setApiUriColor] = useState("neutral"); + const timerRef = React.useRef>(undefined); - const apiUriChanged = (e : React.ChangeEvent) => { - clearTimeout(timerRef.current); - setApiUriColor("warning"); - timerRef.current = setTimeout(() => { - setApiUri(e.target.value); - setApiUriColor("success"); - }, 1000); - } - - const ModalStyle : SxProps = { - width: "80%", - height: "80%" - } - - return ( - - - setOpen(false)}> - - - Settings - - - - - - - - - - - - - - - - - ); + useEffect(() => { + Api.settingsList().then((response) => { + setSettings(response.data); + }); + }, []); + + const apiUriChanged = (e: React.ChangeEvent) => { + clearTimeout(timerRef.current); + setApiUriColor("warning"); + timerRef.current = setTimeout(() => { + setApiUri(e.target.value); + setApiUriColor("success"); + }, 1000); + }; + + const ModalStyle: SxProps = { + width: "80%", + height: "80%", + }; + + return ( + + + setOpen(false)}> + + + Settings + + + + + + + + + + + + + + + + + ); } -export function SettingsItem({title, children} : {title: string, children: ReactNode}) { - return ( - - {title} - - {children} - - - ); -} \ No newline at end of file +export function SettingsItem({ + title, + children, +}: { + title: string; + children: ReactNode; +}) { + return ( + + {title} + {children} + + ); +} diff --git a/tranga-website/src/Header.css b/tranga-website/src/Header.css index 131b3a9..6d01676 100644 --- a/tranga-website/src/Header.css +++ b/tranga-website/src/Header.css @@ -1,11 +1,11 @@ .header { - position: sticky !important; - z-index: 1000; - top: 0; - left: 0; - width: 100%; - height: 60px; - padding: 10px; - display: flex; - flex-flow: row nowrap; -} \ No newline at end of file + position: sticky !important; + z-index: 1000; + top: 0; + left: 0; + width: 100%; + height: 60px; + padding: 10px; + display: flex; + flex-flow: row nowrap; +} diff --git a/tranga-website/src/Header.tsx b/tranga-website/src/Header.tsx index d28bea9..aad86d3 100644 --- a/tranga-website/src/Header.tsx +++ b/tranga-website/src/Header.tsx @@ -1,35 +1,91 @@ import Sheet from "@mui/joy/Sheet"; -import {Link, Stack, Typography} from "@mui/joy"; -import {ReactElement, useContext} from "react"; -import './Header.css'; -import {Article, GitHub} from "@mui/icons-material"; -import {ApiContext} from "./apiClient/ApiContext.tsx"; +import { Link, Stack, Typography } from "@mui/joy"; +import { ReactElement, useContext } from "react"; +import "./Header.css"; +import { Article, GitHub } from "@mui/icons-material"; +import { ApiContext } from "./apiClient/ApiContext.tsx"; -export default function Header({children} : {children? : ReactElement | ReactElement[] | undefined}) : ReactElement { - const Api = useContext(ApiContext); - - return ( - - - - {children} - - - - Tranga - - - Server - Website -
Swagger - - - - ); -} \ No newline at end of file +export default function Header({ + children, +}: { + children?: ReactElement | ReactElement[] | undefined; +}): ReactElement { + const Api = useContext(ApiContext); + + return ( + + + + {children} + + + + + Tranga + + + + + Server + + + Website + + +
+ Swagger + + + + + ); +} diff --git a/tranga-website/src/apiClient/ApiContext.tsx b/tranga-website/src/apiClient/ApiContext.tsx index 46ca069..930c76b 100644 --- a/tranga-website/src/apiClient/ApiContext.tsx +++ b/tranga-website/src/apiClient/ApiContext.tsx @@ -1,4 +1,4 @@ import { createContext } from "react"; -import {V2} from "./V2.ts"; +import { V2 } from "./V2.ts"; -export const ApiContext = createContext(new V2()); \ No newline at end of file +export const ApiContext = createContext(new V2()); diff --git a/tranga-website/src/apiClient/SettingsContext.tsx b/tranga-website/src/apiClient/SettingsContext.tsx index dd2097b..d893484 100644 --- a/tranga-website/src/apiClient/SettingsContext.tsx +++ b/tranga-website/src/apiClient/SettingsContext.tsx @@ -1,31 +1,32 @@ -import {createContext, useContext, useState} from "react"; -import {TrangaSettings} from "./data-contracts.ts"; -import {ApiContext} from "./ApiContext.tsx"; +import { createContext, useContext, useState } from "react"; +import { TrangaSettings } from "./data-contracts.ts"; +import { ApiContext } from "./ApiContext.tsx"; -const [settingsPromise, setSettingsPromise] = useState>(); +const [settingsPromise, setSettingsPromise] = + useState>(); const [settings, setSettings] = useState(); const API = useContext(ApiContext); -export const SettingsContext = createContext<{ GetSettings: () => Promise }>( - { - GetSettings: () : Promise => { - const promise = settingsPromise; - if(promise) return promise; - const p = new Promise((resolve, reject) => { - if (settings) resolve(settings); +export const SettingsContext = createContext<{ + GetSettings: () => Promise; +}>({ + GetSettings: (): Promise => { + const promise = settingsPromise; + if (promise) return promise; + const p = new Promise((resolve, reject) => { + if (settings) resolve(settings); - console.log(`Fetching settings`); - API.settingsList() - .then(result => { - if (!result.ok) - throw new Error(`Error fetching settings`); - setSettings(result.data); - resolve(result.data); - }).catch(reject); - }); - setSettingsPromise(p); - return p; - } - } -); \ No newline at end of file + console.log(`Fetching settings`); + API.settingsList() + .then((result) => { + if (!result.ok) throw new Error(`Error fetching settings`); + setSettings(result.data); + resolve(result.data); + }) + .catch(reject); + }); + setSettingsPromise(p); + return p; + }, +}); diff --git a/tranga-website/src/main.tsx b/tranga-website/src/main.tsx index 213d9f5..6bc3304 100644 --- a/tranga-website/src/main.tsx +++ b/tranga-website/src/main.tsx @@ -1,29 +1,25 @@ -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.tsx"; // @ts-ignore -import '@fontsource/inter'; -import { CssVarsProvider } from '@mui/joy/styles'; -import CssBaseline from '@mui/joy/CssBaseline'; -import {StrictMode} from "react"; -import {trangaTheme} from "./theme.ts"; +import "@fontsource/inter"; +import { CssVarsProvider } from "@mui/joy/styles"; +import CssBaseline from "@mui/joy/CssBaseline"; +import { StrictMode } from "react"; +import { trangaTheme } from "./theme.ts"; export default function MyApp() { - return ( - - - {/* must be used under CssVarsProvider */} - + return ( + + + {/* must be used under CssVarsProvider */} + - {/* The rest of your application */} - - - - ); + {/* The rest of your application */} + + + + ); } - - -createRoot(document.getElementById('root')!).render( - -); +createRoot(document.getElementById("root")!).render(); diff --git a/tranga-website/src/theme.ts b/tranga-website/src/theme.ts index c84e93b..ab0e981 100644 --- a/tranga-website/src/theme.ts +++ b/tranga-website/src/theme.ts @@ -1,88 +1,87 @@ -import { extendTheme } from '@mui/joy/styles'; - +import { extendTheme } from "@mui/joy/styles"; export const trangaTheme = extendTheme({ - "colorSchemes": { - "light": { - "palette": { - "primary": { - "50": "#FCE5EA", - "100": "#FBDDE3", - "200": "#F9CBD4", - "300": "#F7BAC6", - "400": "#F5A9B8", - "500": "#F5A9B8", - "600": "#C48793", - "700": "#AC7681", - "800": "#93656E", - "900": "#7B555C" - }, - "neutral": { - "50": "#E6E6E6", - "100": "#CCCCCC", - "200": "#B3B3B3", - "300": "#999999", - "400": "#808080", - "500": "#666666", - "600": "#4C4C4C", - "700": "#333333", - "800": "#191919", - "900": "#000", - "plainColor": "var(--joy-palette-neutral-50)", - "plainHoverBg": "var(--joy-palette-neutral-700)", - "outlinedColor": "var(--joy-palette-neutral-50)", - }, - "success": { - "50": "#cef0fe", - "100": "#bdebfd", - "200": "#9de2fc", - "300": "#7cd8fb", - "400": "#5bcefa", - "500": "#5bcefa", - "600": "#49a5c8", - "700": "#4090af", - "800": "#2e677d", - "900": "#245264" - }, - "danger": { - "50": "#f2c0b3", - "100": "#ea9680", - "200": "#e68166", - "300": "#dd5733", - "400": "#d52d00", - "500": "#d52d00", - "600": "#aa2400", - "700": "#951f00", - "800": "#6b1700", - "900": "#400d00" - }, - "warning": { - "50": "#ffebdd", - "100": "#ffd7bb", - "200": "#ffc29a", - "300": "#ffae78", - "400": "#ff9a56", - "500": "#ff9a56", - "600": "#cc7b45", - "700": "#995c34", - "800": "#663e22", - "900": "#331f11" - }, - "background": { - "body": "var(--joy-palette-neutral-900)", - "surface": "var(--joy-palette-neutral-900)", - "popup": "var(--joy-palette-neutral-800)" - }, - "text": { - "primary": "var(--joy-palette-neutral-50)", - "secondary": "var(--joy-palette-success-200)", - "tertiary": "var(--joy-palette-primary-200)", - "icon": "var(--joy-palette-primary-50)" - } - } + colorSchemes: { + light: { + palette: { + primary: { + "50": "#FCE5EA", + "100": "#FBDDE3", + "200": "#F9CBD4", + "300": "#F7BAC6", + "400": "#F5A9B8", + "500": "#F5A9B8", + "600": "#C48793", + "700": "#AC7681", + "800": "#93656E", + "900": "#7B555C", }, - "dark": { - "palette": {} - } - } -}) + neutral: { + "50": "#E6E6E6", + "100": "#CCCCCC", + "200": "#B3B3B3", + "300": "#999999", + "400": "#808080", + "500": "#666666", + "600": "#4C4C4C", + "700": "#333333", + "800": "#191919", + "900": "#000", + plainColor: "var(--joy-palette-neutral-50)", + plainHoverBg: "var(--joy-palette-neutral-700)", + outlinedColor: "var(--joy-palette-neutral-50)", + }, + success: { + "50": "#cef0fe", + "100": "#bdebfd", + "200": "#9de2fc", + "300": "#7cd8fb", + "400": "#5bcefa", + "500": "#5bcefa", + "600": "#49a5c8", + "700": "#4090af", + "800": "#2e677d", + "900": "#245264", + }, + danger: { + "50": "#f2c0b3", + "100": "#ea9680", + "200": "#e68166", + "300": "#dd5733", + "400": "#d52d00", + "500": "#d52d00", + "600": "#aa2400", + "700": "#951f00", + "800": "#6b1700", + "900": "#400d00", + }, + warning: { + "50": "#ffebdd", + "100": "#ffd7bb", + "200": "#ffc29a", + "300": "#ffae78", + "400": "#ff9a56", + "500": "#ff9a56", + "600": "#cc7b45", + "700": "#995c34", + "800": "#663e22", + "900": "#331f11", + }, + background: { + body: "var(--joy-palette-neutral-900)", + surface: "var(--joy-palette-neutral-900)", + popup: "var(--joy-palette-neutral-800)", + }, + text: { + primary: "var(--joy-palette-neutral-50)", + secondary: "var(--joy-palette-success-200)", + tertiary: "var(--joy-palette-primary-200)", + icon: "var(--joy-palette-primary-50)", + }, + }, + }, + dark: { + palette: {}, + }, + }, +}); diff --git a/tranga-website/vite.config.ts b/tranga-website/vite.config.ts index eb687e3..ecbc44e 100644 --- a/tranga-website/vite.config.ts +++ b/tranga-website/vite.config.ts @@ -1,10 +1,10 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; // https://vite.dev/config/ export default defineConfig({ plugins: [react()], server: { - host: '127.0.0.1', - } -}) + host: "127.0.0.1", + }, +});