mirror of
https://github.com/C9Glax/tranga-website.git
synced 2025-06-12 14:57:53 +02:00
Created standardized Popup-Window
Moved Update-functions for Queue-Status and Monitoring-list to their respective modules
This commit is contained in:
@ -3,12 +3,14 @@ import '../styles/footer.css';
|
||||
import {Job} from './Job';
|
||||
import Icon from '@mdi/react';
|
||||
import { mdiRun, mdiCounter, mdiEyeCheck, mdiTrayFull } from '@mdi/js';
|
||||
import QueuePopUp from "./QueuePopUp";
|
||||
|
||||
export default function Footer({showQueue} : {showQueue(): void}){
|
||||
export default function Footer({connectedToBackend} : {connectedToBackend: boolean}) {
|
||||
const [MonitoringJobsCount, setMonitoringJobsCount] = React.useState(0);
|
||||
const [AllJobsCount, setAllJobsCount] = React.useState(0);
|
||||
const [RunningJobsCount, setRunningJobsCount] = React.useState(0);
|
||||
const [StandbyJobsCount, setStandbyJobsCount] = React.useState(0);
|
||||
const [countUpdateInterval, setcountUpdateInterval] = React.useState<number>();
|
||||
|
||||
function UpdateBackendState(){
|
||||
Job.GetMonitoringJobs().then((jobs) => setMonitoringJobsCount(jobs.length));
|
||||
@ -18,15 +20,25 @@ export default function Footer({showQueue} : {showQueue(): void}){
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
UpdateBackendState();
|
||||
}, []);
|
||||
if(connectedToBackend){
|
||||
UpdateBackendState();
|
||||
setcountUpdateInterval(setInterval(() => {
|
||||
UpdateBackendState();
|
||||
}, 2000));
|
||||
}else{
|
||||
clearInterval(countUpdateInterval);
|
||||
setcountUpdateInterval(undefined);
|
||||
}
|
||||
}, [connectedToBackend]);
|
||||
|
||||
return (
|
||||
<footer>
|
||||
<div><Icon path={mdiEyeCheck} size={1}/> <span>{MonitoringJobsCount}</span></div>
|
||||
<div onClick={showQueue} className="hoverHand"><Icon path={mdiTrayFull} size={1}/> <span>{StandbyJobsCount}</span></div>
|
||||
<div onClick={showQueue} className="hoverHand"><Icon path={mdiRun} size={1}/> <span>{RunningJobsCount}</span></div>
|
||||
<div><Icon path={mdiCounter} size={1}/> <span>{AllJobsCount}</span></div>
|
||||
<div className="statusBadge"><Icon path={mdiEyeCheck} size={1}/> <span>{MonitoringJobsCount}</span></div>
|
||||
<QueuePopUp>
|
||||
<div className="statusBadge hoverHand"><Icon path={mdiTrayFull} size={1}/> <span>{StandbyJobsCount}</span></div>
|
||||
<div className="statusBadge hoverHand"><Icon path={mdiRun} size={1}/> <span>{RunningJobsCount}</span></div>
|
||||
</QueuePopUp>
|
||||
<div className="statusBadge"><Icon path={mdiCounter} size={1}/> <span>{AllJobsCount}</span></div>
|
||||
<p id="madeWith">Made with Blåhaj 🦈</p>
|
||||
</footer>)
|
||||
}
|
@ -4,6 +4,10 @@ import IProgressToken from "./interfaces/IProgressToken";
|
||||
|
||||
export class Job
|
||||
{
|
||||
static IntervalStringFromDate(date: Date) : string {
|
||||
return `${date.getDay()}.${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
|
||||
}
|
||||
|
||||
static async GetAllJobs(): Promise<string[]> {
|
||||
console.info("Getting all Jobs");
|
||||
return getData("http://127.0.0.1:6531/v2/Jobs")
|
||||
@ -101,9 +105,18 @@ export class Job
|
||||
});
|
||||
}
|
||||
|
||||
static async CreateJobDateInterval(internalId: string, jobType: string, interval: Date) : Promise<null> {
|
||||
return this.CreateJob(internalId, jobType, this.IntervalStringFromDate(interval));
|
||||
}
|
||||
|
||||
static async CreateJob(internalId: string, jobType: string, interval: string): Promise<null> {
|
||||
const validate = /(?:[0-9]{1,2}\.)?[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?/
|
||||
console.info(`Creating Job for Manga ${internalId} at ${interval} interval`);
|
||||
let data = {
|
||||
if(!validate.test(interval)){
|
||||
console.error("Interval was in incorrect format.");
|
||||
return Promise.reject();
|
||||
}
|
||||
const data = {
|
||||
internalId: internalId,
|
||||
interval: interval
|
||||
};
|
||||
|
@ -8,9 +8,10 @@ import '../styles/MangaCoverCard.css'
|
||||
import Icon from '@mdi/react';
|
||||
import { mdiTrashCanOutline, mdiPlayBoxOutline } from '@mdi/js';
|
||||
|
||||
export default function MonitorJobsList({onStartSearch, onJobsChanged} : {onStartSearch() : void, onJobsChanged: EventHandler<any>}) {
|
||||
export default function MonitorJobsList({onStartSearch, onJobsChanged, connectedToBackend} : {onStartSearch() : void, onJobsChanged: EventHandler<any>, connectedToBackend: boolean}) {
|
||||
const [MonitoringJobs, setMonitoringJobs] = useState<IJob[]>([]);
|
||||
const [AllManga, setAllManga] = useState<IManga[]>([]);
|
||||
const [joblistUpdateInterval, setJoblistUpdateInterval] = React.useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
console.debug("Updating display list.");
|
||||
@ -28,8 +29,16 @@ export default function MonitorJobsList({onStartSearch, onJobsChanged} : {onStar
|
||||
}, [MonitoringJobs]);
|
||||
|
||||
useEffect(() => {
|
||||
UpdateMonitoringJobsList();
|
||||
}, []);
|
||||
if(connectedToBackend){
|
||||
UpdateMonitoringJobsList();
|
||||
setJoblistUpdateInterval(setInterval(() => {
|
||||
UpdateMonitoringJobsList();
|
||||
}, 1000));
|
||||
}else{
|
||||
clearInterval(joblistUpdateInterval);
|
||||
setJoblistUpdateInterval(undefined);
|
||||
}
|
||||
}, [connectedToBackend]);
|
||||
|
||||
function UpdateMonitoringJobsList(){
|
||||
console.debug("Updating MonitoringJobsList");
|
||||
|
@ -1,16 +1,18 @@
|
||||
import React, {ReactElement, useEffect} from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import IJob, {JobTypeFromNumber} from "./interfaces/IJob";
|
||||
import '../styles/queuePopUp.css';
|
||||
import '../styles/popup.css';
|
||||
import {Job} from "./Job";
|
||||
import IManga from "./interfaces/IManga";
|
||||
import {Manga} from "./Manga";
|
||||
|
||||
export default function QueuePopUp({closeQueue} : {closeQueue(): void}){
|
||||
export default function QueuePopUp({children} : {children: JSX.Element[]}) {
|
||||
|
||||
const [StandbyJobs, setStandbyJobs] = React.useState<IJob[]>([]);
|
||||
const [StandbyJobsManga, setStandbyJobsManga] = React.useState<IManga[]>([]);
|
||||
const [RunningJobs, setRunningJobs] = React.useState<IJob[]>([]);
|
||||
const [RunningJobsManga, setRunningJobsManga] = React.useState<IManga[]>([]);
|
||||
const [showQueuePopup, setShowQueuePopup] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
Job.GetStandbyJobs()
|
||||
@ -56,45 +58,54 @@ export default function QueuePopUp({closeQueue} : {closeQueue(): void}){
|
||||
.then(setRunningJobsManga);
|
||||
}, [RunningJobs]);
|
||||
|
||||
return (
|
||||
<div id="QueuePopUp">
|
||||
<div id="QueuePopUpHeader">
|
||||
<h1>Queue Status</h1>
|
||||
<img alt="Close Search" id="closeSearch" src="../media/close-x.svg" onClick={closeQueue}/>
|
||||
return (<>
|
||||
<div onClick={() => setShowQueuePopup(true)}>
|
||||
{children}
|
||||
</div>
|
||||
<div id="QueuePopUpBody">
|
||||
<div id="RunningJobQueue">
|
||||
<h1>Running</h1>
|
||||
<div className="JobQueue">
|
||||
{RunningJobs.map((job: IJob) => {
|
||||
const manga = RunningJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId);
|
||||
if (manga === undefined || manga === null)
|
||||
return <div key={"QueueJob-" + job.id}>Error. Could not find matching manga for {job.id}</div>
|
||||
return <div className="QueueJob" key={"QueueJob-" + job.id}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)} />
|
||||
<p>{JobTypeFromNumber(job.jobType)}</p>
|
||||
</div>;
|
||||
})}
|
||||
{showQueuePopup
|
||||
? <div className="popup" id="QueuePopUp">
|
||||
<div className="popupHeader">
|
||||
<h1>Queue Status {showQueuePopup ? "true" : "false"}</h1>
|
||||
<img alt="Close Search" className="close" src="../media/close-x.svg"
|
||||
onClick={() => setShowQueuePopup(false)}/>
|
||||
</div>
|
||||
<div id="QueuePopUpBody" className="popupBody">
|
||||
<div id="RunningJobQueue">
|
||||
<h1>Running</h1>
|
||||
<div className="JobQueue">
|
||||
{RunningJobs.map((job: IJob) => {
|
||||
const manga = RunningJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId);
|
||||
if (manga === undefined || manga === null)
|
||||
return <div key={"QueueJob-" + job.id}>Error. Could not find matching manga
|
||||
for {job.id}</div>
|
||||
return <div className="QueueJob" key={"QueueJob-" + job.id}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)}/>
|
||||
<p>{JobTypeFromNumber(job.jobType)}</p>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div id="WaitingJobQueue">
|
||||
<h1>Standby</h1>
|
||||
<div className="JobQueue">
|
||||
{StandbyJobs.map((job: IJob) => {
|
||||
const manga = StandbyJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId);
|
||||
if (manga === undefined || manga === null)
|
||||
return <div key={"QueueJob-" + job.id}>Error. Could not find matching manga
|
||||
for {job.id}</div>
|
||||
return <div className="QueueJob" key={"QueueJob-" + job.id}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)}/>
|
||||
<p className="QueueJob-Name">{manga.sortName}</p>
|
||||
<p className="QueueJob-JobType">{JobTypeFromNumber(job.jobType)}</p>
|
||||
<p className="QueueJob-additional">{job.jobType == 0 ? `Vol.${job.chapter?.volumeNumber} Ch.${job.chapter?.chapterNumber}` : ""}</p>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="WaitingJobQueue">
|
||||
<h1>Standby</h1>
|
||||
<div className="JobQueue">
|
||||
{StandbyJobs.map((job: IJob) => {
|
||||
const manga = StandbyJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId);
|
||||
if (manga === undefined || manga === null)
|
||||
return <div key={"QueueJob-" + job.id}>Error. Could not find matching manga
|
||||
for {job.id}</div>
|
||||
return <div className="QueueJob" key={"QueueJob-" + job.id}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)}/>
|
||||
<p className="QueueJob-Name">{manga.sortName}</p>
|
||||
<p className="QueueJob-JobType">{JobTypeFromNumber(job.jobType)}</p>
|
||||
<p className="QueueJob-additional">{job.jobType == 0 ? `Vol.${job.chapter?.volumeNumber} Ch.${job.chapter?.chapterNumber}` : ""}</p>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: <></>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
@ -6,7 +6,7 @@ import IManga, {SearchResult} from "./interfaces/IManga";
|
||||
import '../styles/search.css';
|
||||
import '../styles/MangaSearchResult.css'
|
||||
|
||||
export default function Search({onJobsChanged, closeSearch} : {onJobsChanged: EventHandler<any>, closeSearch(): void}) {
|
||||
export default function Search({createJob, closeSearch} : {createJob: (internalId: string, type: string) => void, closeSearch(): void}) {
|
||||
const [mangaConnectors, setConnectors] = useState<IMangaConnector[]>();
|
||||
const [selectedConnector, setSelectedConnector] = useState<IMangaConnector>();
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<string>();
|
||||
@ -98,8 +98,7 @@ export default function Search({onJobsChanged, closeSearch} : {onJobsChanged: Ev
|
||||
<select id="Searchbox-language" onChange={changeSelectedLanguage} value={selectedLanguage === null ? "" : selectedLanguage}>
|
||||
{selectedConnector === undefined
|
||||
? <option value="" disabled hidden>Select Connector</option>
|
||||
: selectedConnector.SupportedLanguages.map(language => <option value={language}
|
||||
key={language}>{language}</option>)}
|
||||
: selectedConnector.SupportedLanguages.map(language => <option value={language} key={language}>{language}</option>)}
|
||||
</select>
|
||||
<button id="Searchbox-button" type="submit" onClick={ExecuteSearch}>Search</button>
|
||||
</div>
|
||||
@ -107,7 +106,7 @@ export default function Search({onJobsChanged, closeSearch} : {onJobsChanged: Ev
|
||||
<div id="SearchResults">
|
||||
{searchResults === undefined
|
||||
? <p></p>
|
||||
: searchResults.map(result => SearchResult(result, onJobsChanged))}
|
||||
: searchResults.map(result => SearchResult(result, createJob))}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
@ -52,7 +52,7 @@ export function CoverCard(manga: IManga) : ReactElement {
|
||||
</div>);
|
||||
}
|
||||
|
||||
export function SearchResult(manga: IManga, jobsChanged: EventHandler<any>) : ReactElement {
|
||||
export function SearchResult(manga: IManga, createJob: (internalId: string, type: string) => void) : ReactElement {
|
||||
return(
|
||||
<div className="SearchResult" key={manga.internalId}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)}></img>
|
||||
@ -65,7 +65,7 @@ export function SearchResult(manga: IManga, jobsChanged: EventHandler<any>) : Re
|
||||
</div>
|
||||
<MarkdownPreview className="Manga-description" source={manga.description} style={{ backgroundColor: "transparent", color: "black" }} />
|
||||
<button className="Manga-AddButton" onClick={(e) => {
|
||||
Job.CreateJob(manga.internalId, "MonitorManga", "03:00:00").then(() => jobsChanged(manga.internalId));
|
||||
createJob(manga.internalId, "MonitorManga")
|
||||
}}>Monitor
|
||||
</button>
|
||||
</div>);
|
||||
|
Reference in New Issue
Block a user